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, 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 插件
因为是须要编译时织入代码,所以须要 maven 插件的反对:https://github.com/mojohaus/a…
配置好 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/p…