1. 程式人生 > >Spring AOP——在通知(Advice)方法中獲取目標方法的引數

Spring AOP——在通知(Advice)方法中獲取目標方法的引數

獲取目標方法的資訊

    訪問目標方法最簡單的做法是定義增強處理方法時,將第一個引數定義為JoinPoint型別,當該增強處理方法被呼叫時,該JoinPoint引數就代表了織入增強處理的連線點。JoinPoint裡包含了如下幾個常用的方法:

  • Object[] getArgs:返回目標方法的引數

  • Signature getSignature:返回目標方法的簽名

  • Object getTarget:返回被織入增強處理的目標物件

  • Object getThis:返回AOP框架為目標物件生成的代理物件

    注意:當使用@Around處理時,我們需要將第一個引數定義為ProceedingJoinPoint型別,該類是JoinPoint的子類。

    下面的切面類(依然放在com.abc.advice包中)中定義了Before、Around、AfterReturning和After 4中增強處理,並分別在4種增強處理中訪問被織入增強處理的目標方法、目標方法的引數和被織入增強處理的目標物件等:

package com.abc.advice;

import java.util.Arrays;

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

@Aspect
public class AdviceTest {
    @Around("execution(* com.abc.service.*.many*(..))")
    public Object process(ProceedingJoinPoint point) throws Throwable {
        System.out.println("@Around:執行目標方法之前...");
        //訪問目標方法的引數:
        Object[] args = point.getArgs();
        if (args != null && args.length > 0 && args[0].getClass() == String.class) {
            args[0] = "改變後的引數1";
        }
        //用改變後的引數執行目標方法
        Object returnValue = point.proceed(args);
        System.out.println("@Around:執行目標方法之後...");
        System.out.println("@Around:被織入的目標物件為:" + point.getTarget());
        return "原返回值:" + returnValue + ",這是返回結果的字尾";
    }
    
    @Before("execution(* com.abc.service.*.many*(..))")
    public void permissionCheck(JoinPoint point) {
        System.out.println("@Before:模擬許可權檢查...");
        System.out.println("@Before:目標方法為:" + 
                point.getSignature().getDeclaringTypeName() + 
                "." + point.getSignature().getName());
        System.out.println("@Before:引數為:" + Arrays.toString(point.getArgs()));
        System.out.println("@Before:被織入的目標物件為:" + point.getTarget());
    }
    
    @AfterReturning(pointcut="execution(* com.abc.service.*.many*(..))", 
        returning="returnValue")
    public void log(JoinPoint point, Object returnValue) {
        System.out.println("@AfterReturning:模擬日誌記錄功能...");
        System.out.println("@AfterReturning:目標方法為:" + 
                point.getSignature().getDeclaringTypeName() + 
                "." + point.getSignature().getName());
        System.out.println("@AfterReturning:引數為:" + 
                Arrays.toString(point.getArgs()));
        System.out.println("@AfterReturning:返回值為:" + returnValue);
        System.out.println("@AfterReturning:被織入的目標物件為:" + point.getTarget());
        
    }
    
    @After("execution(* com.abc.service.*.many*(..))")
    public void releaseResource(JoinPoint point) {
        System.out.println("@After:模擬釋放資源...");
        System.out.println("@After:目標方法為:" + 
                point.getSignature().getDeclaringTypeName() + 
                "." + point.getSignature().getName());
        System.out.println("@After:引數為:" + Arrays.toString(point.getArgs()));
        System.out.println("@After:被織入的目標物件為:" + point.getTarget());
    }
}
    在AdviceManager類中增加以下內容:

//將被AdviceTest的各種方法匹配
public String manyAdvices(String param1, String param2) {
    System.out.println("方法:manyAdvices");
    return param1 + " 、" + param2;
}
    在com.abc.main.AOPTest中加入方法的呼叫,觸發切點:

String result = manager.manyAdvices("aa", "bb");
System.out.println("Test方法中呼叫切點方法的返回值:" + result);
    下面是執行結果:

