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.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>

Spring boot(20) Spring /Spring boot使用AOP、强制使用cglib(以记录方法耗时为例子)_第1张图片
可以看到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$$299269f6.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项目

Spring boot(20) Spring /Spring boot使用AOP、强制使用cglib(以记录方法耗时为例子)_第2张图片

pom.xml
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-aopartifactId>
        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 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.springframeworkgroupId>
            <artifactId>spring-aopartifactId>
            <version>4.3.11.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.8.10version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjrtartifactId>
            <version>1.8.10version>
        dependency> 

2.2 spring-context.xml


<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.(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.(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.

解决办法
Spring boot(20) Spring /Spring boot使用AOP、强制使用cglib(以记录方法耗时为例子)_第3张图片
Spring boot(20) Spring /Spring boot使用AOP、强制使用cglib(以记录方法耗时为例子)_第4张图片

你可能感兴趣的:(spring,spring-boot)