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$$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项目
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.
解决办法