@Around:執行目標方法之前...
@Before:模擬許可權檢查...
@Before:目標方法為:com.abc.service.AdviceManager.manyAdvices
@Before:引數為:[改變後的引數1, bb]
@Before:被織入的目標物件為:
[email protected]
方法:manyAdvices @Around:執行目標方法之後... @Around:被織入的目標物件為:[email protected] @After:模擬釋放資源... @After:目標方法為:com.abc.service.AdviceManager.manyAdvices @After:引數為:[改變後的引數1, bb] @After:被織入的目標物件為:[email protected] @AfterReturning:模擬日誌記錄功能... @AfterReturning:目標方法為:com.abc.service.AdviceManager.manyAdvices @AfterReturning:引數為:[改變後的引數1, bb] @AfterReturning:返回值為:原返回值:改變後的引數1 、 bb,這是返回結果的字尾 @AfterReturning:被織入的目標物件為:
[email protected]
Test方法中呼叫切點方法的返回值:原返回值:改變後的引數1 、bb,這是返回結果的字尾

  從結果中可以看出:在任何一個織入的增強處理中,都可以獲取目標方法的資訊。另外,Spring AOP採用和AspectJ一樣的有限順序來織入增強處理:在“進入”連線點時,最高優先順序的增強處理將先被織入(所以給定的兩個Before增強處理中,優先順序高的那個會先執行);在“退出”連線點時,最高優先順序的增強處理會最後被織入(所以給定的兩個After增強處理中,優先順序高的那個會後執行)。當不同的切面中的多個增強處理需要在同一個連線點被織入時,Spring AOP將以隨機的順序來織入這些增強處理。如果應用需要指定不同切面類裡的增強處理的優先順序,Spring提供瞭如下兩種解決方案:

  • 讓切面類實現org.springframework.core.Ordered介面:實現該介面只需要實現一個int getOrder()方法,該方法返回值越小,優先順序越高

  • 直接使用@Order註解來修飾一個切面類:使用這個註解時可以配置一個int型別的value屬性,該屬性值越小,優先順序越高

    優先順序高的切面類裡的增強處理的優先順序總是比優先順序低的切面類中的增強處理的優先順序高。例如:優先順序為1的切面類Bean1包含了@Before,優先順序為2的切面類Bean2包含了@Around,雖然@Around優先順序高於@Before,但由於Bean1的優先順序高於Bean2的優先順序,因此Bean1中的@Before先被織入。

    同一個切面類裡的兩個相同型別的增強處理在同一個連線點被織入時,Spring AOP將以隨機的順序來織入這兩個增強處理,沒有辦法指定它們的織入順序。如果確實需要保證它們以固有的順序被織入,則可以考慮將多個增強處理壓縮為一個增強處理;或者將不同增強處理重構到不同切面中,通過在切面級別上定義順序。

    如果只要訪問目標方法的引數,Spring還提供了一種更加簡潔的方法:我們可以在程式中使用args來繫結目標方法的引數。如果在一個args表示式中指定了一個或多個引數,該切入點將只匹配具有對應形參的方法,且目標方法的引數值將被傳入增強處理方法。下面輔以例子說明:

package com.abc.advice;

import java.util.Date;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AccessArgAdviceTest {
    @AfterReturning(
            pointcut="execution(* com.abc.service.*.access*(..)) && args(time, name)",
            returning="returnValue")
    public void access(Date time, Object returnValue, String name) {
        System.out.println("目標方法中的引數String = " + name);
        System.out.println("目標方法中的引數Date = " + time);
        System.out.println("目標方法的返回結果returnValue = " + returnValue);
    }
}

    上面的程式中,定義pointcut時,表示式中增加了args(time, name)部分,意味著可以在增強處理方法(access方法)中定義time和name兩個屬性——這兩個形參的型別可以隨意指定,但一旦指定了這兩個引數的型別,則這兩個形參型別將用於限制該切入點只匹配第一個引數型別為Date,第二個引數型別為name的方法(方法引數個數和型別若有不同均不匹配)。

    注意,在定義returning的時候,這個值(即上面的returning="returnValue"中的returnValue)作為增強處理方法的形參時,位置可以隨意,即:如果上面access方法的簽名可以為

public void access(Date time, Object returnValue, String name)


