Spring AOP與AspectJ的對比及應(yīng)用詳解
1 簡介
AOP,即面向切面編程是很常用的技術(shù),特別是在Java Web開發(fā)中。而最流行的AOP框架分別是Spring AOP和AspectJ。
2 Spring AOP vs AspectJ
Spring AOP是基于Spring IoC實現(xiàn)的,它解決大部分常見的需求,但它并不是一個完整的AOP解決方案。對于非Spring容器管理的對象,它更沒有辦法了。而AspectJ旨在提供完整的AOP方案,因此也會更復(fù)雜。
2.1 織入方式
兩者織入方式有極大的不同,這也是它們的本質(zhì)區(qū)別,它們實現(xiàn)代理的方式不同。
AspectJ是在運行前織入的,分為三類:
- 編譯時織入
- 編譯后織入
- 加載時織入
因此需要AspectJ編譯器(ajc)的支持。
而Spring AOP是運行時織入的,主要使用了兩種技術(shù):JDK動態(tài)代理和CGLIB代理。對于接口使用JDK Proxy,而繼承的使用CGLIB。

2.2 Joinpoints
因為織入方式的區(qū)別,兩者所支持的Joinpoint也是不同的。像final的方法和靜態(tài)方法,無法通過動態(tài)代理來改變,所以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是使用代理模式在運行時才創(chuàng)建對應(yīng)的代理類,效率沒有AspectJ高。
3 Spring Boot使用AspectJ
因為AspectJ比較強大,在項目中應(yīng)用會更多,所以這里只介紹它與Spring Boot的集成。
3.1 引入依賴
引入以下依賴,在Spring Boot基礎(chǔ)上加了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請求的方法,同時會調(diào)用private的成員方法和靜態(tài)方法:
@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插件的支持:github.com/mojohaus/as…
配置好pom.xml文件即可。
然后執(zhí)行命令打包:
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 執(zhí)行及測試
編譯成功后,我們就執(zhí)行代碼。如果是通過IDEA來執(zhí)行,則在運行前不需要再build了,因為已經(jīng)通過maven build過了包。通過IDEA自帶的編譯器build,可能無法織入?;蛘哌x擇ajc作為編譯器。
具體請參考:IDEA啟動Springboot但AOP失效
訪問如下:
GET http://localhost:8080/test/hello
則日志如下,成功實現(xiàn)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 總結(jié)
AOP場景應(yīng)用特別多,還是需要掌握的。
代碼請看GitHub: github.com/LarryDpk/pk…
以上就是Spring AOP與AspectJ的對比及應(yīng)用詳解的詳細內(nèi)容,更多關(guān)于Spring AOP對比AspectJ應(yīng)用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IntelliJ IDEA2020.1版本更新pom文件自動導(dǎo)包的方法
這篇文章主要介紹了IntelliJ IDEA2020.1版本更新pom文件自動導(dǎo)包的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Android Studio中ButterKnife插件的安裝與使用詳解
本篇文章主要介紹了Android Studio中ButterKnife插件的安裝與使用詳解,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01
在Spring AOP中代理對象創(chuàng)建的步驟詳解
今天和小伙伴們聊一聊 Spring AOP 中的代理對象是怎么創(chuàng)建出來的,透過這個過程再去熟悉一下 Bean 的創(chuàng)建過程,感興趣的小伙伴跟著小編一起來看看吧2023-08-08
JavaWeb請求轉(zhuǎn)發(fā)和請求包含實現(xiàn)過程解析
這篇文章主要介紹了JavaWeb請求轉(zhuǎn)發(fā)和請求包含實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02

