1. 程式人生 > >說說 Spring AOP 中 @Aspect 的高階用法

說說 Spring AOP 中 @Aspect 的高階用法

1 切點複合運算

支援在切點定義中加入以下運算子進行復合運算:

運算子 說明
&& 與運算。
! 非運算。
|| 或運算。

2 切點命名

一般情況下,切點是直接宣告在需要增強方法處,這種切點的宣告方式稱為匿名切點,匿名切點只能在宣告處被使用 。 如果希望在其它地方可以重用這個切點,我們可以通過 @Pointcut 註解及切面類方法來命名它。

public class NamePointcut {

    /**
     * 切點被命名為 method1,且該切點只能在本類中使用
     */
    @Pointcut
("within(net.deniro.spring4.aspectj.*)") private void method1() { } /** * 切點被命名為 method2,且該切點可以在本類或子孫類中使用 */ @Pointcut("within(net.deniro.spring4.aspectj.*)") protected void method2() { } /** * 切點被命名為 method3,且該切點可以在任何類中使用 * 這裡還使用了複合運算 */ @Pointcut
("method1() && method2()") public void method3() { } }

命名切點的結構如下:

切點可訪問性修飾符與類可訪問性修飾符的功能是相同的,它可以決定定義的切點可以在哪些類中可使用。

因為命名切點僅利用方法名及訪問修飾符的資訊,所以我們一般定義方法的返回型別為 void ,並且方法體為空 。

定義好切點後,就可以在切面類中引用啦:

@Aspect
public class NamePointcutAspect {

    @After("NamePointcut.method2()")
    public
void aspectMethod1() { } /** * 這裡使用了複合運算 */ @After("NamePointcut.method2() && NamePointcut.method3()") public void aspectMethod2() { } }

3 織入順序

一個連線點可以同時匹配多個切點,而切點所對應的增強在連線點上織入順序的規則是這樣的:

1.如果在同一個切面類中宣告的增強,則按照增強在切面類中定義的順序進行織入;
2. 如果增強位於不同的切面類中,並且這些切面類都實現了org.springframework.core.Ordered 介面,則由 Ordered 方法的順序號決定(順序號小的先織入);
3. 如果增強位於不同的切面類中,但這些切面類沒有實現org.springframework.core.Ordered 介面,織入的順序是不確定的 。

假設有兩個切面類 A 與 B,它們都實現了 Ordered 介面,A 的順序號為 1,B 的順序號為 2,切面類 A 與 B 都定義了 3 個增強,那麼同時匹配這 6 個增強的織入順序如下圖所示:

4 獲取連線點資訊

4.1 JoinPoint

org.aspectj.lang.JoinPoint 介面表示目標類連線點物件,它定義這些主要方法。

方法 說明
Object[] getArgs() 獲取連線點方法執行時的入參列表。
Signature getSignature() 獲取連線點的方法簽名物件。
Object getTarget() 獲取連線點所在的目標物件。
Object getThis() 獲取代理物件。

4.2 ProceedingJoinPoint

org.aspectj.lang.ProceedingJoinPoint 繼承了 JoinPoint 介面,它新增了兩個方法(它們用於執行連線點方法)。

方法 說明
Object proceed() throws Throwable 通過反射執行目標物件連線點處的方法。
Object proceed(Object[] var1) throws Throwable 使用新的入參(替換掉原來的入參),通過反射執行目標物件連線點處的方法。

4.3 示例

Cook 介面:

public interface Cook {

    /**
     * 製作食品
     */
    void make();

    /**
     * 製作
     *
     * @param name 食品名稱
     */
    void make(String name);
}

CookA 類:

public class CookA implements Cook {
    public void make() {
        System.out.println("製作食品");
    }

    public void make(String name) {
        System.out.println("製作" + name);
    }
}

在切面類中訪問連線點資訊:

@Aspect
public class JoinPointAspect {

    @Around("within(net.deniro.spring4.aspectj.CookA)")
    public void test(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("---------獲取連線點物件【開始】---------");
        System.out.println("引數:" + pjp.getArgs()[0]);
        System.out.println("簽名物件:" + pjp.getTarget().getClass());

        //執行目標物件方法
        pjp.proceed();
        System.out.println("---------獲取連線點物件【結束】---------");

    }
}

