1. 程式人生 > >Spring 5 - Spring AOP 架構

Spring 5 - Spring AOP 架構

Spring 5 - Spring AOP 架構

概念

  • Joinpoints: 是程式執行中的一個點,比如呼叫一個方法,方法呼叫它自己,類初始化和物件例項的生成等。Joinpoints是AOP的核心概念,在程式裡定義可以插入附加邏輯的點
  • Advice:在特定的joinpoint執行的程式碼就是advice,由你的類的方法定義。有很多型別的advice,比如before,
    它在joinpoint之前執行;比如after,它在joinpoint之後執行
  • Pointcuts:是advice應該被執行時候的joinpoints的集合。通過增加pointcuts,可以更細粒度地控制advice如何
    作用於你程式的元件。一個典型的joinpoint是一個方法呼叫,或者特定類的所有方法呼叫的集合。你可以在複雜的關係中
    合成pointcuts,限制advice的執行
  • Aspects:封裝在一個類中的advice和pointcuts的組合
  • Weaving:把aspects插入程式程式碼的適當位置的過程。對於編譯時AOP,weaving通常在build時候生成;對於執行時AOP,weaving過程在執行時動態執行。AspectJ支援另一個機制-load-time weaving (LTW)-當類被載入的時候,攔截底層的JVM類載入器,把weaving提供給位元組碼
  • Target:被AOP修改的物件。通常,目標物件就是advised物件
  • Introduction:通過附加的方法或者屬性,修改物件的結構的過程。你可以使用introduction AOP,讓任何物件實現一個特定的介面,而不用物件的類實現該介面

Spring AOP基於代理。當你增加一個類的advised的例項的時候,你必須使用ProxyFactory增加類的代理例項,先給它提供所有的構建代理的aspects。當然,一般情況下,你不用直接使用ProxyFactory,而是宣告AOP配置機制(ProxyFactoryBean、aop名稱空間和AspectJ的註解)。為了理解原理,我們先通過程式設計增加代理。
在執行時,Spring分析bean的定義,動態生成代理bean。不直接呼叫目標bean,而是呼叫被注入的代理bean。代理bean分析執行狀況(joinpoint、pointcut或者advice)注入合適的內容。Spring支援兩種代理實現:JDK動態代理和CGLIB代理。預設地,當要被advised的目標物件實現了一個介面,就使用JDK動態代理生成目標的代理例項。否則,如果目標物件沒實現一個介面,就使用CGLIB代理。一個主要原因是,JDK動態代理只能處理介面。

Spring AOP的一個更明顯的簡化是,它只支援一個joinpoint型別:方法呼叫。而AspectJ支援更多的joinpoints。方法呼叫joinpoint是最有用的joinpoint。
Spring AOP的aspect是指實現了Advisor介面的類的例項。Spring提供了方便的Advisor實現,你可以在程式裡重新使用。Advisor有兩個子介面:PointcutAdvisor和IntroductionAdvisor。所有的Advisor實現都實現了PointcutAdvisor,它使用pointcuts控制應用於joinpoints的advice。

Spring的Advice型別

Advice Name Interface Description
Before org.springframework.aop.MethodBeforeAdvice 在joinpoint執行前,執行自定義過程。因為joinpoint總是方法呼叫,實質上允許你在方法執行前執行預處理。Before advice可以完全訪問方法呼叫的目標以及傳給方法的引數,但是無法控制方法本身的執行。如果在advice前拋了異常,攔截器鏈(以及目標方法)的執行被終止,異常被傳播回攔截器鏈
After-Returning org.springframework.aop.AfterReturningAdvice 在joinpoint的方法呼叫執行完成並且已經有返回值以後,執行After-returning advice。它可以訪問方法呼叫的目標,傳給方法的引數和返回值。如果方法拋了異常,就不呼叫advice,異常被傳給呼叫棧。
After(finally) org.springframework.aop.AfterAdvice 方法正常執行完,才呼叫After-returning advice。而after(finally)無論如何都會執行,甚至在方法拋異常的時候也會執行。
Around org.aopalliance.intercept.MethodInterceptor 使用AOP聯盟的方法攔截器標準。在方法呼叫執行前後都可以執行你的advice,你可以控制允許方法呼叫繼續執行的點。你可以選擇旁路方法,提供自己的實現。
Throws org.springframework.aop.ThrowsAdvice 在方法呼叫拋異常後執行。throws advice可以只捕獲特定異常,此時,你可以訪問丟擲異常的方法,傳遞給方法的引數和呼叫的目標。
Introduction org.springframework.aop.IntroductionInterceptor introductions是特殊型別的攔截器。使用這樣的攔截器,可以指定advice介紹的方法的實現。

Advice介面

Interfaces for Spring advice types

關於ProxyFactory類

ProxyFactory控制代理增加過程。它有兩個重要方法,setTarget()方法指定目標物件,addAdvice()方法增加Advice。
在ProxyFactory內部,由DefaultAopProxyFactory的例項(實際使用JdkDynamicAopProxy或者CglibAopProxy)增加相應的代理。
addAdvice()方法把你傳的advice放進DefaultPointcutAdvisor的一個例項,DefaultPointcutAdvisor是PointcutAdvisor的標準實現,預設地,應用於物件的全部方法。

增加 Before Advice

Before advice是Spring支援的最有用的advice型別。它能修改傳給方法的引數,能通過拋異常的辦法阻止方法執行。下來,我們看看簡單的例子,在方法執行前,在控制檯寫一個訊息,其中包含方法的名字。程式碼是SimpleBeforeAdvice類:

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;

import java.lang.reflect.Method;

public class SimpleBeforeAdvice implements MethodBeforeAdvice {
    public static void main(String... args) {
        Guitarist johnMayer = new Guitarist();
        ProxyFactory pf = new ProxyFactory();
        pf.addAdvice(new SimpleBeforeAdvice());
        pf.setTarget(johnMayer);
        Guitarist proxy = (Guitarist) pf.getProxy();
        proxy.sing();
    }

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Before '" + method.getName() + "', tune guitar.");
    }

    private static class Guitarist implements Singer {
        private String lyric = "You're gonna live forever in me";

        @Override
        public void sing(){
            System.out.println(lyric);
        }
    }

    interface Singer {
        void sing();
    }
}

Guitarist類很簡單,只有一個方法-sing()-在控制檯輸出lyric。它實現了Singer介面。
可以看到,SimpleBeforeAdvice實現了MethodBeforeAdvice介面,定義了一個方法before()。我們現在使用的是addAdvice()方法提供的預設pointcut,這樣就匹配類裡的所有方法。before()方法有三個引數:被呼叫的方法,要傳給方法的引數和呼叫的目標物件。SimpleBeforeAdvice類使用before()方法的Method引數,在控制檯寫訊息,訊息包含被呼叫方法的名字。執行一下,輸出是這樣的:

Before 'sing', tune guitar.
You're gonna live forever in me

通過Before Advice,實現安全的方法訪問

我們實現before advice,在允許方法呼叫之前,檢查使用者證書。如果使用者證書無效,通過該advice拋異常,這樣阻止方法的執行。本例有點簡單化,它允許使用者使用任何密碼鑑權,也只允許一個硬編碼的使用者訪問安全的方法。
先看SecureBean類,它會被安全地執行:

class SecureBean {
    void writeSecureMessage() {
        System.out.println("Every time I learn something new, "
                + "it pushes some old stuff out of my brain");
    }
}

因為本示例需要做使用者認證,所以需要有地方儲存他們的細節。我們使用UserInfo儲存使用者證書:

class UserInfo {
    private String userName;
    private String password;

    UserInfo(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }
    public String getPassword() {
        return password;
    }
    public String getUserName() {
        return userName;
    }
}

下來是SecurityManager類,負責認證,並儲存他們的證書,供以後檢索:

class SecurityManager {
    private static ThreadLocal<UserInfo>
            threadLocal = new ThreadLocal<>();

    void login(String userName, String password) {
        threadLocal.set(new UserInfo(userName, password));
    }
    void logout() {
        threadLocal.set(null);
    }
    UserInfo getLoggedOnUser() {
        return threadLocal.get();
    }
}

程式使用SecurityManager類認證使用者,並在之後檢索當前已認證的使用者的細節。使用login()方法做認證,它增加一個UserInfo物件,儲存到ThreadLocal。getLoggedOnUser()方法返回當前認證的使用者的資訊,如果沒有被認證的使用者,就返回null。
要檢查一個使用者是否已經被認證,如果被認證了,就可以訪問SecureBean類的方法。所以,我們增加advice,在方法呼叫前執行,檢查使用者資訊:

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class SecurityAdvice implements MethodBeforeAdvice {
    private SecurityManager securityManager;

    SecurityAdvice() {
        this.securityManager = new SecurityManager();
    }
    
    @Override
    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        UserInfo user = securityManager.getLoggedOnUser();
        if (user == null) {
            System.out.println("No user authenticated");
            throw new SecurityException(
                    "You must login before attempting to invoke the method: "
                            + method.getName());
        } else if ("John".equals(user.getUserName())) {
            System.out.println("Logged in user is John - OKAY!");
        } else {
            System.out.println("Logged in user is " + user.getUserName()
                    + " NOT GOOD :(");
            throw new SecurityException("User " + user.getUserName()
                    + " is not allowed access to method " + method.getName());
        }
    }
}

SecurityAdvice類在它的構造器裡增加了一個SecurityManager的例項。在before()方法裡,我們簡單地檢查,使用者名稱是不是John。如果是,允許使用者訪問;否則,拋異常。
下面的程式碼,做測試:

import org.springframework.aop.framework.ProxyFactory;

public class SecurityDemo {
    public static void main(String... args) {
        SecurityManager mgr = new SecurityManager();
        SecureBean bean = getSecureBean();
        mgr.login("John", "pwd");
        bean.writeSecureMessage();
        mgr.logout();
        try {
            mgr.login("invalid user", "pwd");
            bean.writeSecureMessage();
        } catch (SecurityException ex) {
            System.out.println("Exception Caught: " + ex.getMessage());
        } finally {
            mgr.logout();
        }
        try {
            bean.writeSecureMessage();
        } catch (SecurityException ex) {
            System.out.println("Exception Caught: " + ex.getMessage());
        }
    }

    private static SecureBean getSecureBean() {
        SecureBean target = new SecureBean();
        SecurityAdvice advice = new SecurityAdvice();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvice(advice);
        SecureBean proxy = (SecureBean) factory.getProxy();
        return proxy;
    }
}

我們測試了三個場景,只有John通過了驗證:

Logged in user is John - OKAY!
Every time I learn something new, it pushes some old stuff out of my brain
Logged in user is invalid user NOT GOOD :(
Exception Caught: User invalid user is not allowed access to method writeSecureMessage
No user authenticated
Exception Caught: You must login before attempting to invoke the method: writeSecureMessage

增加After-Returning Advice

After-returning advice在方法呼叫返回後執行。既然方法已經執行了,你不能修改傳給它的引數。雖然你能讀這些引數,你不能修改執行路徑,也不能阻止方法執行。而且,你也不能在after-returning advice內修改返回值,但是,你可以拋異常,異常取代了返回值,被送到呼叫棧。
下面,我們看兩個例子。第一個,在方法被呼叫後,在控制檯輸出訊息。第二個,顯示如何使用after-returning advice,給一個方法增加錯誤檢查。考慮一個類,KeyGenerator,生成用於加密的key。很多加密演算法都會碰到這樣的問題,有些key太弱了(即使不知道key,也容易匯出原始訊息)。第二個例子用來檢查弱key。

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.framework.ProxyFactory;

import java.lang.reflect.Method;

public class SimpleAfterReturningAdvice implements
        AfterReturningAdvice {
    public static void main(String... args) {
        Guitarist target = new Guitarist();
        ProxyFactory pf = new ProxyFactory();
        pf.addAdvice(new SimpleAfterReturningAdvice());
        pf.setTarget(target);
        Guitarist proxy = (Guitarist) pf.getProxy();
        proxy.sing();
    }

    @Override
    public void afterReturning(Object returnValue, Method method,
                               Object[] args, Object target) throws Throwable {
        System.out.println("After '" + method.getName() + "' put down guitar.");
    }

    private static class Guitarist implements SimpleBeforeAdvice.Singer {
        private String lyric = "You're gonna live forever in me";

        @Override
        public void sing() {
            System.out.println(lyric);
        }
    }

    interface Singer {
        void sing();
    }
}

理想情況下,key生成器會檢查弱key,但是,弱key出現機率很小,所以,很多key生成器不做這樣的檢查。我們現在使用after-returning advice做檢查:

class KeyGenerator {
    static final long WEAK_KEY = 0xFFFFFFF0000000L;
    static final long STRONG_KEY = 0xACDF03F590AE56L;

    private Random rand = new Random();

    long getKey() {
        int x = rand.nextInt(3);
        if (x == 1) {
            return WEAK_KEY;
        }
        return STRONG_KEY;
    }
}

不能認為key生成器是安全的。上面程式碼每三次就有一次機會生產弱key。WeakKeyCheckAdvice類檢查getKey()方法返回的是否弱key:

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

import static spring.aop.KeyGenerator.WEAK_KEY;

public class WeakKeyCheckAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method,
                               Object[] args, Object target) throws Throwable {
        if ((target instanceof KeyGenerator)
                && ("getKey".equals(method.getName()))) {
            long key = ((Long) returnValue).longValue();
            if (key == WEAK_KEY) {
                throw new SecurityException(
                        "Key Generator generated a weak key. Try again");
            }
        }
    }
}

