深刻學習Spring框架(三)- AOP面向切面

  

1.什麼是AOP?

  AOP爲 Aspect Oriented Programming 的縮寫,即面向切面編程, 經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術.。AOP是OOP的延續, 是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離, 從而使得業務邏輯各部分之間的耦合度下降, 提升程序的可重用性, 同時提升了開發的效率。java

 

2.AOP與傳統開發對比

  傳統開發模型: 縱向的編程spring

  

 

  面向切面編程: 縱橫配合的編程數據庫

  

3.爲何用AOP?

  在咱們的項目中幾乎每一個業務都須要有事務控制、日誌管理、權限控制等操做。這樣就形成了須要寫大量的重複代碼。例如:express

  

//模擬事務管理器
public class MyTransactionManager {
    public void begin() {
        System.out.println("開啓事務");
    }
    
    public void commit() {
        System.out.println("提交事務");
    }
    
    public void rollback() {
        System.out.println("回滾事務");
    }
    
    public void close() {
        System.out.println("關閉session");
    }
}
//Service層代碼
public class UserServiceImpl implements UserService {
    // 引入事務管理器
    private MyTransactionManager txManager = new MyTransactionManager();

    @Override
    public void insert(String username) {
        try {
            txManager.begin();
            System.out.println("調用dao層插入方法");
            txManager.commit();
        } catch (Exception e) {
            txManager.rollback();
        } finally {
            txManager.close();
        }
    }

    @Override
    public void update(String username) {
        try {
            txManager.begin();
            System.out.println("調用dao層修改方法");
            txManager.commit();
        } catch (Exception e) {
            txManager.rollback();
        } finally {
            txManager.close();
        }
    }
}

每一個須要對數據庫數據進行修改的業務都要進行事務處理,就會有大量的重複代碼,因此就須要用APO來減小重複代碼、提升開發效率、方便維護。編程

 

4.AOP的實現方式(原理)

  AOP的底層是使用動態代理模式來實現的。(JDK動態代理和CGLIB代理技術)
  動態代理,就是使用者使用的不是真實的對象,而是使用的一個代理對象,而這個代理對象中包含的就是真實的對象,代理對象就是不改變原有對象的功能方法的基礎之上封裝新的功能。
  AOP的原理就是把須要加強的代碼(重複的代碼)定義在代理類中,自己只保留核心的代碼。在方法運行時切到代理對象中執行。session

  

5.JDK動態代理

  JDK動態代理是Java官方的代理,使用JDK官方的Proxy類建立代理對象。app

  5.1 java.lang.reflect.Proxy 類:

Java 動態代理機制生成的全部動態代理類的父類,它提供了一組靜態方法來爲一組接口動態地生成代理類及其對象。
主要方法:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder)
參數:
框架

loader :類加載器
interfaces : 模擬的接口
hanlder :代理執行處理器(經過實現java.lang.reflect.InvocationHandler接口)ide

方法職責:爲指定類加載器、一組接口及調用處理器生成動態代理類實例
返回:動態生成的代理對象函數式編程

  5.2 java.lang.reflect.InvocationHandler接口:

public Object invoke(Object proxy, Method method, Object[] args)
參數:

proxy :生成的代理對象
method :當前調用的真實方法對象
args :當前調用方法的實參

方法職責:負責集中處理動態代理類上的全部方法調用
返回: 真實方法的返回結果

5.3 示例代碼:

須要的jar包:

  總體結構:

pojo類

package com.gjs.pojo;

public class User {
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public User() {
        super();
    }
    public User(Integer id, String name, Integer age) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
    }
    
}

service層

package com.gjs.service.impl;

import com.gjs.pojo.User;
import com.gjs.service.UserService;

public class UserServiceImpl implements UserService {

    @Override
    public void insert(User user) {
        System.out.println("調用dao層插入方法");
    }

    @Override
    public void update(User user) {
        System.out.println("調用dao層修改方法");
    }

}

模擬事務管理器

package com.gjs.util;

