學習Spring之Aop手稿

參考:https://mp.weixin.qq.com/s/vfPKWYcUa4yjJ4cnmIEaxQspring

AOP特色:
express

  • 面向切面編程, 利用AOP對業務邏輯的各個部分進行抽取公共代碼, 下降耦合度, 提升代碼重用性, 同時提升開發效率.編程

  • 採起橫向抽取, 取代傳統縱向繼承體系重複性代碼緩存

  • 解決事務管理, 性能監視, 安全檢查, 緩存, 日誌等問題安全

  • Spring AOP在運行期, 經過反向代理的方式解決類加載, 屬性注入bash

  • AspectJ是基於Java的AOP框架, 在Spring使用AspectJ實現AOPapp

AOP實現機制:
底層採用代理機制實現AOP.框架

2種代理機制:
2.0、採用JDK的的動態代理Proxy;
2.一、採用CGLIB字節碼加強ide

AOP專業術語:
Target: 目標類 ( 須要被代理的類 )
Joinpoint: 鏈接點 ( 可能須要使用的目標類方法 )
Advice: 加強代碼 ( 對鏈接點加強的代碼 )
PointCut: 切入點 ( 可能須要 Advice 加強的鏈接點 )
Weaving: 織入 ( 建立代理對象 proxy 執行切入點的方法 )
Aspect: 切面 ( Advice 與 PointCust的結合 )函數


JDK/CGLIB的AOP實現

下面經過JDK動態代理和CGLIB字節碼加強兩種方式實現AOP操做:


當目標類沒有實現接口或者須要更好的性能的時候就須要考慮使用CGLIB實現動態Proxy


JDK動態代理:
1.目標類: Service層
2.切面類: 使用JDK動態代理對Service層代碼加強
3.工廠類: 得到proxy對象


目標類:

public interface  UserService {
    void addUser();
    void updateUser();
    void deleteUser();
}
public class UserServiceImpl implements UserService {
    public void addUser() {
        System.out.println("add User");
    }
    public void updateUser() {
        System.out.println("update User");
    }
    public void deleteUser() {
        System.out.println("delete User");
    }
}
複製代碼

切面, 加強鏈接點:

public class MyAspect {
    public void before(){
        System.out.println("before");
    }
    public void after(){
        System.out.println("after");
    }
}
複製代碼

靜態代理對象工廠:

public class MyProxyBeanFactory {
    public static UserService createService(){
        final UserService userService = new UserServiceImpl();
        final MyAspect myAspect = new MyAspect();

        //經過userService得到代理對象
        UserService proxyService = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new InvocationHandler(){

                    //Proxy代理對象, method代理類的目標方法, args目標方法參數
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        //織入橫向代碼
                        myAspect.before();
                        //執行代理類的方法
                        Object obj = method.invoke(userService, args);
                        myAspect.after();
                        //返回執行代理方法的返回值
                        return obj;
                    }

        });
        //返回代理對象
        return proxyService;
    }
}
複製代碼

applicationContext.xml:

<bean id="userService" class="com.f_aop.jdkproxy.MyProxyBeanFactory" factory-method="createService"></bean>
複製代碼

測試方法:

@Test
    public void f1(){
        String XMLPATH="com/f_aop/jdkproxy/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }複製代碼

CGLIB字節碼加強動態代理:
原理: cglib動態生成一個代理類的子類, 子類重寫代理類的全部不是final的方法, 在子類中採用方法攔截技術攔截全部父類的方法調用, 順勢織入切面邏輯, 實現AOP, 它比JDK動態代理要快。但其操做流程與JDK動態代理一致。

下面只給出靜態代理工廠的代碼:

public class MyProxyBeanFactory {

    public static UserService createService(){
        final UserService userService = new UserServiceImpl();
        final MyAspect myAspect = new MyAspect();

        //建立代理
        Enhancer enhancer = new Enhancer();
        //肯定父類
        enhancer.setSuperclass(userService.getClass());
        //向代理對象的方法中織入切面代碼
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method, Object[] args,
                    MethodProxy methodProxy) throws Throwable {

                myAspect.before();
                //執行目標方法
                //Object obj = method.invoke(userService, args);

                //執行目標方法, 效果與method.invoke(userService, args);
                //通常執行這個方法, 速度要快一些
                Object obj = methodProxy.invoke(proxy, args);
                myAspect.after();
                //返回目標方法返回值
                return obj;
            }
        });
        //使用enhancer建立代理對象
        return (UserService) enhancer.create();
    }
}
複製代碼

