1. 程式人生 > >Spring總結六:AOP(面向切面編程)

Spring總結六:AOP(面向切面編程)

版本 int run 傳統 代碼 tid contex except unit

概述:

  AOP(Aspect-Oriented Programming,面向切面的編程),它是可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。它是一種新的方法論,它是對傳統OOP編程的一種補充。AOP是希望能夠將通用需求功能從不相關的類當中分離出來,能夠使得很多類共享一個行為,一旦發生變化,不必修改很多類,而只需要修改這個行為即可。

  AOP就是將公用功能提取出來,如果以後公用功能的需求發生變化,只需要改動公用的模塊的代碼即可,多個調用的地方則不需要改動。所謂面向切面,就是只關註通用功能,而不關註業務邏輯。實現方式一般是通過攔截。一般應用在日誌記錄,權限驗證,異常攔截等。

  1,Aspect(切面):通常是一個類,裏面可以定義切入點和通知
  2,JointPoint(連接點):程序執行過程中明確的點,一般是方法的調用
  3,Advice(通知):AOP在特定的切入點上執行的增強處理,有before,after,afterReturning,afterThrowing,around
  4,Pointcut(切入點):就是帶有通知的連接點,在程序中主要體現為書寫切入點表達式
  5,AOP代理:AOP框架創建的對象,代理就是目標對象的加強。Spring中的AOP代理可以使JDK動態代理,也可以是CGLIB代理,前者基於接口,後者基於子類,原理我們後面有時間再寫一篇博客。

好了,我們先通過例子來了解一下基於xml配置的AOP:

簡單的實現轉賬功能,劉德華給張學友轉賬10000元:

首先我們把pom.xml中需要包引進去(我是通過父工程管理的jar包版本,所以沒有寫版本信息):

<dependencies>
    <!--spring需要的包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
</dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies>

我們的dao和service代碼:

public class AccountDao {
    /**
     * 轉入方法
     */
    public void shiftto(){
        System.out.println("張學友的賬戶轉入了 10000...");
    }

    /**
     * 轉出方法
     */
    public void rollout(){
        System.out.println("劉德華的賬戶轉出了 10000...");
    }
}

public class AccountService {

    private AccountDao accountDao;

    /**
     * 基於xml配置 提供一個set方法
     */
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    /**
     * 轉賬方法
     */
    public String transfer() {
        //轉出
        accountDao.rollout();
        //轉入
        accountDao.shiftto();
        return "我是AccountService類的transfer方法的返回值";
    }
}

然後配置spring配置文件中對應的bean:

    <!--目標類-->
    <bean id="accountDao" class="com.zy.dao.AccountDao"></bean>
    <bean id="accountService" class="com.zy.service.AccountService">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

如果只有上面這些的話(不使用AOP的情況),我們可以通過測試代碼:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
    @Value("#{accountService}")
    private AccountService service;

    @Test
    public void transfer() throws Exception {
        service.transfer();
    }

}

得出的結果如下:

技術分享圖片

那麽如果現在我們要把轉賬的功能放在事務裏的話,正常情況下需要在實現的service中加入事務的代碼,但是使用AOP的話我們可以這樣做:

新建自己的AsPect類:

public class MyAspect {
    /**
     * before 前置通知方法
     */
    public void before(JoinPoint joinPoint) {
        System.out.println("---------------由" + joinPoint.getTarget().getClass().getSimpleName() + "類開啟事務");
    }

    /**
     * afterReturning 後置通知方法
     */
    public void afterReturning(JoinPoint joinPoint, Object returnVal) {
        System.out.println("---------------提交事務,返回值為:" + returnVal);
    }

    /**
     * after 最終通知方法
     */
    public void after(JoinPoint joinPoint) {
        System.out.println("---------------釋放資源");
    }

    /**
     * afterThrowing 後置異常通知方法
     */
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        System.out.println("---------------事務回滾,程序異常信息:" + ex.getMessage());
    }
}

這個時候 我們需要在spring配置文件applicationContext.xml中這樣配置:

<?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"
       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
">
    <!--目標類-->
    <bean id="accountDao" class="com.zy.dao.AccountDao"></bean>
    <bean id="accountService" class="com.zy.service.AccountService">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--切面類:裏面可以有多個增強目標類的方法-->
    <bean id="myAspect" class="com.zy.aspect.MyAspect"></bean>

    <!--配置目標類和切面類的關系-->
    <aop:config proxy-target-class="false">
        <!--配置切面:指定增強方法的位置-->
        <aop:aspect ref="myAspect">
            <!--配置切入點-->
            <aop:pointcut id="myPointcut" expression="bean(*Service)"></aop:pointcut>
            <!--配置切入點和增強方法的聯系-->
            <!--before 前置通知-->
            <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
            <!--afterReturning 後置通知 可能有返回值 returnVal要和方法的形參名稱一致-->
            <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"
                                 returning="returnVal"></aop:after-returning>
            <!--after 最終通知-->
            <aop:after method="after" pointcut-ref="myPointcut"></aop:after>
            <!--後置異常通知-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="ex"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
</beans>

最後再運行我們的測試代碼會得出以下結果:

技術分享圖片

這樣我們就把事務加進去了,這樣做最大的好處就是可以隨時把事務去掉或者是修改其實現代碼。

Spring總結六:AOP(面向切面編程)