Spring bean 配置:

<?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-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--aspectj 驅動器 -->
    <aop:aspectj-autoproxy/>

    <bean id="cookA" class="net.deniro.spring4.aspectj.CookA"/>
    <bean class="net.deniro.spring4.aspectj.JoinPointAspect"/>
</beans>

輸出結果:

———獲取連線點物件【開始】———
引數:壽司
簽名物件:class net.deniro.spring4.aspectj.CookA
製作壽司
———獲取連線點物件【結束】———

5 繫結連線點的方法入參

args()、this()、target()、@args()、@within()、@target() 和 @annotation() 這些切點函式除可以指定類名外,還可以指定引數名,將目標物件連線點上的方法入參繫結到增強的方法中 。 其中 args() 用於繫結連線點方法的入參, @annotation() 用於繫結連線點方法的註解物件,而 @args() 用於繫結連線點方法入參的註解。

CookC 類:

public class CookC implements Cook {
    public void make() {
        System.out.println("製作食品");
    }

    public void make(String name) {
        System.out.println("製作" + name);
    }

    public void make(String name, int num) {
        System.out.println("製作" + name + " " + num + " 個");
    }
}

切面類:

@Aspect
public class ParamsAspect {

    @Before("target(net.deniro.spring4.aspectj.CookC) && args(name,num,..)")
    public void test(String name,int num) {
        System.out.println("----------繫結連線點入參【開始】----------");
        System.out.println("name:" + name);
        System.out.println("num:" + num);
        System.out.println("----------繫結連線點入參【結束】----------");

    }
}
  • 這裡的連線點表示式 args(name,num,..) 會先找到 name 與 num 的型別,從而生成真正的表示式 args(String,int,..)
  • 增強方法可以通過 name 與 num 得到連線點的方法入參。

切點匹配和引數繫結的過程是這樣的:
1. args()會根據引數名稱在增強方法中查到名稱相同的入參並獲得對應引數的型別,這樣就得到了匹配連線點方法的入參型別 。
2. 連線點方法入參型別所在的位置由引數名在 args() 函式中宣告的位置決定 。

上述示例中的匹配過程如下:

Spring 配置:

<!--aspectj 驅動器 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<bean id="cookC" class="net.deniro.spring4.aspectj.CookC"/>
<bean class="net.deniro.spring4.aspectj.ParamsAspect"/>

注意: 這裡必須通過 <aop:aspectj-autoproxy proxy-target-class="true" />來啟用 CGLib 動態代理,這是因為 CookC 的 public void make(String name, int num) 是該類獨有的方法(非介面定義的方法),所以必須使用 CGLib 生成子類的代理方法 。

單元測試:

ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
CookC cookC = (CookC) context.getBean("cookC");
cookC.make("壽司", 100);

輸出結果:

———-繫結連線點入參【開始】———-
name:壽司
num:100
———-繫結連線點入參【結束】———-
製作壽司 100 個

6 繫結代理物件

使用 this()target() 可繫結被代理物件的例項。通過類例項名繫結物件時,依然具有原來連線點匹配的功能,只是類名是由增強方法中的同名入參型別間接決定的。

@Aspect
public class ProxyAspect {

    @Before("this(cook)")
    public void bind(Cook cook) {
        System.out.println("--------繫結代理物件【開始】--------");
        System.out.println(cook.getClass().getName());
        System.out.println("--------繫結代理物件【結束】--------");
    }
}

首先通過 public void bind(Cook cook) 找出 cook 所對應的型別,接著轉換切點表示式為 this(net.deniro.spring4.aspectj.Cook) 。這樣就表示該切點匹配所有代理物件為 Cook 類中的所有方法。

輸出結果:

——–繫結代理物件【開始】——–
net.deniro.spring4.aspectj.CookC$$EnhancerBySpringCGLIB$$217fb793
——–繫結代理物件【結束】——–

target() 繫結與 this() 相似。

7 繫結類註解物件

@within() 和 @target() 函式都可以將目標類的註解物件繫結到增強方法中。

定義一個日誌註解類:

