spring 面向切面編程(aop)

一、AOP術語

1、通知(advice)

切面的工作被稱爲通知。通知定義了切面是什麼以及何時使用。除了描述切面要完成的工作,通知還定義了何時執行這個工作。
Spring切面可以應用五種類型的通知:

  • 前置通知(before):在目標方法被調用之前調用的通知;
  • 後置通知(after):在目標方法成功執行之後調用,此時不會關心方法的返回是什麼;
  • 返回通知(after-returning):在目標方法成功執行之後調用;
  • 異常通知(after-throwing):在目標方法拋出異常後調用通知;
  • 環繞通知(around):通知包裹了目標方法,在目標方法調用之前和之後執行自定義行爲,可以決定是否執行目標方法。

2、連接點(join point)

就是指spring允許你使用通知的地方

3、切點(point cut)

切點定義何時使用通知。切點的定義會匹配通知所要織入的一個或多個連接點。可以動態的決定是否使用通知

4、切面(aspect)

切面是切點和通知的結合。

5、引入(introduction)

引入允許我們向目標類添加新的方法和屬性,在不修改現有類的情況下,讓他們具有新的行爲和狀態

6、織入(weaving)

織入是把切面應用到目標對象並創建新的代理對象的過程。切面在指定的連接點被織入到目標對象中。在目標對象的生命週期裏有多少個點可以織入:

  • 編譯期:切面在目標類的編譯時被織入。這種方式需要特殊的編譯器。AspectJ的織入編譯器就是以這種方式織入切面的
  • 類加載期:切面在目標類加載到JVM時被織入。這種方式需要特殊的類加載器(ClassLoader),它可以在目標類被引入應用之前增強該類的字節碼。AspectJ5的加載時織入(load-time weaving,LTW)就是支持這種方式
  • 運行期:切面在應用運行的某個時刻被織入。一般情況下,在織入切面時,AOP會爲目標類對象動態的創建一個代理對象。Spring AOP就是以這種方式。

二、指示器

Spring藉助AspectJ的切點表達式語言來定義Spring切面

  • args():限制連接點匹配參數爲指定類型的執行方法
  • @args():限制連接點由指定註解標註的執行方法,支持只有一個入參,且參數類持有指定註解
  • execution():用於匹配是連接點的執行方法
  • this():限制連接點匹配AOP代理的bean引用爲指定類型的類
  • target():限制連接點匹配對象爲指定類型的類
  • @target():限制連接點匹配特定的目標對象,要有指定的註解
  • within():限制連接點匹配指定的類型
  • @within():限制連接點匹配指定註解所標註的類型
  • @annotation():限制匹配帶有指定註解的連接點

三、編碼使用

1、execution+args

定義一個重載的三個方法,用於驗證args

@Service("aopDemo1Service")
public class AopDemo1ServiceImpl implements IAopDemoService {
    @Override
    public String getDemo() {
        System.out.println("demo1 start..............");
        System.out.println("我是沒有入參的demo");
        System.out.println("demo1 end..............");
        return "no param!";
    }

    @Override
    public String getDemo(String arg1) {
        System.out.println("demo1 start..............");
        System.out.println("我是有一個入參的demo,入參是:" + arg1);
        System.out.println("demo1 end..............");
        return "one param!";
    }

    @Override
    public String getDemo(int arg1, String arg2) {
        System.out.println("demo1 start..............");
        System.out.println("我是有兩個入參的demo,第一個參數:" + arg1 + "\n第二個參數:" + arg2);
        System.out.println("demo1 end..............");
        return "two params!";
    }
}

切面配置

@Aspect
@Component
public class AopConfigDemo {
    @Pointcut("execution(* com.wfs.springboot.aop.*.getDemo(..))")
    public void pointCut() {

    }
	/**
     * args 用於匹配到第一個參數爲Integer、第二個參數爲String的連接點
     * @param joinPoint
     * @param arg1
     * @param arg2
     */
    @Around(value = "pointCut() && args(arg1,arg2)")
    public void around(ProceedingJoinPoint joinPoint, Integer arg1,String arg2) {
        System.out.println("aop start................");
        try {
            Object proceed = joinPoint.proceed();
            System.out.println("目標方法返回值:" + proceed);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("aop end................");
    }
}

2、使用自定義註解@annotation

在類上和方法上加上註解

@Service("aopDemo2Service")
@AopAnnotation
public class AopDemo2ServiceImpl implements IAopDemoService{

    @Override
    @AopAnnotation
    public String getDemo() {
        System.out.println("demo2 start..............");
        System.out.println("我是沒有入參的demo");
        System.out.println("demo2 end..............");
        return "no param!";
    }

