Spring AOP理解及應用(附含自定義註解使用)

概念

  • 通知(Advice)

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

  1. 前置通知(Before):在目標方法被調用之前調用通知功能
  2. 後置通知(After):在目標方法完成之後調用通知,此時不會關心方法的輸出是什麼
  3. 返回通知(After-returning):在目標方法成功執行之後調用通知
  4. 異常通知(After-throwing):在目標方法拋出異常後調用通知
  5. 環繞通知(Around):通知包裹了被通知的方法,在被通知的方法調用之前和之後執行自定義的行爲
    後置通知和返回通知的區別是,後置通知是不管方法是否有異常,都會執行該通知;而返回通知是方法正常結束時纔會執行。
  • 連接點(Join point)

連接點是在應用執行過程中能夠插入切面的一個點。

  • 切點(Pointcut)

一個切面並不需要通知應用的所有連接點,切點有助於縮小切面所通知的連接點範圍。如果說通知定義了切面的“什麼”和“何時”的話,那麼切點就定義了“何處”。因此,切點其實就是定義了需要執行在哪些連接點上執行通知。

  • 切面(Aspect)

切面是通知和切點的結合。通知和切點共同定義了切面的全部內容——它是什麼,在何時和在何處完成其功能。

  • 引入(Introduction)

引入允許我們向現有的類添加新方法或屬性。

  • 織入(Weaving)

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

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

應用

spring AOP自定義註解方式實現日誌管理

  • 第一,註解:

@Before – 目標方法執行前執行

@After – 目標方法執行後執行

@AfterReturning – 目標方法返回後執行,如果發生異常不執行

@AfterThrowing – 異常時執行

@Around – 在執行上面其他操作的同時也執行這個方法

  • 第二,SpringMVC如果要使用AOP註解,必須將
<aop:aspectj-autoproxy proxy-target-class="true"/>

放在spring-servlet.xml(配置MVC的XML)中

  • 三、通過切點來選擇連接點

以下是Spring AOP所支持的AspectJ切點指示器

arg() 限制連接點匹配參數爲指定類型的執行方法
@args() 限制連接點匹配參數由指定註解標註的執行方法
execution() 用於匹配是連接點的執行方法
this() 限制連接點匹配AOP代理的bean引用爲指定類型的類
target 限制連接點匹配目標對象爲指定類型的類
@target 限制連接點匹配特定的執行對象,這些對象對應的類要具有指定類型的註解
within() 限制連接點匹配指定的類型
@within() 限制連接點匹配指定註解所標註的類型(當使用Spring AOP時,方法定義在由指定的註解所標註的類裏)
@annotation 限定匹配帶有指定註解的連接點
在這裏插入圖片描述

自定義註解聲明

package org.xdemo.example.springaop.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface Log {
    String name() default "";
}

利用AOP實現自定義註解

package org.xdemo.example.springaop.aop;
 
import java.lang.reflect.Method;
import java.util.UUID;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.xdemo.example.springaop.annotation.Log;
 
@Aspect
@Component
public class LogAop_2 {
 
    ThreadLocal<Long> time=new ThreadLocal<Long>();
    ThreadLocal<String> tag=new ThreadLocal<String>();
     
    @Pointcut("@annotation(org.xdemo.example.springaop.annotation.Log)")
    public void log(){
        System.out.println("我是一個切入點");
    }
     
    /**
     * 在所有標註@Log的地方切入
     * @param joinPoint
     */
    @Before("log()")
    public void beforeExec(JoinPoint joinPoint){
         
        time.set(System.currentTimeMillis());
        tag.set(UUID.randomUUID().toString());
         
        info(joinPoint);
         
        MethodSignature ms=(MethodSignature) joinPoint.getSignature();
        Method method=ms.getMethod();
        System.out.println(method.getAnnotation(Log.class).name()+"標記"+tag.get());
    }
     
    @After("log()")
    public void afterExec(JoinPoint joinPoint){
        MethodSignature ms=(MethodSignature) joinPoint.getSignature();
        Method method=ms.getMethod();
        System.out.println("標記爲"+tag.get()+"的方法"+method.getName()+"運行消耗"+(System.currentTimeMillis()-time.get())+"ms");
    }
     
    @Around("log()")
    public void aroundExec(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("我是Around,來打醬油的");
        pjp.proceed();
    }
     
    private void info(JoinPoint joinPoint){
        System.out.println("--------------------------------------------------");
        System.out.println("King:\t"+joinPoint.getKind());
        System.out.println("Target:\t"+joinPoint.getTarget().toString());
        Object[] os=joinPoint.getArgs();
        System.out.println("Args:");
        for(int i=0;i<os.length;i++){
            System.out.println("\t==>參數["+i+"]:\t"+os[i].toString());
        }
        System.out.println("Signature:\t"+joinPoint.getSignature());
        System.out.println("SourceLocation:\t"+joinPoint.getSourceLocation());
        System.out.println("StaticPart:\t"+joinPoint.getStaticPart());
        System.out.println("--------------------------------------------------");
    }
     
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章