//模擬事務管理器
public class MyTransactionManager {
    public void begin() {
        System.out.println("開啓事務");
    }
    
    public void commit() {
        System.out.println("提交事務");
    }
    
    public void rollback() {
        System.out.println("回滾事務");
    }
    
    public void close() {
        System.out.println("關閉session");
    }
}

JDK動態代理類:

package com.gjs.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.gjs.util.MyTransactionManager;
//JDK動態代理類
public class DynamicProxyHandeler {
    private Object target; // 被代理的對象
    private MyTransactionManager manager; // 事務管理器

    public void setTarget(Object target) {
        this.target = target;
    }

    public void setManager(MyTransactionManager manager) {
        this.manager = manager;
    }

    public <T> T getProxyObject(Class<T> clz) {

        ClassLoader loader = this.getClass().getClassLoader();// 類加載器
        Class<?>[] interfaces = clz.getInterfaces();// 被代理對象的接口
        // InvocationHandler():Proxy的處理器,具體作正在加強的地方(須要開發者本身實現)
        Object proxyInstance = Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {

            /**
             * 處理器中具體作加強的方法
             * 
             * @param proxy
             *            代理對象
             * @param method
             *            被代理對象的方法
             * @param args
             *            被代理對象方法的參數
             * @return 被代理對象方法執行之後的結果
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                try {
                    // 1.開啓事務
                    manager.begin();

                    // 執行被代理對象的犯法
                    result = method.invoke(target, args);

                    // 2.提交事務
                    manager.commit();
                } catch (Exception e) {
                    e.printStackTrace();
                    // 3.回滾事務
                    manager.rollback();
                } finally {
                    // 4.關閉session
                    manager.close();
                }
                return result;
            }
        });
        return (T) proxyInstance;
    }
}

配置文件:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
    http://www.springframework.org/schema/context/spring-context.xsd
">
    
    <!-- 配置模擬的事務管理器 -->
    <bean id="manager" class="com.gjs.util.MyTransactionManager"/>
    
    <!-- 配置代理類 -->
    <bean id="dynamicProxyHandeler" class="com.gjs.proxy.DynamicProxyHandeler">
        <!-- 注入被代理的對象 -->
        <property name="target" >
            <bean class="com.gjs.service.impl.UserServiceImpl"/>
        </property>
        <!-- 注入事務管理器 -->
        <property name="manager" ref="manager"/>
    </bean>
    
</beans>

測試類

package com.gjs.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.gjs.pojo.User;
import com.gjs.proxy.DynamicProxyHandeler;
import com.gjs.service.UserService;
import com.gjs.service.impl.UserServiceImpl;

//表示先啓動Spring容器,把junit運行在Spring容器中
@RunWith(SpringJUnit4ClassRunner.class)
// 表示從哪裏加載資源文件,默認從src(源目錄)下面加載
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpring {
    // 注入代理類的對象
    @Autowired
    private DynamicProxyHandeler dynamicProxyHandeler;

    @Test
    public void testInsert() throws Exception {
        // 經過代理類獲取代理對象
        UserService proxyObject = dynamicProxyHandeler.getProxyObject(UserServiceImpl.class);
        User user = new User(null, "張三", 18);
        proxyObject.insert(user);
    }

    @Test
    public void testUpdate() throws Exception {
        // 經過代理類獲取代理對象
        UserService proxyObject = dynamicProxyHandeler.getProxyObject(UserServiceImpl.class);
        User user = new User(1, "李四", 18);

        // 執行代理對象的修改方法
        proxyObject.update(user);
    }

    @Test
    public void test1() throws Exception {
        // 經過代理類獲取代理對象
        UserService proxyObject = dynamicProxyHandeler.getProxyObject(UserServiceImpl.class);
        proxyObject.toString();
    }
}

5.4 JDK動態代理的不足

1.JDK動態代理的對象必需要實現一個接口
2.須要爲每一個對象建立代理對象
3.動態代理的最小單位是類(全部類中的方法都會被處理),查詢方法不須要事務,可能不須要被代理

6.CGLIB動態代理

  CGLIB和JDK動態代理同樣都是動態代理,不一樣的是CGLIB代理沒有接口也能夠進行代理,並且在Spring中已經集成了CGLIB代理,能夠直接使用,不須要再導入其餘jar包。
  CGLIB代理使用Enhancer類代理

  

  6.1 示例代碼:

  動態代理類:

package com.gjs.proxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;

import com.gjs.util.MyTransactionManager;
//CGLIB動態代理類
public class DynamicProxyHandeler {
    private Object target; // 被代理的對象
    private MyTransactionManager manager; // 事務管理器

    public void setTarget(Object target) {
        this.target = target;
    }

    public void setManager(MyTransactionManager manager) {
        this.manager = manager;
    }

    public <T> T getProxyObject(Class<T> clz) {

        ClassLoader loader = this.getClass().getClassLoader();// 類加載器
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(loader); // 設置類加載器
        enhancer.setSuperclass(target.getClass()); // 設置代理對象的class

        /*
         * 代理的具體實現 InvocationHandler:org.springframework.cglib.proxy.InvocationHandler
         */
        enhancer.setCallback(new InvocationHandler() {
            /**
             * 處理器中具體作加強的方法
             * 
             * @param proxy
             *            代理對象
             * @param method
             *            被代理對象的方法
             * @param args
             *            被代理對象方法的參數
             * @return 被代理對象方法執行之後的結果
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                try {
                    // 1.開啓事務
                    manager.begin();

                    // 執行被代理對象的犯法
                    result = method.invoke(target, args);

                    // 2.提交事務
                    manager.commit();
                } catch (Exception e) {
                    e.printStackTrace();
                    // 3.回滾事務
                    manager.rollback();
                } finally {
                    // 4.關閉session
                    manager.close();
                }
                return result;
            }
        });
        Object proxyObject = enhancer.create();// 建立代理對象
        return (T) proxyObject;
    }
}

  其餘代碼參考jdk動態代理的示例代碼

6.2 CGLIB動態代理使用要點

1,CGLIB能夠生成目標類的子類,並重寫父類非final修飾符的方法。
2,要求類不能是final的,要代理的方法要是非final、非static、非private的。
3,動態代理的最小單位是類(全部類中的方法都會被處理);

 

7. 直接使用動態代理的缺陷

1.直接使用代理雖然解決代碼重複問題,但卻須要在配置文件中爲每個類都配置代理類
2.沒法經過規則制定要攔截的方法,全部方法都會進行代理。例如:查詢的方法不須要進行事務處理、toString方法不須要代理。

  Spring提供了AOP的實現就能解決這些問題。

8.Spring的AOP

Spring的AOP能夠經過規則設置來攔截方法,加入能夠統一處理的代碼。而且spring會根據目標類是否實現了接口來決定採用哪一種動態代理的方式:
  若目標對象實現了接口,Spring就會使用JDK動態代理。
  若目標對象沒有實現任何接口,Spring就使用CGLIB庫生成目標對象的子類。

AOP相關術語:

Joinpoint(鏈接點):所謂鏈接點是指那些被攔截到的點。在spring中,這些點指的是方法,由於spring只支持方法類型的鏈接點。即根據規則指定攔截的方法,咱們將每個被攔截的方法稱爲鏈接點。

Pointcut(切入點):切入點就是攔截方法設置的規則,即須要對哪些Joinpoint進行攔截的定義。

Advice(通知/加強):設置在方法以前攔截或者方法執行以後攔截或者方法出異常後攔截,或者方法以前和以後都攔截。咱們將這些攔截場景稱爲通知。 所謂通知是指攔截到Joinpoint以後所要作的事情就是通知。通知的類型:前置通知,後置通知,異常通知,最終通知,環繞通知。

Aspect(切面):切面就是咱們的攔截處理類。是切入點和通知的結合。

Weaving(織入):把切面加入到對象,並建立出代理對象的過程。(這個過程由Spring自動來完成)

9.基於XML配置AOP

  總體結構:

1.導入相關jar

  AOP 思想必須使用AspectJ語法,而AOP思想不是Spring框架設計出來的,而是叫一個AOP聯盟組織提出這種思想,因此須要導入 AOP聯盟提供的 aspectjweaver.jar(aspectweaver織入包)

  2.建立配置文件並引入聲明及命名空間

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    ">
    
</beans>

  3.編寫一個切面類

package com.gjs.util;

//模擬事務管理器
public class MyTransactionManager {
    public void begin() {
        System.out.println("開啓事務");
    }
    
    public void commit() {
        System.out.println("提交事務");
    }
    //Throwable e 參數名和 aop 異常加強的 after-throwing 的值相同
    public void rollback(Throwable e) {
        e.printStackTrace();
        System.out.println("回滾事務");
    }
    
    public void close() {
        System.out.println("關閉session");
    }
}

  4.添加AOP配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        ">
    
    <!-- 配置事務管理器 -->
    <bean id="manager" class="com.gjs.util.MyTransactionManager"/>
    
    <!-- 配置service實現類 -->
    <bean id="service" class="com.gjs.service.impl.UserServiceImpl"/>
    
    <!-- 
        AOP配置: W (where)W(when)W(what) 原則
            where:從哪裏切入
            when:何時
            what:作什麼
     -->
     
     <!-- proxy-target-class:是不是基於類的代理 (CGLIB代理)。
         若是出現service類注入時注入了代理對象可使用該屬性-->
     <aop:config proxy-target-class="true">
         <!-- 
             配置切入點(Pointcut):在哪些類,哪些方法上切入(where)
                 expression:切入點表達式 
                 id:切入點惟一標識
         -->
         <aop:pointcut expression="execution(* com.gjs.service..*.*(..))" id="pt"/>
         
         <!-- 配置切面 = 切入點(pointcut)+ 通知/加強(advice)
             ref : 引入須要加強的事務管理器
         -->
         <aop:aspect ref="manager">
             <!--
              when :何時
                  以前(before)
                  以後(after-returning)
                  異常(after-throwing)
                  最終(after))
             -->
             <!-- what:作什麼(method) -->
             <!-- where:從哪裏切入(pointcut-ref) -->
             <aop:before method="begin" pointcut-ref="pt"/>
             <aop:after-returning method="commit" pointcut-ref="pt"/>
             <!-- 異常加強 
                 throwing : 異常,值 必須和對應方法名稱的 參數名稱同樣
                 拋出的是頂級異常Throwable
                 -->
             <aop:after-throwing method="rollback" pointcut-ref="pt" throwing="e"/>
             <aop:after method="close" pointcut-ref="pt"/>
         </aop:aspect>
     </aop:config>
</beans>

 

  測試類

package com.gjs.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.gjs.pojo.User;
import com.gjs.service.impl.UserServiceImpl;

//表示先啓動Spring容器,把junit運行在Spring容器中
@RunWith(SpringJUnit4ClassRunner.class)
//表示從哪裏加載資源文件,默認從src(源目錄)下面加載
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpring {
    // 注入代理類的對象
    @Autowired
    private UserServiceImpl service;

    @Test
    public void testInsert() throws Exception {
        User user = new User(null, "張三", 18);
        service.insert(user);
    }

    @Test
    public void testUpdate() throws Exception {
        User user = new User(1, "李四", 18);
        service.update(user);
    }

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

其餘代碼參考動態代理的示例代碼

環繞加強(環繞通知方式)

切面類

package com.gjs.util;

import org.aspectj.lang.ProceedingJoinPoint;

//模擬事務管理器
public class MyTransactionManager {
    
public void allInOne(ProceedingJoinPoint pjo) {
        try {
            System.out.println("開啓事務");
            
            //執行具體的方法
            pjo.proceed();
            
            System.out.println("提交事務");
            
        } catch (Throwable e) {
            System.out.println("回滾事務");

        }finally {
            System.out.println("關閉session");
        }
        
    }
}

 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        ">
    
    <!-- 配置事務管理器 -->
    <bean id="manager" class="com.gjs.util.MyTransactionManager"/>
    
    <!-- 配置service實現類 -->
    <bean id="service" class="com.gjs.service.impl.UserServiceImpl"/>
    
    <!-- 
        AOP配置: W (where)W(when)W(what) 原則
            where:從哪裏切入
            when:何時
            what:作什麼
     -->
     
     <!-- proxy-target-class:是不是基於類的代理 (CGLIB代理)。
         若是出現service類注入時注入了代理對象可使用該屬性-->
     <aop:config proxy-target-class="true">
         <!-- 
             配置切入點(Pointcut):在哪些類,哪些方法上切入(where)
                 expression:切入點表達式 
                 id:切入點惟一標識
         -->
         <aop:pointcut expression="execution(* com.gjs.service..*.*(..))" id="pt"/>
         
         <!-- 配置切面 = 切入點(pointcut)+ 通知/加強(advice)
             ref : 引入須要加強的事務管理器
         -->
         <aop:aspect ref="manager">
             <!-- 環繞加強:Spring事務管理就是使用環繞加強 -->
             <aop:around method="allInOne" pointcut-ref="pt"/>
         </aop:aspect>
     </aop:config>
</beans>

10.切入點表達式說明

execution(表達式)

  表達式語法:execution([修飾符] 返回值類型 包名.類名.方法名(參數))

寫法說明:(經常使用)

全匹配方式:
  public void com.gjs.service.impl.CustomerServiceImpl.saveCustomer()
訪問修飾符能夠省略 :
  void com.zj.service.impl.CustomerServiceImpl.saveCustomer()
返回值可使用*號,表示任意返回值:
  * com.zj.service.impl.CustomerServiceImpl.saveCustomer()
包名可使用*號,表示任意包,可是有幾級包,須要寫幾個*:
  * *.*.*.*.CustomerServiceImpl.saveCustomer()
使用..來表示當前包,及其子包:
  * com..CustomerServiceImpl.saveCustomer()
類名可使用*號,表示任意類:
  * com..*.saveCustomer()
方法名可使用*號,表示任意方法:
  * com..*.*()
參數列表可使用*,表示參數能夠是任意數據類型,可是必須有參數:
  * com..*.*(*)
參數列表可使用..表示有無參數都可,有參數能夠是任意類型:
  * com..*.*(..)
全通配方式:
  * *..*.*(..)

11.經常使用標籤

11.1.<aop:config>

  做用:用於聲明開始aop的配置

11.2.<aop:aspect>

做用:用於配置切面。
屬性:
id:給切面提供一個惟一標識。
ref:引用配置好的通知類bean的id。

11.3.<aop:pointcut>

做用:用於配置切入點表達式
屬性:
expression:用於定義切入點表達式。
id:用於給切入點表達式提供一個惟一標識。

11.4.<aop:before>

做用:用於配置前置通知
屬性:
method:指定通知中方法的名稱。
pointct:定義切入點表達式
pointcut-ref:指定切入點表達式的引用

11.5.<aop:after-returning>

做用:用於配置後置通知,若是出了異常就必定不會調用切面的方法
屬性:
method:指定通知中方法的名稱。
pointct:定義切入點表達式
pointcut-ref:指定切入點表達式的引用

11.6.<aop:after-throwing>

做用:用於配置異常通知,只有出了異常纔會調用切面對應的方法
屬性:
method:指定通知中方法的名稱。
pointct:定義切入點表達式
pointcut-ref:指定切入點表達式的引用

11.7.<aop:after>

做用: 用於配置最終通知,無論出不出異常,調用的切面的方法
屬性:
method:指定通知中方法的名稱。
pointct:定義切入點表達式
pointcut-ref:指定切入點表達式的引用

11.8.<aop:around>

做用:用於配置環繞通知
屬性:
method:指定通知中方法的名稱。
pointct:定義切入點表達式
pointcut-ref:指定切入點表達式的引用

 

12.基於註解配置AOP

  12.1 示例代碼:

  切面類:

package com.gjs.util;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//事務管理器
@Component 
@Aspect     //聲明此類爲切面類, 等價於 <aop:aspect ref="manager">
public class MyTransactionManager {
    
    //設置切入點
    @Pointcut("execution(* com.gjs.service..*.*(..))")
    //至關於<aop:pointcut expression="execution(* com.gjs.service..*.*(..))" id="pt"/>
    public void pointcut() {}
    
    @Before("pointcut()")//至關於<aop:before method="begin" pointcut-ref="pt"/>
    public void begin() {
        System.out.println("開啓事務");
    }
    @After("pointcut()")//至關於<aop:after-returning method="commit" pointcut-ref="pt"/>
    public void commit() {
        System.out.println("提交事務");
    }
    @AfterThrowing(pointcut="pointcut()",throwing="e")
    // 至關於<aop:after-throwing method="rollback" pointcut-ref="pt" throwing="ex"/>
    public void rollback(Throwable e) {
        e.printStackTrace();
        System.out.println("回滾事務");
    }
    @AfterReturning("pointcut()")//至關於<aop:after method="close" pointcut-ref="pt"/>
    public void close() {
        System.out.println("關閉session");
    }
}

  配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        ">
        
    <!-- 配置service實現類 -->
    <bean id="service" class="com.gjs.service.impl.UserServiceImpl"/>
    
    <!-- 配置註解組件掃描的包位置 -->
    <context:component-scan base-package="com.gjs"/>
    
    <!-- 開啓spring aop的註解配置 -->
    <!-- proxy-target-class="true",是否要建立基於類的代理(CGLIB),默認建立JDK動態代理,可能會出現service實現類注入不了的問題 -->
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

其餘代碼參考xml方式

  12.2環繞通知方式:(通常用這種方式)

  切面類:

package com.gjs.util;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//模擬事務管理器
@Component
@Aspect // 聲明此類爲切面類, 等價於 <aop:aspect ref="manager">
public class MyTransactionManager {
    // 設置切入點,至關於<aop:pointcut expression="execution(* com.gjs.service..*.*(..))" id="pt"/>
    @Pointcut("execution(* com.gjs.service..*.*(..))")
    public void pointcut() {
    }
    @Around("pointcut()")//至關於<aop:around method="allInOne" pointcut-ref="pt"/>
    public void allInOne(ProceedingJoinPoint pjo) {

        try {
            System.out.println("開啓事務");

            // 執行具體的方法
            pjo.proceed();

            System.out.println("提交事務");

        } catch (Throwable e) {
            System.out.println("回滾事務");

        } finally {
            System.out.println("關閉session");
        }

    }
}

配置文件與上面相同

13.經常使用註解

13.1 @Aspect

  做用:把當前類聲明爲切面類。

13.2 @Pointcut

做用:指定切入點表達式
屬性:
value:指定表達式的內容

13.3 @Before

做用:把當前方法當作是前置通知。
屬性:
value:用於指定切入點表達式,還能夠指定切入點表達式的引用。

13.4 @After

做用:把當前方法當作是後置通知。報異常,就不執行
屬性:
value:用於指定切入點表達式,還能夠指定切入點表達式的引用。

13.5 @AfterThrowing

做用:把當前方法當作是異常通知。只有報異常才執行
屬性:
value:用於指定切入點表達式,還能夠指定切入點表達式的引用。

13.6 @AfterReturning

做用:把當前方法當作是最終通知。無論報不報異常都執行
屬性:
value:用於指定切入點表達式,還能夠指定切入點表達式的引用。

13.7 @Around

做用:把當前方法當作是環繞通知。
屬性:
value:用於指定切入點表達式,還能夠指定切入點表達式的引用。

注: @After和@AfterReturning 並無寫反,至於爲何從命名和xml方式相反就不得而知了,多是當時Spring的做者寫錯了,後面也沒有去改正

相關文章
相關標籤/搜索