1. 程式人生 > >Spring-AOP @AspectJ切點函式之args()和@args()

Spring-AOP @AspectJ切點函式之args()和@args()

概述

args函式的入參是類名, 而 @args()的入參必須是註解類的類名。

雖然args()允許在類名後使用“+”萬用字元,但該萬用字元在此處沒有意義,新增和不新增的效果都一樣。

args()

該函式接收一個類名,表示目標類方法入參物件是指定類(包含子類)時,切點匹配。

比如args(com.xgj.Waiter) 表示執行時入參是Waiter型別的方法,它和execution(* *(com.xgj.Waiter))的區別在於後者是這對類方法的簽名而言的,而前者是針對執行時的入參型別而言。

比如args(com.xgj.Waiter)

既匹配addWiter(Waiter waiter),又匹配addNaiveWaiter(NaiveWaiter 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)。