天天看點

面向切面程式設計AOP面向切面程式設計:AOP

面向切面程式設計:AOP

簡介

AOP(Aspect Oriented Programming) 面向切面程式設計,是 OOP(Object Oriented Programming,面向對象程式設計) 的補充和完善;不過OOP引入封裝、多态、繼承等概念來建立一個對象層級結構,定義的是縱向關系;而AOP恰恰相反,它是利用一種稱為“橫切”的技術,剖解開封裝的内部,并将那些影響了多個類的公共行為封裝到一個可重用的子產品,并将其命名為 “Aspect”,即切面;所謂切面,就是那些與業務無關,卻為業務子產品所共同調用的邏輯和責任,便于減少系統的重複代碼;

AOP的八大概念:

1 切面(aspect)

類是對物體特征的抽象,切面就是對橫切關注點的抽象

2 切入點(pointcut)

通知定義了切面是什麼和何時,切點定義了何處,切點的定義會比對通知所要的織入的一個或多個連接配接點,我們通常使用明确的類的方法名稱來制定這些切點,或是利用正規表達式定義比對的類和方法名稱來指定這些切點。

切點個格式如下:

execution(* com.ganji.demo.service.user.UserService.GetDemoUser (..) )
           

3 橫切關注點 (target)

對那些方法進行攔截,攔截後如何處理,這些關注點稱為橫切關注點;

4 連接配接點(joinpoint)

被攔截到的點,因為Spring隻支援方法類型的連接配接點,是以在Spring中連接配接點指的就是被攔截的方法,實際上連接配接點還可以是字段或構造器;

5 通知(advice)

通知定義切面是什麼以及何時調用,何時調用包含以下幾種:

方法 解釋
@Before 在方法被調用之前調用通知
@After 在方法完成之後調用通知,無論方法執行是否成功
@After-returning 在方法成功執行之後調用通知
@After-throwing 在方法抛出異常後調用通知
@Around 通知包裹了被通知的方法,在被通知的方法調用之前和調用之後執行自定義的行為

6 引入(introduction)

在不修改代碼的前提下,引入可以在運作期為類動态地添加一些方法或字段;

7 代理(Proxy)

一個類被AOP織入增強後,就産出了一個結果類,它是融合了原類和增強邏輯的代理類。根據不同的代理方式,代理類既可能是和原類具有相同接口的類(JDK動态代理),也可能就是原類的子類(cglib動态代理),是以我們可以采用調用原類相同的方式調用代理類。

8 織入(weaving)

織入是把切面應用到目标對象并建立新的代理對象的過程。切面在指定的連接配接點被織入到目标對象。在目标對象的生命周期裡有多個點可以進行織入:

  • 編譯器:切面在目标類編譯時被織入。Aspect的織入編譯器就是以這種方式織入切面的。
  • 類加載器:切面在目标類加載到JVM時被織入。需要特殊的類加載(Classloader),它可以在目标類被引入之前增強該目标類的位元組碼(CGlib)
  • 運作期:切面在應用運作時的某個時刻被織入。AOP會為目标對象建立一個代理對象

引用

1 xml配置方式

在非spring boot項目中,通過sping.xml方式配置如下:

<bean id="helloWorldImpl1" class="com.xrq.aop.HelloWorldImpl1" />
        <bean id="helloWorldImpl2" class="com.xrq.aop.HelloWorldImpl2" />
        <bean id="timeHandler" class="com.xrq.aop.TimeHandler" />
        
        <aop:config>
            <aop:aspect id="time" ref="timeHandler">
                <aop:pointcut id="addAllMethod" expression="execution(* com.xrq.aop.HelloWorld.*(..))" />
                <aop:before method="printTime" pointcut-ref="addAllMethod" />
                <aop:after method="printTime" pointcut-ref="addAllMethod" />
            </aop:aspect>
        </aop:config>
           

2 使用注解

非spring boot項目添加引用

<dependency> 
     <groupId>org.aspectj</groupId> 
     <artifactId>aspectjrt</artifactId> 
     <version>1.6.11</version> 
</dependency> 
<dependency> 
     <groupId>org.aspectj</groupId> 
     <artifactId>aspectjweaver</artifactId> 
     <version>1.6.11</version> 
</dependency>
           

并在spirng.xml中添加注解掃描:

<context:component-scan base-package="com.**.**.** " />
<aop:aspectj-autoproxy />
           

而在spring boot項目中,直接添加引用即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>
           

非spring boot飲用注意點⚠️

若該切面使用在controller上,則一般因為controller的注解掃描配置在 spring-mvc.xml上,顧需要去掉切面類上的 @Component

然後再spring-mvc.xml上添加配置:

<bean id="AopTest" class="com.xhwl.seven.interceptor.*** "/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
           

說明:配置檔案中

<aop:aspectj-autoproxy proxy-target-class=“true”/> 的 proxy-target-class參數指是否使用cglib代理(無實作任何接口),預設為false; 若切面定義在controller這種無實作接口的類上,需要使用 proxy-target-class=“true” ;

切入點@Pointcut的使用

在确定切面aspect之後,需要在切面上确定切入點pointcut

定義

注解@pointcut("{切入點}")

其中{切入點}格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?) 
           
參數 說明
modifiers-pattern 修飾比對
ret-type-pattern 傳回值比對:可以為*表示任何傳回值,全路徑的類名等
declaring-type-pattern 類路徑比對
name-pattern 方法名比對:以指定方法名 或者 代表所有, set 代表以set開頭的所有方法
param-pattern 參數比對:可以指定具體的參數類型,多個參數間用“,”隔開,各個參數也可以用“”來表示比對任意類型的參數,如(String)表示比對一個String參數的方法;(,String) 表示比對有兩個參數的方法,第一個參數可以是任意類型,而第二個參數是String類型;可以用(…)表示零個或多個任意參數
throws-pattern 異常類型比對
PS: 其中後面帶“?”的為可選項;

一個切面aspect可以定義多個切入點pointcut,并支援運算符 || 、&& 、!

@Pointcut("execution(public * com.xhwl.seven.controller..*Controller.*(..))")
 public void pointCut1(){}
@Pointcut("execution(public * com.xhwl.seven.controller..*Service.*(..))")
public void pointCut2(){}
@Pointcut("pointCut1() ||  pointCut2()")
public void pointCut3(){}
           

使用

定義完Pointcut之後,可在通知 advice 上定義使用

@Before("pointCut2()")
@AfterReturning(returning = "o", pointcut = "pointCut3()")
           

執行個體

/**
 * 擷取方法注解
 * @author Kellan_Song
 * @createTime 2019年4月4日
 */
@Aspect
@Component
public class TestAOP {
	@Pointcut("execution(public * com.xhwl.microservice.controller..*Controller.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void doBefore(JoinPoint point) throws Exception {
       logger.info("===============請求内容===============");
       MethodSignature ms = (MethodSignature) point.getSignature();
       Method method = ms.getMethod();
       logger.info("【請求類方法名】:" + method.getName());
       Annotation annotation = method.getAnnotation(Test.class);
       if (annotation != null) {
          logger.info("【注解Test的value】:" + ((Test) annotation).value());
       }
    }
	
}