Spring-AOP @AspectJ切點函式之args()和@args()
概述
args函式的入參是類名, 而 @args()的入參必須是註解類的類名。
雖然args()允許在類名後使用“+”萬用字元,但該萬用字元在此處沒有意義,新增和不新增的效果都一樣。
args()
該函式接收一個類名,表示目標類方法入參物件是指定類(包含子類)時,切點匹配。
比如args(com.xgj.Waiter)
表示執行時入參是Waiter型別的方法,它和execution(* *(com.xgj.Waiter))
的區別在於後者是這對類方法的簽名而言的,而前者是針對執行時的入參型別而言。
比如args(com.xgj.Waiter)
execution(* *(com.xgj.Waiter))
,實際上 args(com.xgj.Waiter)
等價於 execution(* *(com.xgj.Waiter+))
,當然也等價於 args(com.xgj.Waiter+)
@args()
該函式接收一個註解類的類名,當方法的執行時入參物件標註了指定的註解時,匹配切點。
我們來通過下圖@args(M)匹配示意圖來詳細解釋下:
T0、T1、T2、T3有如上繼承關係,假設目標類方法的簽名為fun(T1 t),它的入參為T1,而切面的切點定義為@args(M), T2類標註了@M。 當fun(T1 t)的傳入物件為T2或者T3時,方法匹配@args(M)宣告所定義的切點。
假設方法簽名是funt(T1 t),入參為T1,而標註了@M的類是T0,當fun(T1 t)傳入T1、T2、T3的例項時,均不匹配切點@args(M).
在類的繼承樹中,(1)處為方法簽名中入參型別在繼承樹的位置,稱之為入參型別點, 而(2)處標註了@M註解的類在類繼承樹中的位置,稱之為註解點。 判斷方法在執行時是否匹配@args(M)切點,可以根據(1)和(2)在類繼承樹中的相對位置來判斷。
- 如果在繼承樹中註解點(2)高於入參型別點(1),則該目標方法不可能匹配到切點@args(M) ,如下圖所示
- 如果在類繼承樹中註解點(2)低於入參型別點(1),則註解點所在類及其子孫類作為方法入參時,該方法匹配切點@args(M)
例項請參考下文。
例項
args()
POJO
package com.xgj.aop.spring.advisor.aspectJ.function.args;
import org.springframework.stereotype.Component;
@Component
public class UserService {
public void addUser(User user) {
System.out.println("addUser " + user);
}
public void modifyUser(User user) {
System.out.println("modifyUser " + user);
}
public void delUser(User user) {
System.out.println("delUser " + user);
}
/**
*
*
* @Title: addArtisanTT
*
* @Description: 入參為ArtisanTT
*
* @param artisan
*
* @return: void
*/
public void addArtisanTT(ArtisanTT artisan) {
System.out.println("addArtisanTT " + artisan);
}
public void modifyArtisanTT(ArtisanTT artisan) {
System.out.println("modifyArtisanTT " + artisan);
}
public void delArtisanTT(ArtisanTT artisan) {
System.out.println("delArtisanTT " + artisan);
}
}
POJO
package com.xgj.aop.spring.advisor.aspectJ.function.args;
import org.springframework.stereotype.Component;
@Component
public class UserServiceExt {
public void addUser(User user) {
System.out.println("入參為user的類 addUser " + user);
}
public void modifyUser(User user) {
System.out.println("入參為user的類 modifyUser " + user);
}
public void delUser(User user) {
System.out.println("入參為user的類 delUser " + user);
}
/**
*
*
* @Title: addArtisanTT
*
* @Description: 入參為ArtisanTT
*
* @param artisan
*
* @return: void
*/
public void addArtisanTT(ArtisanTT artisan) {
System.out.println("addArtisanTT " + artisan);
}
public void modifyArtisanTT(ArtisanTT artisan) {
System.out.println("modifyArtisanTT " + artisan);
}
public void delArtisanTT(ArtisanTT artisan) {
System.out.println("delArtisanTT " + artisan);
}
}
切面
package com.xgj.aop.spring.advisor.aspectJ.function.args;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
*
*
* @ClassName: ArgsAspectJ
*
* @Description: 該函式接收一個類名,表示目標類方法入參物件是指定類(包含子類)時,切點匹配
*
* @author: Mr.Yang
*
* @date: 2017年9月1日 上午11:36:23
*/
@Aspect
public class ArgsAspect {
@Before("args(com.xgj.aop.spring.advisor.aspectJ.function.args.User)")
public void crossCuttingCode() {
System.out.println("some logic is here ");
}
}
配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- (1)宣告Context名稱空間以及Schema檔案 (2)掃描類包以及應用註解定義的bean -->
<context:component-scan base-package="com.xgj.aop.spring.advisor.aspectJ.function.args"/>
<!-- 基於@AspectJ切面的驅動器 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 使用了@AspectJ註解的切面類 -->
<bean class="com.xgj.aop.spring.advisor.aspectJ.function.args.ArgsAspect"/>
</beans>
測試類:
package com.xgj.aop.spring.advisor.aspectJ.function.args;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ArgsAspectTest {
private ApplicationContext ctx;
@Test
public void test() {
ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/aop/spring/advisor/aspectJ/function/args/conf-args.xml");
UserService userService = ctx.getBean("userService", UserService.class);
UserServiceExt userServiceExt = ctx.getBean("userServiceExt", UserServiceExt.class);
User user = new User();
ArtisanTT artisan = new ArtisanTT();
// 織入橫切邏輯
userService.addUser(user);
// 織入橫切邏輯
userService.modifyUser(user);
// 織入橫切邏輯
userService.delUser(user);
System.out.println("================================");
// 入參不是user,因此不會被織入橫切邏輯
userService.addArtisanTT(artisan);
userService.modifyArtisanTT(artisan);
userService.delArtisanTT(artisan);
System.out.println("================================");
// 入參為user,因此也會被織入橫切邏輯
userServiceExt.addUser(user);
// 入參為user,因此也會被織入橫切邏輯
userServiceExt.modifyUser(user);
// 入參為user,因此也會被織入橫切邏輯
userServiceExt.delUser(user);
System.out.println("================================");
// 入參不是user,因此不會被織入橫切邏輯
userServiceExt.addArtisanTT(artisan);
userServiceExt.modifyArtisanTT(artisan);
userServiceExt.delArtisanTT(artisan);
}
}
執行結果:
2018-01-04 09:19:50,614 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4a05bc0c: startup date [Thu Jan 04 09:19:50 CST 2018]; root of context hierarchy
2018-01-04 09:19:50,795 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJ/function/args/conf-args.xml]
some logic is here
addUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef
some logic is here
modifyUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef
some logic is here
delUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef
================================
addArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2
modifyArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2
delArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2
================================
some logic is here
入參為user的類 addUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef
some logic is here
入參為user的類 modifyUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef
some logic is here
入參為user的類 delUser com.xgj.aop.spring.advisor.aspectJ.function.args.User@657531ef
================================
addArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2
modifyArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2
delArtisanTT com.xgj.aop.spring.advisor.aspectJ.function.args.ArtisanTT@712175f2
@args()
首先我們先自定義一個註解,用於測試用
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*
* @ClassName: Monitor
*
* @Description: 自定義註解 @Monitor
* 更多資訊請閱讀http://blog.csdn.net/yangshangwei/article/
* details/77477840
*
* @author: Mr.Yang
*
* @date: 2017年9月1日 下午4:00:12
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
public @interface Monitor {
public boolean value() default true;
}
使用註解掃描的4個POJO
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs;
import org.springframework.stereotype.Component;
@Component
public class T0 {
}
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs;
import org.springframework.stereotype.Component;
@Component
public class T1 extends T0 {
/**
*
*
* @Title: fun
*
* @Description: 目標類方法,旨在這個方法中織入增強邏輯. 當註解標註在T2,方法的入參為T2或者T2的子孫類時,會織入增強
*
* @param t
*
* @return: void
*/
public void fun(T2 t) {
System.out.println(t.getClass().getName() + " fun executed");
}
}
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs;
import org.springframework.stereotype.Component;
@Monitor
@Component
public class T2 extends T1 {
}
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs;
import org.springframework.stereotype.Component;
@Component
public class T3 extends T2 {
}
切面
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
*
*
* @ClassName: AtArgsAspect
*
* @Description: 標註了@Aspect的切面
*
* @author: Mr.Yang
*
* @date: 2017年9月1日 下午4:21:14
*/
@Aspect
public class AtArgsAspect {
@Before("@args(com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.Monitor)")
public void crossCuttingCode() {
System.out.println("前置增強 橫切邏輯織入 some logic is here ");
}
}
配置檔案:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- (1)宣告Context名稱空間以及Schema檔案 (2)掃描類包以及應用註解定義的bean -->
<context:component-scan base-package="com.xgj.aop.spring.advisor.aspectJ.function.args.atargs"/>
<!-- 基於@AspectJ切面的驅動器 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 使用了@AspectJ註解的切面類 -->
<bean class="com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.AtArgsAspect"/>
</beans>
測試類
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AtArgsAspectTest {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:com/xgj/aop/spring/advisor/aspectJ/function/args/atargs/conf-atargs.xml");
T0 t0 = ctx.getBean("t0", T0.class);
T1 t1 = ctx.getBean("t1", T1.class);
T2 t2 = ctx.getBean("t2", T2.class);
T3 t3 = ctx.getBean("t3", T3.class);
// 因t1中的fun入參為t2,且註解標註在了T2類上,t3又是t2的子類,所以 下面兩個呼叫都會織入增強
t1.fun(t2);
t1.fun(t3);
}
}
執行結果:
2017-09-04 17:43:44,302 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@f4efcb0: startup date [Mon Sep 04 17:43:44 BOT 2017]; root of context hierarchy
2017-09-04 17:43:44,390 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJ/function/args/atargs/conf-atargs.xml]
前置增強 橫切邏輯織入 some logic is here
com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.T2$$EnhancerBySpringCGLIB$$fcff1873 fun executed
前置增強 橫切邏輯織入 some logic is here
com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.T3$$EnhancerBySpringCGLIB$$109aa116 fun executed
從執行結果看,正確的織入了橫切邏輯。
在類繼承樹中註解點低於入參型別點,則註解點所在類及其子孫類作為方法入參時,該方法匹配切點@args(M), 符合。
如果我們先取消掉T2上的@Monitor註解,然後把註解標註在T0上
如下:
package com.xgj.aop.spring.advisor.aspectJ.function.args.atargs;
import org.springframework.stereotype.Component;
@Monitor
@Component
public class T0 {
}
執行結果:
2017-09-04 17:56:31,858 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1c057a1e: startup date [Mon Sep 04 17:56:31 BOT 2017]; root of context hierarchy
2017-09-04 17:56:31,970 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJ/function/args/atargs/conf-atargs.xml]
com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.T2$$EnhancerBySpringCGLIB$$42855f4c fun executed
com.xgj.aop.spring.advisor.aspectJ.function.args.atargs.T3$$EnhancerBySpringCGLIB$$5620e7ef fun executed
因在繼承樹中註解點高於入參型別點,因此該目標方法不可能匹配到切點@args(M)。