Spring實現AOP的三種方式


什么是AOP

AOP(Aspect Oriented Programming),即面向切面編程,可以說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP引入封裝、繼承、多態等概念來建立一種對象層次結構,用於模擬公共行為的一個集合。不過OOP允許開發者定義縱向的關系,但並不適合定義橫向的關系,例如日志功能。日志代碼往往橫向地散布在所有對象層次中,而與它對應的對象的核心功能毫無關系對於其他類型的代碼,如安全性、異常處理和透明的持續性也都是如此,這種散布在各處的無關的代碼被稱為橫切(cross cutting),在OOP設計中,它導致了大量代碼的重復,而不利於各個模塊的重用。

AOP技術恰恰相反,它利用一種稱為"橫切"的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行為封裝到一個可重用模塊,並將其命名為"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重復代碼,降低模塊之間的耦合度,並有利於未來的可操作性和可維護性。

使用"橫切"技術,AOP把軟件系統分為兩個部分:核心關注點橫切關注點。業務處理的主要流程是核心關注點,與之關系不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如權限認證、日志、事物。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。

AOP核心概念

1、橫切關注點

對哪些方法進行攔截,攔截后怎么處理,這些關注點稱之為橫切關注點

2、切面(aspect)

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

3、連接點(joinpoint)

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

4、切入點(pointcut)

對連接點進行攔截的定義

5、通知(advice)

所謂通知指的就是指攔截到連接點之后要執行的代碼,通知分為前置、后置、異常、最終、環繞通知五類

6、目標對象

代理的目標對象

7、織入(weave)

將切面應用到目標對象並導致代理對象創建的過程

8、引入(introduction)

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

 

共通的接口和實現類

在實現三種方式之前,先定義一個接口和一個接口的實現類

接口:

package com.maolei.service;

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

接口實現類:

package com.maolei.service;

public class UserServiceImpl implements UserService{

    public void add() {
        System.out.println("插入數據");
        
    }

    public void delete() {
        System.out.println("刪除數據");
        
    }

    public void update() {
        System.out.println("更新數據");
        
    }

    public void query() {
        System.out.println("查詢數據");
        
    }
    

}

 

實現AOP的三種方式

1、使用原生的Spring API接口

定義兩個log類

package com.maolei.log;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class Log implements MethodBeforeAdvice{

    public void before(Method method, Object[] args, Object target) throws Throwable {
        // method:要執行對象的方法
        // args:參數
        // target:要執行對象的實例
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被執行了");
    }

}

 

package com.maolei.log;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterLog implements AfterReturningAdvice{

    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        // returnValue:方法執行后返回的值
        System.out.println("執行了"+method.getName()+",這個方法的返回值為"+returnValue);
        
    }

}

配置application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.maolei.service.UserServiceImpl"/>
    <bean id="log" class="com.maolei.log.Log"/>
    <bean id="afterLog" class="com.maolei.log.AfterLog"/>
    
    <!-- 方式一:使用原生的Spring API接口 -->
    <!-- 配置aop:需要導入aop的約束 -->
    <aop:config>
    <!-- 切入點:expression表達式,execution(要執行的位置!* * * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.maolei.service.UserServiceImpl.*(..))"/>
        <!-- 定義通知器 -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config> 

</beans>

測試類

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.maolei.service.UserService;

public class MyTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = context.getBean("userService",UserService.class);
        userService.query();

    }

}

測試結果:

com.maolei.service.UserServiceImpl的query被執行了
查詢數據
執行了query,這個方法的返回值為null

2、自定義橫切關注點

自定義類

package com.maolei.diy;

public class DiyPointCut {
    public void before() {
        System.out.println("======方法執行前======");
    }
    
    public void after() {
        System.out.println("======方法執行后======");
    }

}