cglib的整個流程與JDK的動態代理都是同樣的, 就在底層處理接口和加載字節碼文件有區別。

AOP聯盟通知類型

AOP聯盟定義Advice規範, 編寫Advice代碼需實現Advice接口。


Spring按照Advice在目標類中方法的鏈接點的位置, 分爲5類:

前置通知: 
    實現接口: org.springframework.aop.MethodBeforeAdvice
    只在目標方法前進行代碼加強;
後置通知: 
    實現接口: org.springframework.aop.AfterReturningAdvice
    只在目標方法後進行代碼加強;
環繞通知( 必須手動執行目標方法 ): 
    實現接口: org.springframework.aop.MethodInterceptor
    只在目標方法先後進行代碼加強; 效果等同於JDK的Proxy/cglib
異常通知: 
    實現接口: org.springframework.aop.ThrowsAdvice
    在拋出異常的時候進行代碼加強;
引介通知:
    實現接口: org.springframework.aop.IntroductionInterceptor
    只在目標類中增添一些新的方法和屬性;
複製代碼

AOP聯盟的代理實現

  • 使用Spring提供的ProxyFactoryBean模擬代理過程, 實現Spring的AOP:

使用環繞型通知進行演示(目標類與前面的同樣):

1.導入aop, aopalliance jar包
2.切面類(MyAspect)實現MethodInterceptor接口
3.實現MethodInterceptor中invoke方法, 手動織入橫向代碼
4.在applicationContext.xml中配置, 使用Spring提供的ProxyFactoryBean對目標類實現代理
複製代碼

演示代碼:

切面類:

public class MyAspect implements MethodInterceptor{
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {

        System.out.println("前");
        //手動執行目標方法
        Object obj = mi.proceed();
        System.out.println("後");
        //返回目標方法執行的返回值
        return obj;
    }
}
複製代碼

配置applicationContext.xml:

<!-- 得到目標類對象 -->
    <bean id="userService" class="com.f_aop.methodInterceptor.UserServiceImpl"></bean>

    <!-- 建立切面類 -->
    <bean id="myAspect" class="com.f_aop.methodInterceptor.MyAspect"></bean>

    <!-- 建立代理類, 使用Spring配備的代理工廠 -->
    <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 指定接口 -->
        <property name="interfaces" value="com.f_aop.methodInterceptor.UserService"></property>
        <!-- 肯定目標類對象 -->
        <property name="target" ref="userService"></property>
<!-- 肯定Aspect, 因爲interceptorNames的形參值是String[], 因此使用value, 而非ref -->
        <property name="interceptorNames" value="myAspect"></property>
        <property name="optimize" value="true"></property>
    </bean>
複製代碼

測試方法:

@Test
    public void f1(){
        String XMLPATH="com/f_aop/methodInterceptor/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);
        //使用proxyService, 而非userService
        //經過代理對象執行Advice
        UserService userService = (UserService) applicationContext.getBean("proxyService");
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }複製代碼

運行結果:

前
add User
後
前
update User
後
前
delete User
後
複製代碼


applicationContext.xml中建立代理類標籤詳解:

ProxyFactoryBean: Spring的代理工廠,生成代理對象
    interfaces: 目標類實現的接口, 多個值使用<array><value>肯定每一個值
                        單個值的時候直接使用value
    target: 肯定目標類
    interceptorNames: 肯定切面類的名稱, 類型爲String[], 使用value, 切記不使用ref
    optimize: 強制底層使用cglib
        當沒有設置optimize的值時:
            Spring自動判斷, 沒有接口使用cglib, 有接口使用jdk
        顯式設置optimize, 若是聲明optimize=true,不管是否有接口,都採用cglib
複製代碼

上面這種代理實現, 是在applicationContext.xml配置文件中模擬代理工廠產生代理對象, 在測試函數中得到是容器產生的代理對象proxyService.


  • 利用AspectJ簡化Spring中ProxyFactoryBean的配置:

使用環繞型通知進行演示, 編寫流程:

1.導入aspectj.weaver jar包.
2.在applicationContext.xml配置文件中添加aop的xmlns和xsi限制
3.在配置文件中配置切面類(MyAspect)的切入點(PointCut), 特殊切面(包含advice與PointCut).
    首先使用expression表達式配置切入點(PointCut), 即目標類中哪些方法須要加強.
    而後配置特殊切面, 對配置好的切入點, 使用加強點advice進行加強.