@Retention(RetentionPolicy.RUNTIME)//保留期限
@Target({ElementType.METHOD,ElementType.TYPE})//目標型別
public @interface Log {
    boolean value() default true;//宣告成員變數
}

把該註解類應用於 CookD:

@Log
public class CookD implements Cook {
    public void make() {
        System.out.println("製作糕點");
    }

    public void make(String name) {

    }
}

繫結類註解物件:

@Aspect
public class ClassAnnotationObjectAspect {

    @Before("@within(log)")
    public void bind(Log log){
        System.out.println("----------繫結類註解物件【開始】----------");
        System.out.println(log.getClass().getName());
        System.out.println("----------繫結類註解物件【結束】----------");
    }
}

Spring 配置:

<!--aspectj 驅動器 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>


<bean id="cookD" class="net.deniro.spring4.aspectj.CookD"/>
<bean class="net.deniro.spring4.aspectj.ClassAnnotationObjectAspect"/>

單元測試:

ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
CookD cook = (CookD) context.getBean("cookD");
cook.make();

輸出結果:

———-繫結類註解物件【開始】———-
com.sun.proxy.$Proxy8
———-繫結類註解物件【結束】———-

從輸出結果 com.sun.proxy.$Proxy8 可以看出 ,CookD 類的註解 Log 物件也被代理咯O(∩_∩)O哈哈~

8 繫結返回值

在後置增強中,可以通過 returning 來繫結連線點方法的返回值。

切面:

@Aspect
public class ReturnValueAspect {

    @AfterReturning(value = "target(net.deniro.spring4.aspectj.CookA)", returning = "value")
    public void bind(boolean value) {
        System.out.println("繫結返回值【開始】");
        System.out.println("value:" + value);
        System.out.println("繫結返回值【結束】");
    }
}

**注意:**returning 的值必須與方法引數名相同。

CookA 新增 smell 方法:

public boolean smell(String name) {
    System.out.println(name + "香嗎?");
    return true;
}

單元測試:

ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
CookA cook = (CookA) context.getBean("cookA");
cook.smell("烤鴨");

輸出結果:

烤鴨香嗎?
繫結返回值【開始】
value:true
繫結返回值【結束】

9 繫結異常

可以使用 AfterThrowing 註解的 throwing 成員變數來繫結連線點丟擲的異常。

切面類:

@Aspect
public class ExceptionAspect {

    @AfterThrowing(value = "target(net.deniro.spring4.aspectj.CookA)",throwing = "e")
    public void bind(CookException e){
        System.out.println("繫結異常【開始】");
        System.out.println("e:" + e.getMessage());
        System.out.println("繫結異常【結束】");
    }
}

**注意:**throwing 的值必須與方法引數名相同。

單元測試:

ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
CookA cook = (CookA) context.getBean("cookA");
cook.make("");

輸出結果:

繫結異常【開始】
e:煮啥呢???
繫結異常【結束】