application.xml配置追加實現方式二

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.maolei.service.UserServiceImpl"/>
    <bean id="log" class="com.maolei.log.Log"/>
    <bean id="afterLog" class="com.maolei.log.AfterLog"/>
    
    <!-- 方式一:使用原生的Spring API接口 -->
    <!-- 配置aop:需要導入aop的約束 -->
    <!-- <aop:config>
    切入點:expression表達式,execution(要執行的位置!* * * * *)
        <aop:pointcut id="pointcut" expression="execution(* com.maolei.service.UserServiceImpl.*(..))"/>
        定義通知器
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config> -->
    
    <!-- 方式二:自定義橫切關注點 -->
    <bean id ="diy" class="com.maolei.diy.DiyPointCut"/>
    <aop:config>
        <aop:aspect ref="diy">
            <aop:pointcut expression="execution(* com.maolei.service.UserServiceImpl.*(..))" id="pointcut"/>
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
    
</beans>

測試類不變,測試結果:

======方法執行前======
查詢數據
======方法執行后======

3、注解實現AOP

創建一個注解類

 

package com.maolei.diy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.maolei.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("======方法執行前======");
    }
    @After("execution(* com.maolei.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("======方法執行后======");
    }
    @Around("execution(* com.maolei.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        Signature signature = jp.getSignature();
        System.out.println("signature:"+signature);
        System.out.println("環繞前");
        Object proceed = jp.proceed();
        System.out.println("環繞后");
    }

}

 

application.xml配置追加實現方式三:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans
 6         https://www.springframework.org/schema/beans/spring-beans.xsd
 7         http://www.springframework.org/schema/aop
 8         https://www.springframework.org/schema/aop/spring-aop.xsd">
 9 
10     <bean id="userService" class="com.maolei.service.UserServiceImpl"/>
11     <bean id="log" class="com.maolei.log.Log"/>
12     <bean id="afterLog" class="com.maolei.log.AfterLog"/>
13     
14     <!-- 方式一:使用原生的Spring API接口 -->
15     <!-- 配置aop:需要導入aop的約束 -->
16     <!-- <aop:config>
17     切入點:expression表達式,execution(要執行的位置!* * * * *)
18         <aop:pointcut id="pointcut" expression="execution(* com.maolei.service.UserServiceImpl.*(..))"/>
19         定義通知器
20         <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
21         <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
22     </aop:config> -->
23     
24     <!-- 方式二:自定義切面 -->
25     <!-- <bean id ="diy" class="com.maolei.diy.DiyPointCut"/>
26     <aop:config>
27         <aop:aspect ref="diy">
28             <aop:pointcut expression="execution(* com.maolei.service.UserServiceImpl.*(..))" id="pointcut"/>
29             <aop:before method="before" pointcut-ref="pointcut"/>
30             <aop:after method="after" pointcut-ref="pointcut"/>
31         </aop:aspect>
32     </aop:config> -->
33     
34     <!-- 方式三: -->
35     <bean id ="annotationPointCut" class="com.maolei.diy.AnnotationPointCut"/>
36     <!-- 開啟注解支持 -->
37     <aop:aspectj-autoproxy/>
38 
39 </beans>

測試類不變,測試結果:

signature:void com.maolei.service.UserService.query()
環繞前
======方法執行前======
查詢數據
======方法執行后======
環繞后

 

 

Spring對AOP的支持

Spring中AOP代理由Spring的IOC容器負責生成、管理,其依賴關系也由IOC容器負責管理。因此,AOP代理可以直接使用容器中的其它bean實例作為目標,這種關系可由IOC容器的依賴注入提供。Spring創建代理的規則為:

1、默認使用Java動態代理來創建AOP代理,這樣就可以為任何接口實例創建代理了

2、當需要代理的類不是代理接口的時候,Spring會切換為使用CGLIB代理,也可強制使用CGLIB

AOP編程其實是很簡單的事情,縱觀AOP編程,程序員只需要參與三個部分:

1、定義普通業務組件

2、定義切入點,一個切入點可能橫切多個業務組件

3、定義增強處理,增強處理就是在AOP框架為普通業務組件織入的處理動作

所以進行AOP編程的關鍵就是定義切入點和定義增強處理,一旦定義了合適的切入點和增強處理,AOP框架將自動生成AOP代理,即:代理對象的方法=增強處理+被代理對象的方法。

 

理論參照:https://www.cnblogs.com/xrq730/p/4919025.html

代碼來自狂神說!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM