1 简介
2 Spring AOP vs AspectJ
Spring AOP是基于Spring IoC实现的,它解决大部分常见的需求,但它并不是一个完整的AOP解决方案。对于非Spring容器管理的对象,它更没有办法了。而AspectJ旨在提供完整的AOP方案,因此也会更复杂。
2.1 织入方式
AspectJ是在运行前织入的,分为三类:
- 编译时织入
- 编译后织入
- 加载时织入
而Spring AOP是运行时织入的,主要使用了两种技术:JDK动态代理和CGLIB代理。对于接口使用JDK Proxy,而继承的使用CGLIB。
2.2 Joinpoints
Joinpoint | Spring AOP Supported | AspectJ Supported |
---|---|---|
Method Call | No | Yes |
Method Execution | Yes | Yes |
Constructor Call | No | Yes |
Constructor Execution | No | Yes |
Static initializer execution | No | Yes |
Object initialization | No | Yes |
Field reference | No | Yes |
Field assignment | No | Yes |
Handler execution | No | Yes |
Advice execution | No | Yes |
2.3 性能
编译织入会比较运行时织入快很多,Spring AOP是使用代理模式在运行时才创建对应的代理类,效率没有AspectJ高。
3 Spring Boot使用AspectJ
3.1 引入依赖
引入以下依赖,在Spring Boot基础上加了Lombok和aspectj:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
3.2 被AOP的对象
为了验证AOP的功能,我们添加一个TestController,它有一个处理Get请求的方法,同时会调用private的成员方法和静态方法:
@RestController
@RequestMapping("/test"
@Slf4j
public class TestController {
@GetMapping("/hello"
public String hello( {
log.info("------hello( start---";
test(;
staticTest(;
log.info("------hello( end---";
return "Hello, pkslow.";
}
private void test( {
log.info("------test( start---";
log.info("test";
log.info("------test( end---";
}
private static void staticTest( {
log.info("------staticTest( start---";
log.info("staticTest";
log.info("------staticTest( end---";
}
}
3.3 配置Aspect
配置切面如下:
@Aspect
@Component
@Slf4j
//@EnableAspectJAutoProxy
public class ControllerAspect {
@Pointcut("execution(* com.pkslow.springboot.controller..*.*(.."
private void testControllerPointcut( {
}
@Before("testControllerPointcut("
public void doBefore(JoinPoint joinPoint{
log.info("------pkslow aop doBefore start------";
String method = joinPoint.getSignature(.getName(;
String declaringTypeName = joinPoint.getSignature(.getDeclaringTypeName(;
log.info("Method: {}.{}" ,declaringTypeName, method;
log.info("------pkslow aop doBefore end------";
}
@Around("testControllerPointcut("
public Object doAround(ProceedingJoinPoint joinPoint throws Throwable {
log.info("------pkslow aop doAround start------";
long start = System.nanoTime(;
Object obj = joinPoint.proceed(;
long end = System.nanoTime(;
log.info("Execution Time: " + (end - start + " ns";
log.info("------pkslow aop doAround end------";
return obj;
}
}
@Pointcut
定义哪些类和方法会被捕抓来代理,这里配置的是controller下的所有方法。@Before和
@Around
则定义了一些处理逻辑。@Before
是打印了方法名,而@Around
是做了一个计时。@EnableAspectJAutoProxy的。
3.4 maven插件
配置好pom.xml文件即可。
mvn clean package
这时会显示一些织入信息,大致如下:
[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 [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 [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 [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 [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 [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失效
GET http://localhost:8080/test/hello
则日志如下,成功实现AOP功能:
3.6 一些遇到的错误
ajc Syntax error, annotations are only available if source level is 1.5 or greater
需要配置插件:
<complianceLevel>${java.version}</complianceLevel> <source>${java.version}</source> <target>${java.version}</target>
可能还会遇到无法识别Lombok的错误,配置如下则解决该问题:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.14.0</version> <configuration> <complianceLevel>${java.version}</complianceLevel> <source>${java.version}</source> <target>${java.version}</target> <proc>none</proc> <showWeaveInfo>true</showWeaveInfo> <forceAjcCompile>true</forceAjcCompile> <sources/> <weaveDirectories> <weaveDirectory>${project.build.directory}/classes</weaveDirectory> </weaveDirectories> </configuration> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin>
4 总结
AOP场景应用特别多,还是需要掌握的。
代码请看GitHub: https://github.com/LarryDpk/pkslow-samples