    @Override
    public String getDemo(String arg) {
        System.out.println("demo2 start..............");
        System.out.println("我是有一個入參的demo,入參是:" + arg);
        System.out.println("demo2 end..............");
        return "one param!";
    }

    @Override
    public String getDemo(int arg1, String arg2) {
        System.out.println("demo2 start..............");
        System.out.println("我是有兩個入參的demo,第一個參數:" + arg1 + "\n第二個參數:" + arg2);
        System.out.println("demo2 end..............");
        return "two params!";
    }

aop配置

	/**
     * 只有方法上有註解纔會使用切面
     */
	@Pointcut("@annotation(com.wfs.springboot.annotation.AopAnnotation)")
    public void pointCut() {

    }

    /**
     * args 用於匹配到第一個參數爲Integer、第二個參數爲String的連接點
     * @param joinPoint
     */
    @Around(value = "pointCut()")
    public void around(ProceedingJoinPoint joinPoint) {
        System.out.println("aop start................");
        try {
            Object proceed = joinPoint.proceed();
            System.out.println("目標方法返回值:" + proceed);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("aop end................");
    }

3、@winthin、winthin

	/**
     * 使用@within 當前註解的類所有方法都會被織入
     */
    @Pointcut("@within(com.wfs.springboot.annotation.AopAnnotation)")
    public void pointCut() {

    }
   /**
     * IAopDemoService類型及子類型的任何方法
     */
	@Pointcut("within(com.wfs.springboot.aop.IAopDemoService+)")
    public void pointCut() {

    }

within支持表達式,類似execution

4、@args

僅支持只有一個入參的方法,並且參數類上必須持有定義的註解
aop配置

@Around(value = "pointCut() && @args(com.wfs.springboot.annotation.AopAnnotation)")
    public void around(ProceedingJoinPoint joinPoint) {
        System.out.println("aop start................");
        try {
            Object proceed = joinPoint.proceed();
            System.out.println("目標方法返回值:" + proceed);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("aop end................");
    }

入參類

@Data
@AopAnnotation
public class User {

    private String name;

    private SexEnum sex;
}

調用方法

	@Override
    public void getDemo(User user) {
        System.out.println("我有帶註解的入參");
    }

5、this

只支持全限定名,不支持通配符;是對代理對象類型的匹配所以包括接口類型也匹配
當前目標對象實現了IAopDemoService接口的任何方法

@Pointcut("this(com.wfs.springboot.aop.IAopDemoService)")
    public void pointCut() {

    }

6、target、@target

只支持全限定名,不支持通配符;是對目標對象類型的匹配所以不包括接口類型匹配

    @Pointcut("target(com.wfs.springboot.aop.AopDemo1ServiceImpl)")
    public void pointCut() {

    }
	@Pointcut("within(com.wfs.springboot.aop.IAopDemoService+) && @target(com.wfs.springboot.annotation.AopAnnotation)")
    public void pointCut() {

    }

@target不能單獨使用,啓動會報錯

7、bean

支持通配符

	/**
     * 表示bean的id或者名稱爲aopDemo1Service
     */
    @Pointcut("bean(aopDemo1Service)")
    public void pointCut() {

    }

四、通知參數

public interface JoinPoint {
	// 連接點的信息execution(String com.wfs.springboot.aop.AopDemo1ServiceImpl.getDemo(String))
    String toString();
    //連接點簡要信息 execution(AopDemo1ServiceImpl.getDemo(..))
    String toShortString();
	//連接點詳細信息execution(public java.lang.String com.wfs.springboot.aop.AopDemo1ServiceImpl.getDemo(java.lang.String))
    String toLongString();
	//返回代理對象
    Object getThis();
	//返回目標對象
    Object getTarget();
	// 返回目標參數
    Object[] getArgs();
	// 返回當前連接點的簽名
    Signature getSignature();
	// 返回連接點在類文件中的位置
    SourceLocation getSourceLocation();
	//連接點類型 method-execution
    String getKind();
	// 返回連接點靜態部分
    JoinPoint.StaticPart getStaticPart();
public interface Signature {
    //String com.wfs.springboot.aop.AopDemo1ServiceImpl.getDemo(int,String)
    String toString();
	//AopDemo1ServiceImpl.getDemo(..)
    String toShortString();
	//public java.lang.String com.wfs.springboot.aop.AopDemo1ServiceImpl.getDemo(int,java.lang.String)
    String toLongString();
	// 方法名 getDemo
    String getName();
	// 表示修飾符  1
    int getModifiers();
	// 目標類
    Class getDeclaringType();
	// 目標類全限定名
    String getDeclaringTypeName();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章