也可以為
public void access(Object returnValue, Date time, String name)

還可以為
public void access(Date time, String name, Object returnValue)

    只需要滿足另外的引數名的順序和pointcut中args(param1, param2)的順序相同即可。我們在AdviceManager中定義一個方法,該方法的第一個引數為Date型別,第二個引數為String型別,該方法的執行將觸發上面的access方法,如下:

//將被AccessArgAdviceTest的access方法匹配
public String accessAdvice(Date d, String n) {
    System.out.println("方法:accessAdvice");
    return "aa";
}
    在AOPTest中增加呼叫這個accessAdvice方法並執行,下面是輸出結果:



    從執行結果可以看出,使用args表示式有如下兩個作用:

提供了一種簡單的方式來訪問目標方法的引數

可用於對切入點表示式作額外的限制

    除此之外,使用args表示式時,還可以使用如下形式:args(param1, param2, ..),注意args引數中後面的兩個點,它表示可以匹配更多引數。在例子args(param1, param2, ..)中,表示目標方法只需匹配前面param1和param2的型別即可。



相關推薦

Spring AOP——在通知Advice方法獲取目標方法引數

獲取目標方法的資訊     訪問目標方法最簡單的做法是定義增強處理方法時,將第一個引數定義為JoinPoint型別,當該增強處理方法被呼叫時,該JoinPoint引數就代表了織入增強處理的連線點。JoinPoint裡包含了如下幾個常用的方法: Object[] getAr

SpringAOP——在Advice方法獲取目標方法引數

獲取目標方法的資訊     訪問目標方法最簡單的做法是定義增強處理方法時,將第一個引數定義為JoinPoint型別,當該增強處理方法被呼叫時,該JoinPoint引數就代表了織入增強處理的連線點。JoinPoint裡包含了如下幾個常用的方法: Object[] getArgs:返回目標方法的引數 Sig

Spring通知Advice

obj throws npoi nts src pan joinpoint title 執行  Spring提供了5種Advice類型:   Interception Around:JointPoint前後調用   Before:JointPoint前調用   After

Spring AOP定義切點PointCut通知Advice

本文討論一下Spring AOP程式設計中的兩個關鍵問題,定義切點和定義通知,理解這兩個問題能應付大部分AOP場景。 如果你還不熟悉AOP,請先看AOP基本原理,本文的例子也沿用了AOP基本原理中的例子。 切點表示式 切點的功能是指出切面的通知應該從哪裡織入應用的執行流

spring學習筆記(8)AOP增強advice配置與應用

