1. 程式人生 > >Spring boot(20) Spring /Spring boot使用AOP、強制使用cglib(以記錄方法耗時為例子)

Spring boot(20) Spring /Spring boot使用AOP、強制使用cglib(以記錄方法耗時為例子)

1. Spring boot

1.1 Spring boot預設使用了AOP和動態代理

RPC,AOP都會用到代理,代理的技術有jdk的Proxy代理(必須傳入介面),cglib(可以是類而非介面, spring),Javassist(jboss )而Spring boot本身也在方方面面使用了代理技術。
我們看下Spring boot的依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId
>
spring-boot-starter</artifactId> </dependency>

這裡寫圖片描述
可以看到Spring boot 最基礎的依賴已經包含了AOP
起始類App的最終注入Spring容器的例項就是個代理:
驗證

SpringBootApplication
public class MainApp implements CommandLineRunner{
    @Autowired 
    MainApp app;
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MainApp.class, args);
    }
    @Override
public void run(String... arg0) throws Exception { LogCore.BASE.info("app ={}",app); } }

日誌:

2017-08-09 11:32:30,331 INFO (MainApp.java:37)- app =org.sonic.tcp.rpc.provider.MainApp$$EnhancerBySpringCGLIB$$c44be2a2@20ca951f

我們檢視其執行時方法列表為

final void App$$EnhancerBySpringCGLIB$$299269
f6.CGLIB$setBeanFactory$6(BeanFactory) throws BeansException private static final void App$$EnhancerBySpringCGLIB$$299269f6.CGLIB$BIND_CALLBACKS(Object) public final void App$$EnhancerBySpringCGLIB$$299269f6.setBeanFactory(BeanFactory) throws BeansException public static MethodProxy App$$EnhancerBySpringCGLIB$$299269f6.CGLIB$findMethodProxy(Signature) public static void App$$EnhancerBySpringCGLIB$$299269f6.CGLIB$SET_STATIC_CALLBACKS(Callback[]) public static void App$$EnhancerBySpringCGLIB$$299269f6.CGLIB$SET_THREAD_CALLBACKS(Callback[]) static void App$$EnhancerBySpringCGLIB$$299269f6.CGLIB$STATICHOOK1() static void App$$EnhancerBySpringCGLIB$$299269f6.CGLIB$STATICHOOK2()

我們再輸出下類名
com.sonic.aop.App$$EnhancerBySpringCGLIB$$299269f6

1.2 在Spring boot使用AOP

1.2.1 建立個DEMO專案

這裡寫圖片描述

pom.xml
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>
1.2.2寫一個要被代理的類AopDemoService
@Component
public class AopDemoService {
    @Autowired
    ApplicationContext context;

    public Object getMappings() {
        LogCore.BASE.info("requestMappings");
        test("hello aop {}" + this.getClass().getName());
        return HttpHeadUtil.requestMappingsDetail(context);
    }

    public String test(String msg) {
        ThreadTool.sleep(1000);
        LogCore.BASE.info("test msg={}", msg);
        return "yes";
    }
}

代理

/** 切面類 被代理的類自己呼叫自己則不會走下面的方法 */
@Aspect
@Configuration
public class AspectDemo {
    private ThreadLocal<Long> time = new ThreadLocal<>();

    /* 定義一個切入點 */
    @Pointcut("execution(* com.sonic.aop.*Service.*(..))")
    public void doPointCut() {
        LogCore.BASE.info("pointCut");
    }

    /* 通過連線點切入 */
    @Before("execution(* com.sonic.aop.*Service.*(..))")
    public void doBefore() {
        LogCore.BASE.info("doBefore()");
        time.set(System.currentTimeMillis());
    }

    @AfterReturning("execution(* com.sonic.aop.*Service.*(..))")
    public void doAfterReturning(JoinPoint joinPoint) {
        LogCore.BASE.info("doAfterReturning(joinPoint) {}, time used={}", joinPoint.getSignature(),
                System.currentTimeMillis() - time.get());
    }

    @Around("execution(* com.sonic.aop.*Service.*(..))")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        LogCore.BASE.info("AOP @Around start");
        try {
            Object obj = joinPoint.proceed();
            LogCore.BASE.info("AOP @Around end");
            return obj;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }
}
1.2.3 測試Controller
    @Autowired
    AopDemoService service; 
    @RequestMapping("/test")
    public Object handle(HttpServletRequest req) {
        LogCore.BASE.info("{}", req);
        return service.getMappings();
    }

我們看看訪問/test後的列印日誌

2017-08-08 21:14:40,143 INFO (AspectDemo.java:48)- AOP @Around start
2017-08-08 21:14:40,147 INFO (AspectDemo.java:33)- doBefore()
2017-08-08 21:14:48,205 INFO (AopDemoService.java:16)- requestMappings
2017-08-08 21:14:49,215 INFO (AopDemoService.java:23)- test msg=hello aop {}com.sonic.aop.AopDemoService
2017-08-08 21:14:49,628 INFO (AspectDemo.java:51)- AOP @Around end
2017-08-08 21:14:49,631 INFO (AspectDemo.java:41)- doAfterReturning(joinPoint) Object com.sonic.aop.AopDemoService.getMappings(), time used=9481

2. Spring

2.1 pom.xml

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.10</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.10</version>
        </dependency> 

2.2 spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util-3.1.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <context:component-scan base-package="com.cctv" />
    <context:annotation-config />
    <aop:aspectj-autoproxy/> 
    <import resource="classpath:httpServer.xml"/>
</beans>

2.3 Aspect類

@Aspect
@Configuration
public class ServiceAspect {

    /* 定義一個切入點 */
    @Pointcut("execution(* com.cctv.service.*(..))")
    public void doPointCut() {
        LogCore.BASE.info("PONIT_CUT");

    }

    @Around("execution(* com.cctv.service.*.*(..))")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
        long time = System.currentTimeMillis();
        try {
            Object obj = joinPoint.proceed();
            return obj;
        } catch (Throwable e) {
            throw e;
        }finally{
            LogCore.BASE.info("{}, time used={}", joinPoint.getSignature(),
                    System.currentTimeMillis() - time);
        }
    }

}

2.4 被代理的類

只要在com.cctv.service下的元件都被被代理

3 如何強制使用cglib?

3.1 Spring AOP代理失敗,需要強制使用cglib

如果包下的XXService類實現了介面,Spring會報如下異常

DEBUG] org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:327) - Invoking destroy method 'close' on bean with name 'getJedisHandle'
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'carService2' defined in file [/Users/bao/data/workspace_sjz_gplus/...../CarService2.class]: Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/core/DecoratingProxy
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at com.zhht.aipark.Bootstrap.main(Bootstrap.java:24)
Caused by: java.lang.NoClassDefFoundError: org/springframework/core/DecoratingProxy
    at org.springframework.aop.framework.AopProxyUtils.completeProxiedInterfaces(AopProxyUtils.java:133)
    at org.springframework.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.java:120)
    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:109)
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:469)
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:349)
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:298)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1583)
    at org.springframework.beans.

<aop:aspectj-autoproxy/>

改為

<aop:aspectj-autoproxy proxy-target-class="true"/>

即可

3.2 Spring boot AOP生成例項。但是Antowired具體類或者強轉為具體類失敗 需要強制cglib代理。

Spring boot
報異常如下


***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'demoService' could not be injected as a 'com.sonic.aop.service.DemoService' because it is a JDK dynamic proxy that implements:
    com.sonic.aop.service.IService


Action:

Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.

解決辦法
這裡寫圖片描述
這裡寫圖片描述