Spring boot(20) Spring /Spring boot使用AOP、強制使用cglib(以記錄方法耗時為例子)
阿新 • • 發佈:2019-01-10
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.
解決辦法