下面我們做測試:

public class AfterAdviceDemo {
    private static KeyGenerator getKeyGenerator() {
        KeyGenerator target = new KeyGenerator();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvice(new WeakKeyCheckAdvice());
        return (KeyGenerator)factory.getProxy();
    }

    public static void main(String... args) {
        KeyGenerator keyGen = getKeyGenerator();
        for(int x = 0; x < 10; x++) {
            try {
                long key = keyGen.getKey();
                System.out.println("Key: " + key);
            } catch(SecurityException ex) {
                System.out.println("Weak Key Generated!");
            }
        }
    }
}

增加Around Advice

Around advice像是before和after advice的組合。但是有幾點不同:你可以修改返回值,也能阻止方法的執行。意思是,使用around advice,你可以用新程式碼代替方法。Spring很多地方都使用了around advice,比如遠方代理支援和事務管理。
首先,我們使用Agent類,看怎麼使用基本的方法攔截器在方法呼叫的兩端寫一條訊息。應該注意的是,MethodInterceptor介面的invoke()方法的引數集和MethodBeforeAdvice、AfterReturningAdvice的不一樣。
下面的例子,我們通過advise,得到方法執行時效能的相關資訊。特別是,我們想知道方法執行了多長時間。我們使用了Spring的StopWatch。先看看
WorkerBean,它是被觀察物件:

class WorkerBean {
    void doSomeWork(int noOfTimes) {
        for (int x = 0; x < noOfTimes; x++) {
            work();
        }
    }

    private void work() {
        System.out.print("");
    }
}

ProfilingInterceptor類使用StopWatch類觀察方法呼叫時長:

public class ProfilingInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        StopWatch sw = new StopWatch();
        sw.start(invocation.getMethod().getName());
        Object returnValue = invocation.proceed();
        sw.stop();
        dumpInfo(invocation, sw.getTotalTimeMillis());
        return returnValue;
    }

    private void dumpInfo(MethodInvocation invocation, long ms) {
        Method m = invocation.getMethod();
        Object target = invocation.getThis();
        Object[] args = invocation.getArguments();
        System.out.println("Executed method: " + m.getName());
        System.out.println("On object of type: " +
                target.getClass().getName());
        System.out.println("With arguments:");
        for (int x = 0; x < args.length; x++) {
            System.out.print("       > " + args[x]);
        }
        System.out.print("\n");
        System.out.println("Took: " + ms + " ms");
    }
}

下來是測試程式碼:

public class ProfilingDemo {
    public static void main(String... args) {
        WorkerBean bean = getWorkerBean();
        bean.doSomeWork(10000000);
    }

