1. 程式人生 > >Spring -- AOP入門基礎&基於Aspect的AOP通知用法

Spring -- AOP入門基礎&基於Aspect的AOP通知用法

動態代理

我們在日常開發過程中是否會遇到下圖中的這種狀況
這裡寫圖片描述
紅框中的是我們要輸出的日誌,你是否發現,日誌中大部分資訊都是相同的,並且如果我們要修改一個地方,所有的地方都需要改,而且程式碼看起來還比較冗餘

下面我們就可以通過動態代理的方式解決這個問題
看下程式碼

public interface Calculation {

    public int add(int x, int y);

    public int sub(int x, int y);

    public int mul(int x, int y);

    public int dev(int x, int
y); }

定義介面,加減乘除方法。

public class CalculationImpl implements Calculation {

    @Override
    public int add(int x, int y) {
        int result = x + y;
        return result;
    }

    @Override
    public int sub(int x, int y) {
        int result = x - y;
        return result;
    }

    @Override
public int mul(int x, int y) { int result = x * y; return result; } @Override public int dev(int x, int y) { int result = x / y; return result; } }

具體實現類,這裡我們看到,沒有植入日誌。

public class CalculationProxy {

    private Calculation calculation = null
; CalculationProxy(Calculation calculation) { this.calculation = calculation; } public Calculation getCalculationLog() { Calculation proxy = null; ClassLoader loader = calculation.getClass().getClassLoader(); Class[] interfaces = new Class[] { Calculation.class }; InvocationHandler h = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out .println("GP-->invoke begin , execute method is " + method.getName() + ", args is " + Arrays.asList(args)); Object obj = method.invoke(calculation, args); return obj; } }; proxy = (Calculation) Proxy.newProxyInstance(loader, interfaces, h); return proxy; } }

動態代理類的實現,在實現invoke方法的時候,我們輸出日誌在呼叫目標方法之前。

測試

public class Main {
    public static void main(String[] args) {
        Calculation calculation = new CalculationImpl();
        CalculationProxy calculationProxy = new CalculationProxy(calculation);
        Calculation cal = calculationProxy.getCalculationLog();

        System.out.println(cal.add(1, 3));
        System.out.println(cal.mul(1, 5));
    }
}

輸出結果

GP–>invoke begin , execute method is add, args is [1, 3]
4
GP–>invoke begin , execute method is mul, args is [1, 5]
5

看起來是不是要清晰多了,我們將日誌單獨提取到動態代理的方法中,對核心的業務方法沒有侵入,而且還便於我們的維護。

AOP簡介

AOP(Aspect-Oriented Programming, 面向切面程式設計): 是一種新的方法論, 是對傳統 OOP(Object-Oriented Programming, 面向物件程式設計) 的補充.
AOP 的主要程式設計物件是切面(aspect), 而切面模組化橫切關注點.
在應用 AOP 程式設計時, 仍然需要定義公共功能, 但可以明確的定義這個功能在哪裡, 以什麼方式應用, 並且不必修改受影響的類. 這樣一來橫切關注點就被模組化到特殊的物件(切面)裡.
AOP 的好處:

  • 每個事物邏輯位於一個位置, 程式碼不分散, 便於維護和升級
  • 業務模組更簡潔, 只包含核心業務程式碼.

AOP術語

切面(Aspect): 橫切關注點(跨越應用程式多個模組的功能)被模組化的特殊物件
通知(Advice): 切面必須要完成的工作
目標(Target): 被通知的物件
代理(Proxy): 向目標物件應用通知之後建立的物件
連線點(Joinpoint):程式執行的某個特定位置:如類某個方法呼叫前、呼叫後、方法丟擲異常後等。連線點由兩個資訊確定:方法表示的程式執行點;相對點表示的方位。例如 ArithmethicCalculator#add() 方法執行前的連線點,執行點為 ArithmethicCalculator#add(); 方位為該方法執行前的位置
切點(pointcut):每個類都擁有多個連線點:例如 ArithmethicCalculator 的所有方法實際上都是連線點,即連線點是程式類中客觀存在的事務。AOP 通過切點定位到特定的連線點。類比:連線點相當於資料庫中的記錄,切點相當於查詢條件。切點和連線點不是一對一的關係,一個切點匹配多個連線點,切點通過 org.springframework.aop.Pointcut 介面進行描述,它使用類和方法作為連線點的查詢條件。

基於Aspect的AOP – 前置通知

涉及JAR包
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.1.jar
spring-aop-4.1.7.RELEASE.jar
spring-aspects-4.1.7.RELEASE.jar
spring-beans-4.1.7.RELEASE.jar
spring-context-4.1.7.RELEASE.jar
spring-core-4.1.7.RELEASE.jar
spring-expression-4.1.7.RELEASE.jar

看下程式碼

public interface Calculation {

    public int add(int x, int y);

    public int sub(int x, int y);

    public int mul(int x, int y);

    public int dev(int x, int y);
}
@Component
public class CalculationImpl implements Calculation {

    @Override
    public int add(int x, int y) {
        int result = x + y;
        return result;
    }

    @Override
    public int sub(int x, int y) {
        int result = x - y;
        return result;
    }

    @Override
    public int mul(int x, int y) {
        int result = x * y;
        return result;
    }

    @Override
    public int dev(int x, int y) {
        int result = x / y;
        return result;
    }

}

實現介面,並且使用@Component標記為IOC容器中的元件。

    <context:component-scan base-package="com.gp.spring.aop.impl"></context:component-scan>

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

加入掃描註解的包路徑。
Spring預設不支援@AspectJ風格的切面宣告,增加aspectj-autoproxy,這樣Spring就能發現@AspectJ風格的切面並且將切面應用到目標物件。

@Aspect
@Component
public class CalculationAspect {

    @Before("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
    public void beforeMethod(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        List<Object> list = Arrays.asList(joinPoint.getArgs());
        System.out.println("Method begin ... ,method=" + name + ", args = "
                + list);
    }

}
  • CalculationAspect 標記為Component元件,註冊到IOC容器中
  • @Aspect標記,spring對其進行AOP相關的配置,生成相應的代理類
  • @Before,為前置通知,方法執行前的通知
  • “execution(public int com.gp.spring.aop.impl.Calculation.add(int,
    int))”表示式,表示此通知要執行的包路徑、方法的相關資訊。此表示式可進行模糊匹配,比如”execution(public int
    com.gp.spring.aop.impl.Calculation.*(..))”,表示Calculation類下的所有方法。
  • 方法中的引數JoinPoint joinPoint,表示連線點,通過此引數可以獲取到執行方法的相關資訊,比如方法名、方法引數。

執行測試方法

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Calculation calculation = (Calculation)context.getBean("calculationImpl");
        int result = calculation.add(3, 4);
        System.out.println(result);
    }

輸出結果

Method begin … ,method=add, args = [3, 4]
7

基於Aspect的AOP – 後置通知

後置通知與前置通知用法類似,區別就是在執行方法之後執行後置通知方法。

程式碼如下

    @After("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
    public void afterMethod(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        List<Object> list = Arrays.asList(joinPoint.getArgs());
        System.out.println("Method end ... ,method=" + name + ", args = "
                + list);
    }

這裡用的@After註解,其他都沒變化。

測試輸出結果如下

Method begin … ,method=add, args = [3, 4]
executeing …
Method end … ,method=add, args = [3, 4]
7

我在對add方法增加了一個輸出,便於區分前置通知、後置通知

    public int add(int x, int y) {
        int result = x + y;
        System.out.println("executeing ...");
        return result;
    }

基於Aspect的AOP – 返回通知

方法執行成功後,呼叫返回通知,如果方法在執行過程中丟擲異常,則不會呼叫。

程式碼

    @AfterReturning(value = "execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))", returning = "ret")
    public void afterReturnMethod(JoinPoint joinPoint, Object ret) {
        String name = joinPoint.getSignature().getName();
        List<Object> list = Arrays.asList(joinPoint.getArgs());
        System.out.println("@AfterReturning ... ,method=" + name + ", args = "
                + list + ", return = " + ret);
    }

與後置通知不同的是,後置通知在方法丟擲異常,仍會被呼叫,這裡我們可以使用try … catch … 進行類比。

輸出結果

@Before … ,method=add, args = [3, 4]
executeing …
@After … ,method=add, args = [3, 4]
@AfterReturning … ,method=add, args = [1, 3], return = 4
7

基於Aspect的AOP – 異常通知

方法在執行過程中,如果丟擲異常,則呼叫異常通知

程式碼

    @AfterThrowing(value = "execution(public int com.gp.spring.aop.impl.Calculation.*(int, int))", throwing = "ex")
    public void afterThrowMethod(JoinPoint joinPoint, Exception ex) {
        System.out.println("@AfterThrowing ... ,ex = " + ex);
    }

此處與之前的用法有些卻別,在註解的引數中增加了throwing,然後在方法中增加了要捕獲的異常,此處類似於try…catch…的catch中程式碼塊

輸出結果

Exception in thread “main” @AfterThrowing … ,ex = java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at com.gp.spring.aop.impl.CalculationImpl.dev(CalculationImpl.java:29)

基於Aspect的AOP – 環繞通知

環繞通知,類似與我們最開始講解的動態代理,在此通知中你可以去呼叫目標方法,並在目標方法的上下做各種通知(前置、返回、後置、異常等處理)

程式碼:

    @Around("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
    public Object aroundMethod(ProceedingJoinPoint pjd) {
        String name = pjd.getSignature().getName();
        List<Object> list = Arrays.asList(pjd.getArgs());
        Object obj = null;

        System.out.println("前置通知 ... ,method=" + name + ", args = "
                + list);
        try {
            obj = pjd.proceed();
            System.out.println("返回通知 ... ,method=" + name + ", args = "
                    + list);
        } catch (Throwable e) {
            System.out.println("異常通知 ... , exception = " + e);
            e.printStackTrace();
        }
        System.out.println("後置通知 ... ,method=" + name + ", args = "
                + list);
        return obj;
    } 

返回結果:

前置通知 … ,method=add, args = [1, 3]
executeing …
返回通知 … ,method=add, args = [1, 3]
後置通知 … ,method=add, args = [1, 3]
4

基於Aspect的AOP – 切入方法優先順序

下面我們在來增加一個前置通知,建立一個新類

程式碼如下

@Aspect
@Component
public class ValidateAspect {

    @Before("execution(public int com.gp.spring.aop.impl.Calculation.add(int, int))")
    public void validate(){
        System.out.println("驗證方法 com.gp.spring.aop.impl.ValidateAspect");
    }
}

此類我們做了一個驗證。

然後執行測試方法(程式碼複用基於Aspect的AOP – 環繞通知)

前置通知 … ,method=add, args = [1, 3]
驗證方法 com.gp.spring.aop.impl.ValidateAspect
executeing …
返回通知 … ,method=add, args = [1, 3]
後置通知 … ,method=add, args = [1, 3]
@AfterReturning … ,method=add, args = [1, 3], return = 4
4

驗證方法 com.gp.spring.aop.impl.ValidateAspect,輸出了我們新增加的切入方法,那麼這裡的先後順序怎麼制定呢,我們可以使用@order(1)

如下

@Order(1)
@Aspect
@Component
public class ValidateAspect {

}

@Order(1)表示優先執行此切入方法。數值越小,優先順序越高。

基於Aspect的AOP – 重用切點表示式

這裡寫圖片描述
我們之前學習過,定義一個切面方法,要在其註解中,增加切入的目標方法資訊,那如果多個方法都切入同樣的目標方法的時候,我們可不可以將相同的目標方法資訊提取出來呢。

下面我們演示下程式碼(複用之前程式碼)

@Order(2)
@Aspect
@Component
public class CalculationAspect {

    @Pointcut("execution(public int com.gp.spring.aop.impl.Calculation.*(..))")
    public void aspectName(){

    }

    @AfterReturning(value = "aspectName()", returning = "ret")
    public void afterReturnMethod(JoinPoint joinPoint, Object ret) {
        String name = joinPoint.getSignature().getName();
        List<Object> list = Arrays.asList(joinPoint.getArgs());
        System.out.println("@AfterReturning ... ,method=" + name + ", args = "
                + list + ", return = " + ret);
    }

    @Around("aspectName()")
    public Object aroundMethod(ProceedingJoinPoint pjd) {
        String name = pjd.getSignature().getName();
        List<Object> list = Arrays.asList(pjd.getArgs());
        Object obj = null;

        System.out.println("前置通知 ... ,method=" + name + ", args = "
                + list);
        try {
            obj = pjd.proceed();
            System.out.println("返回通知 ... ,method=" + name + ", args = "
                    + list);
        } catch (Throwable e) {
            System.out.println("異常通知 ... , exception = " + e);
            e.printStackTrace();
        }
        System.out.println("後置通知 ... ,method=" + name + ", args = "
                + list);
        return obj;
    } 
}

我們定義了一個aspectName方法,然後增加@Pointcut(“execution(public int com.gp.spring.aop.impl.Calculation.*(..))”)註解。
然後後續,直接呼叫此方法名即可。

如果在其他類中呢,增麼呼叫aspectName方法,程式碼如下

@Order(1)
@Aspect
@Component
public class ValidateAspect {

    @Before("com.gp.spring.aop.impl.CalculationAspect.aspectName()")
    public void validate(){
        System.out.println("驗證方法 com.gp.spring.aop.impl.ValidateAspect");
    }
}

方法所在類的全路徑名即可。

執行結果

驗證方法 com.gp.spring.aop.impl.ValidateAspect
前置通知 … ,method=add, args = [1, 3]
executeing …
返回通知 … ,method=add, args = [1, 3]
後置通知 … ,method=add, args = [1, 3]
@AfterReturning … ,method=add, args = [1, 3], return = 4
4

相關推薦

Spring -- AOP入門基礎&基於Aspect的AOP通知用法

動態代理 我們在日常開發過程中是否會遇到下圖中的這種狀況 紅框中的是我們要輸出的日誌,你是否發現,日誌中大部分資訊都是相同的,並且如果我們要修改一個地方,所有的地方都需要改,而且程式碼看起來還比較冗餘 下面我們就可以通過動態代理的方式解決這個問題 看

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

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

Spring batch 入門基礎

clas 環境 html 日誌 過程 容易 計劃 bre .html Spring Batch是一個輕量級的,完全面向Spring的批處理框架,可以應用於企業級大量的數據處理系統。Spring Batch以POJO和大家熟知的Spring框架為基礎,使開發者更容易的訪問和利

Spring AOP 入門

系統 add method schema main函數 方式 測試 對象 ng- 目錄 AOP概念 AOP核心概念 Spring對AOP的支持 基於Spring的AOP簡單實現 基於Spring的AOP使用其他細節 AOP概念 AOP(Aspect Oriented P

spring AOP切面程式設計——基於自定義註解

AOP稱為面向切面程式設計,在程式開發中主要用來解決一些系統層面上的問題,比如日誌,事務,許可權等待, (1)Aspect(切面):通常是一個類,裡面可以定義切入點和通知 (2)JointPoint(連線點):程式執行過程中明確的點,一般是方法的呼叫 (3)Advice(

Spring AOP切面基礎 實現請求引數的驗證

1.應用的場景 對部分函式的呼叫進行日誌記錄,用於觀察特定問題在執行過程中的函式呼叫情況監控部分重要函式,若丟擲指定的異常,需要以簡訊或郵件方式通知相關人員監控部分重要函式的執行時間,更靈活植入和取消介面報文的引數驗證基本的Demo程式碼 切面類 package co

Spring AOP使用Aspectj基於xml方式,初始化Bean引數

場景: 大多數實體繼承了一個BaseBean,這裡所做的就是使用Spring的Aop功能實現,攔截到的方法,對其引數做一些處理。 spring-xxx.xml的核心配置: <aop:aspectj-autoproxy proxy-ta

Spring原始碼系列(三)--spring-aop基礎元件、架構和使用

# 簡介 前面已經講完 spring-bean( 詳見[Spring](https://www.cnblogs.com/ZhangZiSheng001/category/1776792.html) ),這篇部落格開始攻克 Spring 的另一個重要模組--spring-aop。 spring-aop 可以

[Spring框架]Spring AOP基礎入門總結二:Spring基於AspectJ的AOP的開發.

work 復制代碼 配置文件 exec dao tool ont pda nbsp 前言: 在上一篇中: [Spring框架]Spring AOP基礎入門總結一. 中 我們已經知道了一個Spring AOP程序是如何開發的, 在這裏呢我們將基於AspectJ來進行AOP

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

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

Spring AOP 基於 XML ---- 宣告通知

知識點如下   例項如下 介面類 package cn.com.day02; public interface Calculator { /* * author:命運的信徒 date:2018/12/21 arm:aop基礎 * 這裡是四個方法;返

JavaEE框架——Spring入門基礎(控制反轉Ioc和切面技術Aop

一.簡介: Spring在英語中含義是春天,對於JavaEE開發者來說,Spring框架出現確實帶來了一股全新的春天的氣息。早在2002年,Rod Johson在其編著的《Expert one to one J2EE design anddevelopment》書中,對J

Spring Boot入門AOP基礎及Advice的執行順序

本文主要分為兩個部分,首先介紹AOP的基礎,包括為什麼要使用AOP以及AOP中的基本概念,然後講解AOP中各類Advice的執行順序並給出簡單示例。 一、AOP基礎 1、為什麼要使用AOP AOP(Aspect Oriented Programmi

使用Spring的註解方式實現AOP入門

單元測試 comment cast override src ioc ans 文件 返回 首先在Eclipse中新建一個普通的Java Project,名稱為springAOP。為了使用Spring的註解方式進行面向切面編程,需要在springAOP項目中加入與AOP相關的

Spring Boot入門第二天:一個基於Spring Boot的Web應用,使用了Spring Data JPA和Freemarker。

per pan let mysq 應用 posit ble host thead 今天打算從數據庫中取數據,並展示到視圖中。不多說,先上圖: 第一步:添加依賴。打開pom.xml文件,添加必要的依賴,完整代碼如下: <?xml version="1.0" enco

spring-AOP通知和顧問

多個 targe ges 配置 context color ive 後置 功能 通知和顧問都是切面的實現形式,其中通知可以完成對目標對象方法簡單的織入功能。 而顧問包裝了通知,可以讓我們對通知實現更加精細化的管理,讓我們可以指定具體的切入點。 通知分為前置通知,環繞通知及後

Java動態代理學習【Spring AOP基礎之一】

tor -1 我們 null exception 文件 cat static 一個   Spring AOP使用的其中一個底層技術就是Java的動態代理技術。Java的動態代理技術主要圍繞兩個類進行的    java.lang.reflect.InvocationHan

CgLib動態代理學習【Spring AOP基礎之一】

div 目前 .get 不知道 ctu get() 內容 想要 外部依賴   如果不了解JDK中proxy動態代理機制的可以先查看上篇文章的內容:Java動態代理學習【Spring AOP基礎之一】   由於Java動態代理Proxy.newProxyInstance()的

Spring AOP用法

val around 技術 pac factor 日常 三種方式 viso 可復用 AOP在事務處理、日誌、安全等方面用的很多,在日常軟件定制開發中,用好AOP可以進一步解耦,增強代碼的可復用性。平時用的最多的還是Spring AOP動態代理,其用法如下: 第一種實現

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