增強型別 增強(advice)主要包括如下五種型別 1. 前置增強(BeforeAdvice):在目標方法執行前實施增強 2. 後置增強(AfterReturningAdvice):在目標方法執行後實施增強 3. 環繞增強(MrthodIntercept

數學工具scipy的優化方法

rain return ted bounds 使用 slice turn message http 給定一個多維函數,如何求解全局最優? 文章包括: 1.全局最優的求解:暴力方法 2.全局最優的求解:fmin函數 3.凸優化 函數的曲面圖 import numpy as n

jQuery入門-----jQuery的靜態方法

-- 圖片 靜態 str height 事件 個數 另一個 doc jQuery靜態方法 什麽是靜態方法? 靜態方法對應的是對象方法,對象方法用實例對象調用,而靜態方法用類名調用. ready()事件的暫停和恢復   >>暫停或者恢復jQuery.read

spring AOP 概述 Pointcut

分享圖片 圖片 mat 方法名 end 方法 hat color pointcut   Pointcut(切點)決定Advice通知應該作用於哪個連接點,也就是說通過Pointcut來定義需要增強的方法 的集合,這些集合的選取可以按照一定的規則來完成。在這種情況下,Poin

Spring.NET教程容器物件的作用域(基礎篇)

容器中物件的部署分為兩種方式:singleton和非singleton(Java裡叫prototype)。這裡的singleton指的是“單例模式”,就是說當一個物件被定義為singleton時,容器中就只會有一個共享的例項,任何時候通過id或別名請求該物件都會返回這個共享例項的引用(也就是說這個物件只會被建

Java多執行緒java的Sleep方法

點我跳過黑哥的卑鄙廣告行為,進入正文。 Java多執行緒系列更新中~   正式篇: Java多執行緒(一) 什麼是執行緒 Java多執行緒(二)關於多執行緒的CPU密集型和IO密集型這件事 Java多執行緒(三)如何建立執行緒 Java多執行緒(四)java中的Sleep方法  

Java多線程java的Sleep方法

start 線程的生命周期 cnblogs del 廣告 catch 創建 exceptio 分析 點我跳過黑哥的卑鄙廣告行為,進入正文。 Java多線程系列更新中~   正式篇: Java多線程(一) 什麽是線程 Java多線程(二)關於多線程的CPU密集型和IO密

Spring AOP 中篇: AOP切面原理

Spring AOP 中篇: AOP切面原理 該文章參考多篇文章的基礎上進行了簡化並做少許修改,方便理解。原文章地址如下: Spring框架IOC容器和AOP解析 一、Spring: 1. 概念 Spring是一個開源框架,於20

Java8新特性------介面可以定義方法

 Java8比起以前的版本存在很大的變化,我們知道在之前的版本中介面只能是定義抽象的方法,是不能定義實現的,但是在java8環境下,這個不可能已經變得可能。下面我們通過例子一步一步的來講解下java8

Spring-Aop攔截

前言:前面介紹了Spring的核心模組以及相關的依賴注入等概念。這篇講解一下spring的另一個重點,AOP面向切面程式設計。  說道AOP不得不提到幾個概念:  切面:也就是我們自己的一些業務方法。  通知:用於攔截時出發的操作。  切點:具體攔截的某個業務點。  這樣說可

Spring AOP系列—反射

## 前言 前面我們進行了代理模式、靜態代理、動態代理的學習。而動態代理就是利用Java的反射技術(Java Reflection),在執行時建立一個實現某些給定介面的新類(也稱“動態代理類”)及其例項(物件)。所以接下來我們有必要學習一下Java中的反射。 ## 一、基礎知識 ### 1.1 反射是什麼?

AOP的連線點Joinpoint、切點Pointcut、增強Advice、引介Introduction、織入Weaving、切面Aspect

連線點(Joinpoint):程式執行的某個特定位置(如:某個方法呼叫前、呼叫後,方法丟擲異常後)。一個類或一段程式程式碼擁有一些具有邊界性質的特定點,這些程式碼中的特定點就是連線點。Spring僅支援方法的連線點。 切點(Pointcut):如果連線點相當於資料中的記錄,那麼

你如何理解AOP的連線點Joinpoint、切點Pointcut、增強Advice、引介Introduction、織入Weaving、切面Aspect這些概念?

a. 連線點(Joinpoint):程式執行的某個特定位置(如:某個方法呼叫前、呼叫後,方法丟擲異常後)。一個類或一段程式程式碼擁有一些具有邊界性質的特定點,這些程式碼中的特定點就是連線點。Spring僅支援方法的連線點。  b. 切點(Pointcut):如果連線點相當

spring AOP基礎實現AOP兩種方法

1.AOP的作用   在OOP中,正是這種分散在各處且與物件核心功能無關的程式碼(橫切程式碼)的存在,使得模組複用難度增加。AOP則將封裝好的物件剖開,找出其中對多個物件產生影響的公共行為,並將其封裝為一個可重用的模組,這個模組被命名為“切面”(Aspect),切面將那些與

做一個合格的程式猿之淺析Spring AOP原始碼十五 分析JdkDynamicAopProxy的invoke方法

 上一節我們已經分析了Proxyfactorybean如何去生成一個目標物件的代理的,這一節我們將淺析一下基於JDK動態代理的核心回撥方法invoke的原始碼: 首先先開啟JdkDynamicAop

Spring原始碼解析Spring AOP對攔截器呼叫的實現

前面我們分析了Spring AOP實現中得到Proxy物件的過程,下面我們看看在Spring AOP中攔截器鏈是怎樣被呼叫的,也就是Proxy模式是怎樣起作用的,或者說Spring是怎樣為我們提供AOP功能的; 在JdkDynamicAopProxy中生成Proxy物件的時