`

相關推薦

說一說 Spring AOP @Aspect高階用法

1 切點複合運算 支援在切點定義中加入以下運算子進行復合運算: 運算子 說明 && 與運算。 ! 非運算。 || 或運算。 2 切點命名 一般情況下,切點是直接宣告在需要增強方法處,這種切點的宣告方式稱為匿名切點,匿名切點只能在宣告處被使用 。

談談Spring AOP@Aspect高階用法示例

前言 本文主要跟大家分享介紹了關於Spring AOP中@Aspect的高階用法,下面話不多說了,來隨著小編一起看看詳細的介紹吧。 1 切點複合運算 支援在切點定義中加入以下運算子進行復合運算: 運算子 說明 && 與運算。 !

說說 Spring AOP @Aspect高階用法

1 切點複合運算 支援在切點定義中加入以下運算子進行復合運算: 運算子 說明 && 與運算。 ! 非運算。 || 或運算。 2 切點命名 一般情況下,切點是直接宣告在需要增強方

Spring AOP高級——源碼實現(2)Spring AOP通知器(Advisor)與切面(Aspect

color oaf 小麻煩 ntc tro sta ins pack package 本文例子完整源碼地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/Spring%20AO

spring---aop(6)---Spring AOPProxyFactoryBean介紹

except trace col fine erl 參數 owa per face 寫在前面   這篇文章裏面就要說說Spring自己的AOP,搞清楚哪種方式是Spring自己實現的AOP,哪種方式是Spring引入aspectj的AOP。 簡單例子   Spring

轉載《Spring AOPpointcut expression表達式解析 及匹配多個條件》

targe 組合 man fin rgs acc ans value account 原文地址:https://www.cnblogs.com/rainy-shurun/p/5195439.html 原文 Pointcut 是指那些方法需要被執行"AOP",是由"Poi

Spring AOP切點表達式用法總結

情況 cut 需要 xml文件 表示 標註 blog 封裝 HR 1. 簡介 面向對象編程,也稱為OOP(即Object Oriented Programming)最大的優點在於能夠將業務模塊進行封裝,從而達到功能復用的目的。通過面向對象編程,不同的模板可以相

4.spring JDBC的基本用法

1.Spring的jdbc的基本用法: 2.spring的ioc配置以及以及增刪改查 3.策略模式 4. JDBCDaoSupport 其中優化部分,抽取重複程式碼段!如下圖方法!原理:因為子類自動會繼承父類中protected和public方法!

轉:Spring AOP的動態代理

原文連結:Spring AOP中的動態代理 0  前言 1  動態代理 1.1 JDK動態代理 1.2 CGLIB動態代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的過濾功能

Spring AOP常用名詞解釋

Spring AOP中常用名詞解釋 1.切面(Aspect): 一個關注點的模組化,這個關注點可能會橫切多個物件。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。 在Spring AOP中,切面可以使用通用類(基於模式的風格) 或者在普通類中以 @Aspect 註解(@Aspec

java基礎(五):spring aop 之 @aspect

Aspectj切入點語法定義 例如定義切入點表示式 execution(* com.sample.service.impl….(…)) 一:execution()是最常用的切點函式,其語法如下所示: pointcut 宣告"切入點", 整個表示式可以分為五個部分

基於代理的spring aop目標物件引入新特性的實現

IntroductionInterceptor也是一個標記介面,其子類中有個便捷的實現類,即DelegatingIntroductionInterceptor. 說明,定義引入的實現類,也需要實現新增特性的介面. 目標物件類 package siye; pub

基於代理的spring aop多種通知實現

不需要引入額外的jar包,只需引入需要模組的spring內部jar包即可. 介面結構 標註的都是標記介面,其中大多數有明確約定的介面實現,只有異常通知介面沒有,但有其預設約定. Advice spring aop通知的頂層標記介面

基於代理的spring aop,切面的實現

基於代理的spring aop中,使用介面Advisor表示切面. 對目標物件織入通知可使用PointAdvisor介面及其子類,定義切面.其子類中有便捷的DefaultPointcutAdvisor實現類可使用. 對目標物件織入引入功能時,可使用Introductio

基於代理的spring aop,使用xml實現通知和引入

ProxyFactoryBean xml配置中,實現代理工廠的類 屬性 定義 target 代理的目標物件 proxyInterfaces 代理需要實現的

Spring AOP的JDK動態代理

一、關於靜態代理和動態代理的概念1 代理模式是常用的Java設計模式,它的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物

Spring MVC Session的用法

Spring MVC是個非常優秀的框架,其優秀之處繼承自Spring本身依賴注入(Dependency Injection)的強大的模組化和可配置性,其設計處處透露著易用性、可複用性與易整合性。優良的設計模式遍及各處,使得其框架雖然學習曲線陡峭,但一旦掌握則欲罷不能。初學者

Spring AOP的前置通知和後置通知詳解

不同版本的spring對AOP的支援有所不同,spring2.0之前,它主要針對不同型別的攔截器使用XML配置檔案通過代理來實現。而spring2.0之後,它可以使用JDK5的註解來完成AOP的實現,

spring Aop切入點和連線點什麼關係?

連線點是個虛的概念,可簡單理解為切入點的集合;他只是對應用程式的所有需要進行插入切面的一個統稱; 每個切入點都對應具體的連線點,在執行期spring就是根據這些切入點的定義,將通知或者攔截器插入到具體的連線點上。

表單高階用法

下拉框: <select id="selectID" > <option value="1">1</option> <option valu