複製代碼

下面使用代碼演示, 由於只需修改配置文件與測試類, 只給出配置文件代碼:

<?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 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="userService" class="com.f_aop.aspectJ.UserServiceImpl"></bean>

    <!-- 建立切面類 -->
    <bean id="myAspect" class="com.f_aop.aspectJ.MyAspect"></bean>

    <!-- 配置特殊切面 -->
    <!-- proxy-target-class配置是否使用cglib -->
    <aop:config proxy-target-class="true">
        <aop:pointcut id="myPointCut" expression="execution(* com.f_aop.aspectJ.*.*(..))"/>
        <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"/>
    </aop:config>
</beans>

<!--
    aop:config: 配置AOP
        proxy-target-class: 配置是否強行使用cglib, 效果與前面的optimize同樣
        pointcut: 配置切入點.
            expression:  配置切入點表達式,用於得到目標類中須要加強的目標方法.
        advisor: 配置切入點與切面類, 指明哪些方法須要加強.
            advice-ref: 切入類對象引用.
            pointcut-ref: 切入點引用.
-->
複製代碼

相比於Spring提供的ProxyFactoryBean, AspectJ更加便捷。

AspectJ詳解

AspectJ是基於Java的AOP框架, 用於自定義AOP開發.

  • 切入點表達式
    用於描述目標類中的目標方法, 指定哪些方法可做爲切入點.

下面說明切入點表達式寫法:

語法: expression = " execution( 修飾符 返回值 包.類.方法名(參數) throws 異常 ) "

切入表達式針對每一個部分的編寫規則以下
修飾符(通常省略):
    public  公共方法
    *       任意方法
返回值(不能省略):
    void    沒有返回值
    String  返回值爲字符串
    *       返回值任意
包(可省略):
    com.demo                固定包
    com.demo.*              demo下任意子包,例如:com.demo.aop
    com.demo..              demo下全部包(包含本身,也包含多級子包)
    com.demo.*.service..    demo下任意子包, 子包中包含固定包service,service下全部包
類(可省略):
    UserServiceImpl 指定類
    *Impl           以Impl結尾的類
    User*           以User開頭的類
    *               任意類
方法名(不能省略):
    addUser 指定方法
    add*    以add開頭的方法
    *User   以User結尾的方法
    *       任意方法
參數:
    ()          無參
    (int)       一個int型參數
    (int, int)  兩個int型參數
    (..)        任意參數
throws(可省略, 通常不寫)
複製代碼

下面給出一個例子:

1.execution(* com.demo.*.service..*.*(..))
    指定com.demo下具備固定service包的任意子包中任意類中的任意方法,方法返回值任意.

其餘種類的expression表達式:
1.within: 匹配包或子包中的方法.
    within(com.demo..*) demo下全部包中任意類中任意方法
2.this: 匹配實現接口的類的代理對象中方法:
    this(com.demo.aop.user.UserDAO) 匹配UserDAO中實現類代理對象任意方法.
3.target: 匹配實現接口的類的目標對象中方法:
    target(com.demo.aop.user.UserDAO) 匹配UserDAO中實現類目標對象任意方法.
4.args: 匹配參數格式符合標準的方法
    args(int, int) 匹配形參值類型爲int, int的任意方法.
5.bean(id): 匹配指定bean全部方法
    bean('userService') 匹配userService中全部方法
複製代碼
  • AspectJ通知類型
    與AOP聯盟同樣, AspectJ也定義了多種通知類型.

AspectJ總共6中通知類型:

1.before: 前置通知,用於校驗數據
    在目標方法以前執行, 若拋出異常, 組織目標方法運行.
2.afterReturning: 後置通知,常規數據處理
    目標方法執行後執行, 可得到目標方法的返回值.目標方法出現異常, 方法不執行.
3.around: 環繞通知
    目標方法先後, 可阻止目標方法執行, 必須手動執行目標方法.
4.afterThrowing: 拋出異常通知
    目標方法出現異常後執行, 沒有出現異常就不執行.
5.after: 最終通知, 資源回收, 相似finally方法
    方法執行完, 不管方法中是否出現異常, 都將執行.
複製代碼

環繞通知與其餘通知之間的聯繫:

try{
    //前置: before
    //手動執行目標方法
    //後置: afterReturning
} catch(){
    //捕獲異常: afterThrowing
} finally{
    //最終通知: after
}
複製代碼

從上面看出, 徹底可使用環繞通知模擬前置通知, 後置通知, 環繞通知結合AfterThrowing, After實現AOP.

aop標籤對應的通知類型種類:


使用AOP聯盟進行切面類編寫, 須要定義通知類型, 切面類必須實現特定接口(MethodInterceptor), 而後爲目標方法添加加強代碼, 相比於AOP聯盟, AspectJ只要定義切面類, 加強代碼的使用徹底交給配置文件, 避免代碼污染, 簡化操做。

使用AspectJ實現SpringAOP

基於xml配置通知類型的開發流程:
1.導入AOP聯盟, AspectJ, AOP依賴, Aspect規範 jar包.
2.編寫目標類: 接口與實現類.
3.編寫切面類: 編寫AspectJ的通知類型方法, 方法名任意, 無需實現什麼接口.
4.配置xml: 配置通知類型.
5.測試.


下面給出演示代碼, 代碼中已經給出註釋加以說明(如有不懂請在評論區留言):

目標類 ( 接口與實現類 ):

public interface UserService {
    void addUser();
    void updateUser();
    void deleteUser();
}
public class UserServiceImpl implements UserService {
    public void addUser() {
        System.out.println("add User");
    }
    public void updateUser() {
        System.out.println("update User");
    }
    public void deleteUser() {
        System.out.println("delete User");
    }
}
複製代碼

切面類:

package com.f_aop.aspectJFinal;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspect{

//    測試前置通知與後置通知 
//    public void myBefore(JoinPoint jPoint){
//        System.out.println("前置通知"+jPoint.getSignature().getName());
//    }
//    
//    public void myAfterReturning(JoinPoint jPoint, Object ret){
//        System.out.println("後置通知"+jPoint.getSignature().getName()+"--"+ret);
//    }

    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{

        System.out.println("前置通知");
        //手動執行目標方法
        Object obj = joinPoint.proceed();
//        環繞通知與拋出異常通知的測試結果:       
//        int i = 1/0;
//        前置通知
//        add User
//        拋出異常通知/ by zero
//        最終通知

        System.out.println("後置通知");
        return obj;
    }
    public void myAfterThrowing(JoinPoint jPoint, Throwable e){
        System.out.println("拋出異常通知"+e.getMessage());
    }
    public void myAfter(JoinPoint jPoint){
        System.out.println("最終通知");
    }
}
複製代碼

applicationContext.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 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="userService" class="com.f_aop.aspectJFinal.UserServiceImpl"></bean>

    <!-- 建立切面類 -->
    <bean id="myAspect" class="com.f_aop.aspectJFinal.MyAspect"></bean>

    <!-- 使用 config 配置AspectJ的AOP -->
    <aop:config>
        <!-- 聲明切入面 -->
        <aop:aspect ref="myAspect">
            <!-- 配置目標方法的切入點 -->
            <aop:pointcut id="myPointCut" expression="execution(* com.f_aop.aspectJFinal.UserServiceImpl.*(..))"/>
            <!-- 
            配置通知類型的時候, method表明切入類方法, pointcut-ref表明目標類切入點.
            兩者結合的意思就是目標類中哪些切入點須要切入方法進行加強.
             -->
            <!-- 前置通知 
            <aop:before method="myBefore" pointcut-ref="myPointCut"/>
             後置通知, returning用於接收目標方法執行完後的返回值
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret"/>
            -->

            <!-- 拋出異常通知要配合環繞通知使用, 環繞通知拋出的異常使用拋出異常通知接收 -->
            <aop:around method="myAround" pointcut-ref="myPointCut"/>

            <!-- 拋出異常, throwing="e" 表明執行目標方法後,可能會拋出的異常經過 e 進行接收 -->
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
            <!-- 最終通知 -->
            <aop:after method="myAfter" pointcut-ref="myPointCut"/>
        </aop:aspect>
    </aop:config>
</beans>
複製代碼

測試方法:

@Test
    public void f1(){
        String XMLPATH="com/f_aop/aspectJFinal/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);
        UserService userService = (UserService) applicationContext.getBean("userService");
        //測試AOP
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
複製代碼

基於註解的通知類型開發流程:

1.在剛開始配置註解的時候, 能夠按照 xml 中bean, aop的配置信息來給類/屬性添加註解, 這樣不容易把邏輯搞混.
2.測試, 其實整個開發過程與 xml 配置沒什麼區別, 都是同樣的, 只是形式上有區別。

在給各類類添加註解之間, 必定要牢記:

1.在 xml 配置文件中添加掃描, 掃描註解類:

<context:component-scan base-package="com.demo.aspectJAnnotation"></context:component-scan>
複製代碼

2.肯定AOP註解生效:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
複製代碼

AspectJ中通知類型的註解種類:

1.@Aspect    
    聲明切面類, 不須要指定切面類名稱.
    等同於<aop:aspect ref="myAspect">, 通常與 @Component 結合使用, Component表明myAspect對象

2.@Pointcut("execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))")
    聲明公共切入點, 經過"方法名"得到切入點引用.
    等同於<aop:pointcut id="myPointCut" expression="execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))"/>

2.@Before(value="execution(* com.demo..service.*.*(..))")
    前置通知, 直接添加在切面類方法前.
    等同於<aop:before method="myBefore" pointcut-ref="myPointCut"/>
    或者上面 @Before 也可寫作 @Before(value="myPointCut()") myPointCut是方法名
        此時要先在切面類中聲明公共切入點方法: 
            @Pointcut("execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))")
            private void myPointCut(){}
        這樣寫的做用就是爲了少寫代碼, 避免在多個切面類通知方法前都要加execution=(...).
        而且若是切入點表達式寫錯了, 也很難排查問題.(不懂請看下面的演示代碼)

3.@AfterReturning(value="myPointCut()", returning="ret")
    後置通知, 直接添加在後置通知方法前.
    等同於<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret"/>
    ret表示接收的返回值名稱, 含有與標籤中的ret同樣.

4.@Around("myPointCut()")
    環繞通知, 添加在環繞方法前面.
    等同於<aop:around method="myAround" pointcut-ref="myPointCut"/>

5.@AfterThrowing(value="myPointCut()", throwing="e")
    拋出異常通知, 添加在拋出異常通知方法前.
    等同於<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>

6.@After("myPointCut()")
    最終通知, 添加在最終通知以前.
    等同於<aop:after method="myAfter" pointcut-ref="myPointCut"/>
複製代碼

接下來給出演示代碼:

目標類:

package com.f_aop.aspectJFinalAnnotation;
import org.springframework.stereotype.Service;

//生成UserService的bean: userService
@Service("userService")
public class UserServiceImpl implements UserService {
    public void addUser() {
        System.out.println("add User");
    }
    public void updateUser() {
        System.out.println("update User");
    }
    public void deleteUser() {
        System.out.println("delete User");
    }
}
複製代碼

xml 配置文件 applicationContext.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"
       xmlns:context="http://www.springframework.org/schema/context"
       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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd ">

    <!-- 掃描註解類 -->
    <context:component-scan base-package="com.f_aop.aspectJFinalAnnotation"></context:component-scan>

    <!-- 肯定AOP註解生效 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
複製代碼

切面類:

package com.f_aop.aspectJFinalAnnotation;

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

//得到切面類Bean
@Component
//聲明切面類
@Aspect
//因爲兩者都修飾同一個類, 因此不加id

public class MyAspect{

    //直接設置切入點, 不使用自定義的公共切入點
//    @Before("execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))")  
//    public void myBefore(JoinPoint jPoint){
//        System.out.println("前置通知"+jPoint.getSignature().getName());
//    }

//    設置切入點, 經過returning得到返回值 
//    @AfterReturning(value="myPointCut()", returning="ret) // public void myAfterReturning(JoinPoint jPoint, Object ret){ // System.out.println("後置通知"+jPoint.getSignature().getName()+"--"+ret); // } @Pointcut("execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))") private void myPointCut(){ //配置空方法,用於聲明公共切入點 } @Around("myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前置通知"); //手動執行目標方法 Object obj = joinPoint.proceed(); int i = 1/0; // 前置通知 // add User // 拋出異常通知/ by zero // 最終通知 System.out.println("後置通知"); return obj; } @AfterThrowing(value="myPointCut()", throwing="e") public void myAfterThrowing(JoinPoint jPoint, Throwable e){ System.out.println("拋出異常通知"+e.getMessage()); } @After("myPointCut()") public void myAfter(JoinPoint jPoint){ System.out.println("最終通知"); } } 複製代碼
相關文章
相關標籤/搜索