Spring AOP与AspectJ的对比及应用

打印 上一主题 下一主题

主题 511|帖子 511|积分 1533

1 简介

AOP,即面向切面编程是很常用的技术,特别是在Java Web开发中。而最流行的AOP框架分别是Spring AOP和AspectJ。
2 Spring AOP vs AspectJ

Spring AOP是基于Spring IoC实现的,它解决大部分常见的需求,但它并不是一个完整的AOP解决方案。对于非Spring容器管理的对象,它更没有办法了。而AspectJ旨在提供完整的AOP方案,因此也会更复杂。
2.1 织入方式

两者织入方式有极大的不同,这也是它们的本质区别,它们实现代理的方式不同。
AspectJ是在运行前织入的,分为三类:

  • 编译时织入
  • 编译后织入
  • 加载时织入
因此需要AspectJ编译器(ajc)的支持。
而Spring AOP是运行时织入的,主要使用了两种技术:JDK动态代理和CGLIB代理。对于接口使用JDK Proxy,而继承的使用CGLIB。

2.2 Joinpoints

因为织入方式的区别,两者所支持的Joinpoint也是不同的。像final的方法和静态方法,无法通过动态代理来改变,所以Spring AOP无法支持。但AspectJ是直接在运行前织入实际的代码,所以功能会强大很多。
JoinpointSpring AOP SupportedAspectJ SupportedMethod CallNoYesMethod ExecutionYesYesConstructor CallNoYesConstructor ExecutionNoYesStatic initializer executionNoYesObject initializationNoYesField referenceNoYesField assignmentNoYesHandler executionNoYesAdvice executionNoYes2.3 性能

编译织入会比较运行时织入快很多,Spring AOP是使用代理模式在运行时才创建对应的代理类,效率没有AspectJ高。
3 Spring Boot使用AspectJ

因为AspectJ比较强大,在项目中应用会更多,所以这里只介绍它与Spring Boot的集成。
3.1 引入依赖

引入以下依赖,在Spring Boot基础上加了Lombok和aspectj:
  1. <dependencies>
  2.   <dependency>
  3.     <groupId>org.springframework.boot</groupId>
  4.     <artifactId>spring-boot-starter-web</artifactId>
  5.   </dependency>
  6.   <dependency>
  7.     <groupId>org.aspectj</groupId>
  8.     <artifactId>aspectjweaver</artifactId>
  9.     <version>${aspectj.version}</version>
  10.   </dependency>
  11.   <dependency>
  12.     <groupId>org.aspectj</groupId>
  13.     <artifactId>aspectjrt</artifactId>
  14.     <version>${aspectj.version}</version>
  15.   </dependency>
  16.   <dependency>
  17.     <groupId>org.projectlombok</groupId>
  18.     <artifactId>lombok</artifactId>
  19.     <version>${lombok.version}</version>
  20.     <scope>provided</scope>
  21.   </dependency>
  22. </dependencies>
复制代码
3.2 被AOP的对象

为了验证AOP的功能,我们添加一个TestController,它有一个处理Get请求的方法,同时会调用private的成员方法和静态方法:
  1. @RestController
  2. @RequestMapping("/test")
  3. @Slf4j
  4. public class TestController {
  5.     @GetMapping("/hello")
  6.     public String hello() {
  7.         log.info("------hello() start---");
  8.         test();
  9.         staticTest();
  10.         log.info("------hello() end---");
  11.         return "Hello, pkslow.";
  12.     }
  13.     private void test() {
  14.         log.info("------test() start---");
  15.         log.info("test");
  16.         log.info("------test() end---");
  17.     }
  18.     private static void staticTest() {
  19.         log.info("------staticTest() start---");
  20.         log.info("staticTest");
  21.         log.info("------staticTest() end---");
  22.     }
  23. }
复制代码
3.3 配置Aspect

配置切面如下:
  1. @Aspect
  2. @Component
  3. @Slf4j
  4. //@EnableAspectJAutoProxy
  5. public class ControllerAspect {
  6.     @Pointcut("execution(* com.pkslow.springboot.controller..*.*(..))")
  7.     private void testControllerPointcut() {
  8.     }
  9.     @Before("testControllerPointcut()")
  10.     public void doBefore(JoinPoint joinPoint){
  11.         log.info("------pkslow aop doBefore start------");
  12.         String method = joinPoint.getSignature().getName();
  13.         String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
  14.         log.info("Method: {}.{}" ,declaringTypeName, method);
  15.         log.info("------pkslow aop doBefore end------");
  16.     }
  17.     @Around("testControllerPointcut()")
  18.     public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  19.         log.info("------pkslow aop doAround start------");
  20.         long start = System.nanoTime();
  21.         Object obj = joinPoint.proceed();
  22.         long end = System.nanoTime();
  23.         log.info("Execution Time: " + (end - start) + " ns");
  24.         log.info("------pkslow aop doAround end------");
  25.         return obj;
  26.     }
  27. }
复制代码
@Pointcut定义哪些类和方法会被捕抓来代理,这里配置的是controller下的所有方法。
而@Before和@Around则定义了一些处理逻辑。@Before是打印了方法名,而@Around是做了一个计时。
注意:是不需要配置@EnableAspectJAutoProxy的。
3.4 maven插件

因为是需要编译时织入代码,所以需要maven插件的支持:https://github.com/mojohaus/aspectj-maven-plugin
配置好pom.xml文件即可。
然后执行命令打包:
  1. mvn clean package
复制代码
这时会显示一些织入信息,大致如下:
  1. [INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
  2. [INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
  3. [INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
  4. [INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
  5. [INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
  6. [INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
复制代码
看到以上信息,说明成功织入了代码,具体可以查看生成的class文件。

可以看到有许多代码都不是我们写的,而是织入生成。
3.5 执行及测试

编译成功后,我们就执行代码。如果是通过IDEA来执行,则在运行前不需要再build了,因为已经通过maven build过了包。通过IDEA自带的编译器build,可能无法织入。或者选择ajc作为编译器。具体请参考:IDEA启动Springboot但AOP失效
访问如下:
  1. GET http://localhost:8080/test/hello
复制代码
则日志如下,成功实现AOP功能:

3.6 一些遇到的错误

遇到错误:
  1. ajc Syntax error, annotations are only available if source level is 1.5 or greater
复制代码
需要配置插件:
  1. <complianceLevel>${java.version}</complianceLevel>
  2. <source>${java.version}</source>
  3. <target>${java.version}</target>
复制代码
可能还会遇到无法识别Lombok的错误,配置如下则解决该问题:
  1. <plugin>
  2.   <groupId>org.codehaus.mojo</groupId>
  3.   <artifactId>aspectj-maven-plugin</artifactId>
  4.   <version>1.14.0</version>
  5.   <configuration>
  6.     <complianceLevel>${java.version}</complianceLevel>
  7.     <source>${java.version}</source>
  8.     <target>${java.version}</target>
  9.     <proc>none</proc>
  10.     <showWeaveInfo>true</showWeaveInfo>
  11.     <forceAjcCompile>true</forceAjcCompile>
  12.     <sources/>
  13.     <weaveDirectories>
  14.       <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
  15.     </weaveDirectories>
  16.   </configuration>
  17.   <executions>
  18.     <execution>
  19.       <goals>
  20.         <goal>compile</goal>
  21.       </goals>
  22.     </execution>
  23.   </executions>
  24. </plugin>
复制代码
4 总结

AOP场景应用特别多,还是需要掌握的。
代码请看GitHub: https://github.com/LarryDpk/pkslow-samples

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张国伟

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表