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是直接在运行前织入实际的代码,所以功能会强大很多。
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
因为AspectJ比较强大,在项目中应用会更多,所以这里只介绍它与Spring Boot的集成。
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, met