    private static WorkerBean getWorkerBean() {
        WorkerBean target = new WorkerBean();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget
            
           

相關推薦

Spring 5 - Spring AOP 架構

Spring 5 - Spring AOP 架構 概念 Advice介面 關於ProxyFactory類 增加 Before Advice 通過Before Advice,實現安全的方法訪問 增加After-Returni

Java 22:Spring 5(Spring MVC 入門)

Spring MVC的流程經歷流程: 1、請求帶著使用者請求的資訊,到達DispatcherServlet。 Spring MVC所有的請求都會通過一個前端控制器Servlet。前端控制器是常用的Web應用程式模式。DispatcherServlet的任務是將請求傳送給Sp

[Spring Boot實戰系列] - No.5 Spring boot AOP 示例

Spring boot AOP 示例 在之前的文章中,介紹過Spring 的AOP與AspectJ相關的內容。最近實驗室的一個專案又用到了springboot的AOP,在網上調研了一下發現了幾個配置極其簡單但功能很完善的示例,在這裡總結一下。AOP相關的原理及含義不再解釋,參考之前的

Spring 5 設計模式 - 使用代理和裝飾模式的Spring AOP

Spring 5 設計模式 - 使用代理和裝飾模式的Spring AOP Spring中的代理模式 什麼是AOP AOP要解決的問題 程式碼糾纏 程式碼分散 解決 AOP的核心術語

Spring 5官方文件》37. Spring AOP的經典用法

原文連結 在本附錄中,我們會討論一些初級的Spring AOP介面,以及在Spring 1.2應用中所使用的AOP支援。 對於新的應用,我們推薦使用 Spring AOP 2.0來支援,在AOP章節有介紹。但在已有的專案中,或者閱讀資料或者文章時,可能會遇到Spring AOP 1.2風格的示

[AOP] 5. Spring AOP中提供的種種Aspects

本文繼續討論ConcurrencyThrottleInterceptor(基於Spring 4.3.7)。以及上一篇文章中遺留的一個關於SimpleAsyncTaskExecutor類中屬性concurrencyLimit的問題。 這些都和併發控制相關。但是這

那些年、一起追過的Spring--(5)----AOP

瞭解: (在概念上和原理上充分理解) 參考視訊:http://www.jikexueyuan.com/course/665_3.html?ss=1 1. 什麼是AOP 2. AOP的存在價值 3. AOP的原理剖析 4. AOP的關鍵概念

day39-Spring 08-SpringAOP:基於AspectJ的註解

ima spring mage 開發 技術 asp day3 cnblogs ring 基於AspectJ的註解的開發要重點掌握. day39-Spring 08-Spring的AOP:基於AspectJ的註解

day39-Spring 11-SpringAOP:基於AspectJ的XML配置方式

asp 技術 mage bsp aop src xml配置方式 img aspectj day39-Spring 11-Spring的AOP:基於AspectJ的XML配置方式

5.Spring+Struts+Hibernate配置文件整合

extends nco 方法 系統 ted -a type -i ping 一:配置文件整合SSH 1.創建Hibernate實體類的映射文件,一般在resource下建文件夾下放置 <?xml version="1.0" encoding="UTF-8"?>&

Spring Boot學習——AOP編程的簡單實現

col .com tsig 訪問 pan -s ping 編程範式 lan 首先應該明白一點,AOP是一種編程範式,是一種程序設計思想,與具體的計算機編程語言無關,所以不止是Java,像.Net等其他編程語言也有AOP的實現方式。AOP的思想理念就是將通用邏輯

Spring系列之AOP

-a cte implement 結合 動態擴展 分離 可操作性 技術 其中 一、什麽是AOPAOP(Aspect-OrientedProgramming,面向方面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OO

Spring系列之AOP實現的兩種方式

部分 靜態常量 cep value conf tar import enc ble AOP常用的實現方式有兩種,一種是采用聲明的方式來實現(基於XML),一種是采用註解的方式來實現(基於AspectJ)。 首先復習下AOP中一些比較重要的概念: Joinpoint(連接點)

spring筆記3-AOP

父類 ride 支持 ack beans 方法參數 cep 結構 express 一.概述 AOP:(Aspect Oriented Programming)即:面向切面編程。把我們程序重復的代碼抽取出來,在需要執行的時候,使用動態代理的技術,在不修改源碼的基礎上,對我們

(二)整合spring cloud雲服務架構 - particle雲架構

介紹 能夠 步驟 架構 第一篇 img .net 業務 服務架構 第一篇文章簡單給大家介紹了Spring Cloud架構,我這邊結合了當前大部分企業的通用需求,包括技術的選型比較嚴格、苛刻,不僅要用業界最流行的技術,還要和國際接軌,在未來的5~10年內不能out。作為公司

(一)整合spring cloud雲服務架構 - Spring Cloud簡介

springcloud 架構 雲服務 Spring Cloud是一系列框架的有序集合。利用Spring Boot的開發模式簡化了分布式系統基礎設施的開發,如服務發現、註冊、配置中心、消息總線、負載均衡、斷路器、數據監控等(這裏只簡單的列了一部分),都可以用Spring Boot的開發風格做到一鍵啟動和部署

(三)整合spring cloud雲服務架構 - particle雲架構代碼結構構建

itl log lan 作用 購物 基本架構 集成 eight control 上一篇介紹了spring cloud雲服務架構的基本架構圖,本篇我們根據架構圖進行代碼的構建,根據微服務化設計思想,結合spring cloud本身的服務發現、治理、配置化管理、分布式等項目優秀

Spring cloud 微服務架構 Eureka篇

ring enabled 密碼 config lns 用戶 one ima nap 1 服務發現 ## 關於服務發現 在微服務架構中,服務發現(Service Discovery)是關鍵原則之一。手動配置每個客戶端或某種形式的約定是很難做的,並且很脆弱。Sprin

spring cloud微服務架構 服務提供者和服務消費者

服務 lee 名詞 mave into gin tag bigint snap 服務提供者和服務消費者 下面這張表格,簡單描述了服務提供者/消費者是什麽: | 名詞 | 概念 | | ----- | ---------

spring cloud 微服務架構 簡介

session 進行 tell div apach 後來 tro 最新版 maven Spring Cloud 1、 Spring Cloud 簡介 Spring Cloud是在Spring Boot的基礎上構建的,用於簡化分布式系統構建的工具集,為開發人員提供快