SpringBoot中AspectJ的使用
AspectJ作為語言級別的AOP框架,功能相比於SpringAOP更加強大。SpringAOP旨在提供給用戶一個輕量級的AOP實現方案,它只能應用在SpringIOC容器中管理的bean。而AspectJ旨在提供給用戶一個完整的AOP解決方案,它可以應用在所有的域對象中。
- AspectJ織入代碼方式
AspectJ在織入代碼時,有三種不同類型的編織:
官網對於織入時機的解釋
- 編譯時織入(CTW)
- 編譯后織入 (BTW)
- 類加載時織入 (LTW)
- SpringAOP織入代碼方式
SpringAOP是基於動態代理來實現的,在運行期通過接口或者子類的方式來實現AOP。在SpringAOP中主要有兩種:
- JDK動態代理(基於接口實現)
- CGLib動態代理(基於類實現)
本文主要介紹AspectJ在SpringBoot中的兩種實現(CTW、LTW)
1. CTW的實現
使用CTW的方式織入時,需要采用特殊的編譯器(ajc)來進行編譯
- IDEA的相關配置
在Setting將Java Complier替換為Ajc編譯器
注意:必須現在項目中導入aspectjtools.jar
在Project Structure中導入依賴包aspectjrt.jar
在facets中添加AspectJ
- 相關設置
aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<include within="com.zakary.qingblog..*" />
</weaver>
<aspects>
<aspect name="com.zakary.qingblog.aop.LoginAspect"/>
<aspect name="com.zakary.qingblog.aop.InterceptorAspect"/>
<aspect name="com.zakary.qingblog.aop.ParamsCheckAspect"/>
</aspects>
</aspectj>
- 切面實現
切面
@Aspect
@DeclarePrecedence("InterceptorAspect,LoginAspect,ParamsCheckAspect")
public class LoginAspect {
private Logger logger= LoggerFactory.getLogger(QingblogApplication.class);
/**
* 匹配規則
* execution: 用於匹配方法執行的連接點;
* execution(public * *(..)) ==> 匹配所有目標類的public方法,第一個*代表返回類型,第二個*代表方法名,而..代表任意入參的方法。
* execution(* com.oysept.springboot.controller..*.*(..)) ==> 該包及所有子包下任何類的任何方法。
* execution(* com.oysept.springboot.controller.*(..)) ==> 該包下任何類的任何方法。
* execution(* com.oysept.springboot.controller.AspectJController.*(..)) ==> 該包下AspectJController類的任何方法。
* execution(* com..*.*Controller.method*(..)) ==> 匹配包名前綴為com的任何包下類名后綴為Controller的方法,方法名必須以method為前綴。
* execution(* *To(..)) ==> 匹配目標類所有以To為后綴的方法。
* 注: 該方法只是為了聲明一個公共的環繞通知,也可以直接在具體方法配置,如: @Around("execution(* com.oysept.springboot.controller..*.*(..))")
*/
@Pointcut("execution(* com.zakary.qingblog.controller.LoginController.userLogin(..))")
public void loginAop() {}
@Before("loginAop()")
public void before(JoinPoint point) throws Throwable {
Object[] objArgs = point.getArgs();
String mail= AnalysisUtils.getObjectToMap(objArgs[0]).get("userMail").toString();
String password=AnalysisUtils.getObjectToMap(objArgs[0]).get("userPassword").toString();
logger.info("User Login : [ userMail : "+mail+"\t , password : "+password+"\t ]");
}
}
切入點
@Controller
public class LoginController {
@Autowired
private LoginService loginService;
@RequestMapping("/userLogin")
@ResponseBody
public JSONResult userLogin(@RequestBody @Validated({ValidationGroups.LoginGroup.class}) User user, HttpServletRequest request){
User user1=loginService.login(user);
HttpSession session=request.getSession();
session.setAttribute("userId",user1.getUserId());
return JSONResult.ok("success");
}
}
編譯后的class文件
@Controller
public class LoginController {
@Autowired
private LoginService loginService;
public LoginController() {
}
@RequestMapping({"/userLogin"})
@ResponseBody
public JSONResult userLogin(@RequestBody @Validated({LoginGroup.class}) User user, HttpServletRequest request) {
JoinPoint var5 = Factory.makeJP(ajc$tjp_0, this, this, user, request);
JSONResult var9;
try {
LoggerAspect.aspectOf().before(var5);
LoginAspect.aspectOf().before(var5);
ParamsCheckAspect.aspectOf().beforeLogin(var5);
User user1 = this.loginService.login(user);
HttpSession session = request.getSession();
session.setAttribute("userId", user1.getUserId());
var9 = JSONResult.ok("success");
} catch (Throwable var10) {
LoggerAspect.aspectOf().releaseResource(var5);
throw var10;
}
LoggerAspect.aspectOf().releaseResource(var5);
return var9;
}
static {
ajc$preClinit();
}
}
圖中執行了多個AspectOf方法,因為我在此處實現了多個切面
2. LTW的實現
- IDEA的相關配置
RUN->Edit Configurations->Configuration->Main Class->Environment->VM options填入
-javaagent:"C:\Users\Zakary.m2\repository\org\aspectj\aspectjweaver\1.9.5\aspectjweaver-1.9.5.jar"
-javaagent:"C:\Users\Zakary.m2\repository\org\springframework\spring-instrument\5.1.9.RELEASE\spring-instrument-5.1.9.RELEASE.jar"
此包在maven倉庫中可以下載 - application.yml
spring:
aop:
auto: false
關閉Springaop
設置ltw CustomLtwConfig.java
@Configuration
@ComponentScan("com.zakary.qingblog.controller")
@EnableLoadTimeWeaving(aspectjWeaving=ENABLED)
public class CustomLtwConfig{
}
注:aspectjWeaving有三個值,ENABLED為強制使用LTW的方式,DISENABLED不使用,AUTODETECT為查找META-INF下有無aop.xml文件,如果沒有,不使用LTW,有的話使用LTW
- 切面實現同上
3. 多切面執行順序的解決方案
- 如果采用SpringAop時,同一個切點含有多個advice時,使用@order注解來決定每個advice的執行順序,其中value值越小越先執行。但@order不能使用在aspectj的切面中,@order只能決定SpringIOC容器中的bean。
- 采用AspectJ時,使用注解@DeclarePrecedence,參數為切面類,
例如
@DeclarePrecedence("InterceptorAspect,LoginAspect,ParamsCheckAspect")