RPC,AOP都会用到代理,代理的技术有jdk的Proxy代理(必须传入接口),cglib(可以是类而非接口, spring),Javassist(jboss )而Spring boot本身也在方方面面使用了代理技术。
我们看下Spring boot的依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
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
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
dependencies>
@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;
}
}
}
@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
<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>
<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>
@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);
}
}
}
只要在com.cctv.service下的组件都被被代理
如果包下的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"/>
即可
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.