5.4 Spring AOP

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png


5.4.1 ?從代理機制初探AOP

? ??? ??來看一個簡單的例子,當需要在執行某些方法時留下日志信息,可能會這樣寫:

import?java.util.logging.*;
public?class?HelloSpeaker{
pirvate?Logger?logger=Logger.getLogger(this.getClass().getName());public?void?hello(String?name){logger.log(Level.INFO,?"hello?method?starts…");//?方法開始執行時留下日志Sytem.out.println("hello,?"+name);?//?程序的主要功能Logger.log(Level.INFO,?"hello?method?ends…");//?方法執行完畢時留下日志}
}

? ??? ??在HelloSpeaker類中,當執行hello()方法時,程序員希望該方法執行開始與執行完畢時都留下日志。最簡單的做法是用上面的程序設計,在方法執行的前后加上日志動作。


? ??? ??可以使用代理(Proxy)機制來解決這個問題,有兩種代理方式:靜態代理(static proxy)動態代理(dynamic proxy)

? ??? ??在靜態代理的實現中,代理類與被代理的類必須實現同一個接口。在代理類中可以實現記錄等相關服務,并在需要的時候再呼叫被代理類。這樣被代理類就可以僅僅保留業務相關的職責了。

? ??? ??舉個簡單的例子,首先定義一個IHello接口,IHello.java代碼如下:

public?interface?IHello{public?void?hello(String?name);
}

? ? ? ? 然后讓實現業務邏輯的HelloSpeaker類實現IHello接口,HelloSpeaker.java代碼如下:

public?class?HelloSpeaker?implements?IHello{public?void?hello(String?name){System.out.println("hello,"+name);}
}

? ??? ??可以看到,在HelloSpeaker類中沒有任何日志的代碼插入其中,日志服務的實現將被放到代理類中,代理類同樣要實現IHello接口

HelloProxy.java代碼如下:

public?class?HelloProxy?implements?IHello{private?Logger?logger=Logger.getLogger(this.getClass().getName());private?IHello?helloObject;public?HelloProxy(IHello?helloObject){this.helloObject=helloObject;}public?void?hello(String?name){log("hello?method?starts…");//?日志服務helloObject.hello(name);//?執行業務邏輯log("hello?method?ends…");//?日志服務}private?void?log(String?msg){logger.log(Level.INFO,msg);}
}

? ??? ??在HelloProxy類的hello()方法中,真正實現業務邏輯前后安排記錄服務,可以實際撰寫一個測試程序來看看如何使用代理類。

public?class?ProxyDemo{public?static?void?main(String[]?args){IHello?proxy=new?HelloProxy(new?HelloSpeaker());proxy.hello("Justin");}
}

? ??? ??程序運行結果:

hello,Justin

5.4.2 ?動態代理

? ??? ??要實現動態代理,同樣需要定義所要代理的接口。

IHello.java代碼如下:

public?interface?IHello{public?void?hello(String?name);
}

? ? ? ? 然后讓實現業務邏輯的HelloSpeaker類實現IHello接口。

HelloSpeaker.java代碼如下:

public?class?HelloSpeaker?implements?IHello{public?void?hello(String?name){System.out.println("Hello,"+name);}
}

? ??? ??與上例不同的是,這里要實現不同的代理類

import?java.lang.reflect.InvocationHandler;
import?java.lang.reflect.Method;
public?class?LogHandler?implements?InvocationHandler{private?Object?sub;public?LogHandler()?{}public?LogHandler(Object?obj){sub?=?obj;}public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)throws?Throwable{System.out.println("before?you?do?thing");method.invoke(sub,?args);System.out.println("after?you?do?thing");return?null;}
}

? ??? ??寫一個測試程序,使用LogHandler來綁定被代理類

ProxyDemo.java代碼如下:

import?java.lang.reflect.Proxy;
public?class?ProxyDemo?{public?static?void?main(String[]?args)?{HelloSpeaker?helloSpeaker=new?HelloSpeaker();LogHandler?logHandler=new?LogHandler(helloSpeaker);Class?cls=helloSpeaker.getClass();IHello?iHello=(IHello)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),logHandler);iHello.hello("Justin");}
}

? ??? ??程序運行結果:

before?you?do?thing
Hello,?Justin
after?you?do?thing


5.4.3 ?AOP術語與概念

1.cross-cutting concerns

? ??? ??在DynamicProxyDemo例子中,記錄的動作原先被橫切(Cross-cutting)到HelloSpeaker本身所負責的業務流程中。類似于日志這類的動作,如安全檢查、事務等服務,在一個應用程序中常被安排到各個類的處理流程之中。這些動作在AOP的術語中稱為cross-cutting concerns。如圖5.7所示,原來的業務流程是很單純的。?

?

153307_Uc7K_220508.png


圖5.7? 原來的業務流程

? ??? ??為了加入日志與安全檢查等服務,類的程序代碼中被硬生生地寫入了相關的Logging、Security程序片段,如圖5.8所示。?

?153308_0Xp8_220508.png

圖5.8? 加入各種服務的業務流程

2.Aspect

? ??? ??將散落在各個業務類中的cross-cutting concerns收集起來,設計各個獨立可重用的類,這種類稱為Aspect。例如,在動態代理中將日志的動作設計為LogHandler類,LogHandler類在AOP術語中就是Aspect的一個具體實例。在需要該服務的時候,縫合到應用程序中;不需要服務的時候,也可以馬上從應用程序中脫離。應用程序中的可重用組件不用做任何的修改。例如,在動態代理中的HelloSpeaker所代表的角色就是應用程序中可重用的組件,在它需要日志服務時并不用修改本身的程序代碼。


5.4.4 ?通知Advice

? ??? ??Spring提供了5種通知(Advice)類型:Interception Around、Before、After Returning、Throw 和Introduction。它們分別在以下情況被調用:

  • Interception Around Advice:在目標對象的方法執行前后被調用。

  • Before Advice:在目標對象的方法執行前被調用。

  • After Returning Advice:在目標對象的方法執行后被調用。

  • Throw Advice:在目標對象的方法拋出異常時被調用。

  • Introduction Advice:一種特殊類型的攔截通知,只有在目標對象的方法調用完畢后執行

創建一個Before Advice的Web項目,步驟如下。

① 創建一個Web項目,命名為“Spring_Advices”。

② 編寫Java類。

? ??? ??Before Advice會在目標對象的方法執行之前被呼叫。這個接口提供了獲取目標方法、參數及目標對象。

MethodBeforeAdvice接口的代碼如下:

import?java.lang.ref.*;
import?java.lang.reflect.Method;
public?interface?MethodBeforeAdvice{void?before(Method?method,?Object[]?args,?Object?target)?throws?Exception;
}

? ??? ??用實例來示范如何使用Before Advice。首先要定義目標對象必須實現的接口IHello。

IHello.java代碼如下:

public?interface?IHello{public?void?hello(String?name);
}

? ??? ??接著定義一個HelloSpeaker,實現IHello接口。

HelloSpeaker.java代碼如下:

public?class?HelloSpeaker?implements?IHello{public?void?hello(String?name){System.out.println("Hello,"+name);}
}

? ??? ??在對HelloSpeader不進行任何修改的情況下,想要在hello()方法執行之前可以記錄一些信息。有一個組件,但沒有源代碼,可對它增加一些日志的服務。

LogBeforeAdvice.java代碼如下:

import?java.lang.reflect.*;
import?java.util.logging.Level;
import?java.util.logging.Logger;
import?org.springframework.aop.MethodBeforeAdvice;
public?class?LogBeforeAdvice?implements?MethodBeforeAdvice{private?Logger?logger=Logger.getLogger(this.getClass().getName());public?void?before(Method?method,Object[]?args,Object?target)?throws?Exception{logger.log(Level.INFO,?"method?starts…"+method);}
}

③ 添加Spring開發能力。

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"
xsi:schemaLocation="http://www.springframework.org/schema/beans?
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"><bean?id="logBeforeAdvice"?class="LogBeforeAdvice"?/><bean?id="helloSpeaker"?class="HelloSpeaker"?/><bean?id="helloProxy"class="org.springframework.aop.framework.ProxyFactoryBean"><property?name="proxyInterfaces"><value>IHello</value></property><property?name="target"><ref?bean="helloSpeaker"?/></property><property?name="interceptorNames"><list><value>logBeforeAdvice</value></list></property></bean>
</beans>

④ 運行程序,測試結果。

寫一個程序測試一下Before Advice的運作。

SpringAOPDemo.java代碼如下:

import?org.springframework.context.ApplicationContext;
import?org.springframework.context.support.FileSystemXmlApplicationContext;
public?class?SpringAOPDemo{public?static?void?main(String[]?args){ApplicationContext?context=new?FileSystemXmlApplicationContext("/WebRoot/WEB-INF/classes/applicationContext.xml");IHello?helloProxy=(IHello)context.getBean("helloProxy");helloProxy.hello("Justin");}
}

程序運行結果:

Hello,Justin


HelloSpeaker與LogBeforeAdvice是兩個獨立的類。對于HelloSpeaker來說,它不用知道LogBeforeAdvice的存在;而LogBeforeAdvice也可以運行到其他類之上。HelloSpeaker與LogBeforeAdvice都可以重復使用。


5.4.5 ?切入點Pointcut

創建一個切入點Pointcut項目,步驟如下。

① 創建一個Web項目,命名為“Spring_Pointcut”。

② 編寫Java類。

IHello.java代碼如下:

public?interface?IHello{public?void?helloNewbie(String?name);public?void?helloMaster(String?name);
}

HelloSpeaker類實現IHello接口。HelloSpeaker.java代碼如下:

public?class?HelloSpeaker?implements?IHello{public?void?helloNewbie(String?name){System.out.println("Hello,?"+name+"newbie!?");}public?void?helloMaster(String?name){System.out.println("Hello,?"+name+"master!?");}
}

③ 添加Spring開發能力。

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"
xsi:schemaLocation="http://www.springframework.org/schema/beans?
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"><bean?id="logBeforeAdvice"?class="LogBeforeAdvice"?/><bean?id="helloAdvisor"class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"><property?name="mappedName"><value>hello*</value></property><property?name="advice"><ref?bean="logBeforeAdvice"?/></property></bean><bean?id="helloSpeaker"?class="HelloSpeaker"?/><bean?id="helloProxy"class="org.springframework.aop.framework.ProxyFactoryBean"><property?name="proxyInterfaces"><value>IHello</value></property><property?name="target"><ref?bean="helloSpeaker"?/></property><property?name="interceptorNames"><list><value>helloAdvisor</value></list></property></bean>
</beans>



④ 運行程序,測試結果。

SpringAOPDemo.java代碼如下:

import?org.springframework.context.ApplicationContext;
import?org.springframework.context.support.FileSystemXmlApplicationContext;
public?class?SpringAOPDemo?{public?static?void?main(String[]?args)?{ApplicationContext?context?=?new?FileSystemXmlApplicationContext("/WebRoot/WEB-INF/classes/applicationContext.xml");IHello?helloProxy?=?(IHello)?context.getBean("helloProxy");helloProxy.helloNewbie("Justin");helloProxy.helloMaster("Tom");}
}

程序運行結果:

Hello,?Justinnewbie!?
Hello,?Tommaster!


附:目錄《JavaEE基礎實用教程》筆記說明

轉載于:https://my.oschina.net/jerrypan/blog/631893

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/285508.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/285508.shtml
英文地址,請注明出處:http://en.pswp.cn/news/285508.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

WPF 實現心電圖曲線繪制

本文經原作者授權以原創方式二次分享&#xff0c;歡迎轉載、分享。原文作者&#xff1a;流浪g原文地址&#xff1a;https://www.cnblogs.com/cong2312/p/16411637.html一、前言項目中之前涉及到胎兒心率圖曲線的繪制&#xff0c;最近項目中還需要添加心電曲線和血樣曲線的繪制功…

C語言試題110之利用條件運算符的嵌套來完成此題:學習成績大于等于90分的同學用 A 表示,60到89 分之間的用 B 表示, 60 分以下的用 C 表示。

?作者簡介:大家好我是碼莎拉蒂,CSDN博客專家?????? ??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款模擬面試、刷題神器?? 點擊跳轉進入網站 1、題目 題目:C語言試題110之利用條件運算符的嵌套來完成此題:學習成績大于等于90分的同學用 A 表…

【NOIp 2015】【DFS】斗地主

題面 自己網上去搜吧… 代碼 #include <cstdio> #include <cstring> #include <algorithm> #define INF 10000000 #define maxn 40 using namespace std;int t,n,temp,a,zhang[maxn],ansINF;void dfs(int,int,int,int); void shunzi(int,int,int,int,int);vo…

[轉]從入門到精通,Java學習路線導航

引言 最近也有很多人來向我"請教"&#xff0c;他們大都是一些剛入門的新手&#xff0c;還不了解這個行業&#xff0c;也不知道從何學起&#xff0c;開始的時候非常迷茫&#xff0c;實在是每天回復很多人也很麻煩&#xff0c;所以在這里統一作個回復吧。 Java學習路線…

如何讓 Dapper 支持 DateOnly 類型

前言在上次的文章中&#xff0c;我們讓 EF Core 6 支持了 DateOnly 類型。那么&#xff0c;Dapper 是否支持 DateOnly 類型呢&#xff1f;public class User {public int Id { get; set; }public string Name { get; set; }public DateOnly Birthday { get; set; } }using (var…

HP proliant服務器從usb啟動

1&#xff0c;開機出現自檢畫面開始按F9進入設置&#xff0c;進入BIOS 選擇standard boot order&#xff08;rpl&#xff09;&#xff0c;把usb driver放在第一位&#xff0c;保存好 2&#xff0c;按F1開始啟動。 &#xff08;注&#xff1a;我使用ubuntu14.04&#xff0c;到啟…

VB常用內部函數大全一覽表(建議收藏)

VB提供了大量的內部函數供用戶在編程時調用。內部函數按其功能分為數學運算函數、字符串函數、轉換函數、日期與時間函數、判斷函數和格式輸出函數等。 文章目錄 算術函數字符串函數日期和時間函數數據類型轉換函數算術函數 字符串函數 日期和時間函數

數據庫分類介紹

在當今的互聯網中&#xff0c;最常見的數據庫模型主要是兩種&#xff0c;即“關系型數據庫”和“非關系型數據庫”。 一、關系型數據庫 1、關系型數據庫的由來 雖然網狀數據庫和層次數據庫已經很好的解決了數據的集中和共享問題&#xff0c;但是在數據庫獨立性和抽象級別上扔有…

BZOJ 1717 [Usaco2006 Dec]Milk Patterns 產奶的模式(后綴數組)

【題目鏈接】http://www.lydsy.com/JudgeOnline/problem.php?id1717 【題目大意】 求一個最長的串&#xff0c;使得其在母串中出現的次數達到要求 【題解】 二分答案&#xff0c;利用后綴數組求出的height數組進行檢驗 【代碼】 #include <cstdio> #include <cstring…

記一次 .NET 某物管后臺服務 卡死分析

一&#xff1a;背景 1. 講故事這幾個月經常被朋友問&#xff0c;為什么不更新這個系列了&#xff0c;哈哈&#xff0c;確實停了好久&#xff0c;主要還是打基礎去了&#xff0c;分析 dump 的能力不在于會靈活使用 windbg&#xff0c;而是對底層知識有一個深厚的理解&#xff0c…

【C#程序設計】教學講義——第三章:C#語言基礎

完整C#教學課件系列: 【C#程序設計】教學講義——第一章:C#語言概述 【C#程序設計】教學講義——第二章:簡單C#程序設計 【C#程序設計】教學講義——第三章:C#語言基礎 文章目錄 3.1 C#程序結構3.2 變量和常量3.3 常用數據類型3.4 運算符和表達式3.1 C#程序結構 3.1.1 組成…

直接在script里面換樣式IE6,7,8不兼容

1 <!DOCTYPE HTML>2 <html>3 <head>4 <meta http-equiv"Content-Type" content"text/html; charsetutf-8">5 <title>無標題文檔</title>6 </head>7 8 <body>9 10 <input id"inp1" type&quo…

C語言試題111之 s=a+aa+aaa+aaaa+aa...a 的值,其中 a 是一個數字。例如 2+22+222+2222+22222(此時 共有 5 個數相加),幾個數相加有鍵盤控制。

?作者簡介:大家好我是碼莎拉蒂,CSDN博客專家?????? ??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款模擬面試、刷題神器?? 點擊跳轉進入網站 1、題目 題目: s=a+aa+aaa+aaaa+aa…a 的值,其中 a 是一個數字。例如 2+22+222+2222+22222(此時 共…

Redis常用配置參數詳解及查看修改命令

目錄 Redis常用配置參數 Redis配置參數查看命令 語法 舉例 說明&#xff1a; Redis配置參數修改命令 語法 舉例 說明&#xff1a; Redis常用配置參數 序號配置項說明1daemonize noRedis 默認不是以守護進程的方式運行&#xff0c;可以通過該配置項修改&#xff0c;使…

反射封裝工具類-----零SQL插入

V_1.0 需求&#xff1a;開發一個工具方法&#xff0c;輔助初級程序員在不需要掌握sql命令和JDBC的情況下&#xff0c;實現對數據庫的插入操作。 V_4.0 實現0sql插入操作需要解決的問題. 1. 如何確認當前【陌生對象】關聯的【表名】 2. 如何確認當前表中需要添加數據的字段 3. …

MathType插入帶序號公式的兩種方法

方法一&#xff1a; 由于我之前使用表格15% 70% 15%來布局的&#xff0c;所以最開始相的就是如何錄入公示后插入公式序號&#xff0c;如下圖所示 先設置序號格式 錄好公式后點“Insert Number”就好了&#xff0c;這樣的話需要緊挨著公式&#xff0c;用空格把他空到最右側就好了…

數據結構算法:基于C#語言用圖實現最短路徑,太妙了!

文章目錄 構造類并實現最短路徑方法設計界面編寫程序測試新的Graph類構造類并實現最短路徑方法 在前面的C#編程中,我們已經完成了諸如遍歷、最小生成樹等許多方法,這個類已經可以完成諸如鄰接矩陣輸入、頂點矩陣輸入問題。這個類在Graph2.cs中。 現在,我們新建立一個WINDOW…

【系統設計】鄰近服務

在本文中&#xff0c;我們將設計一個鄰近服務&#xff0c;用來發現用戶附近的地方&#xff0c;比如餐館&#xff0c;酒店&#xff0c;商場等。設計要求 從一個小明去面試的故事開始。面試官&#xff1a;你好&#xff0c;我想考察一下你的設計能力&#xff0c;如果讓你設計一個…

[轉]Redis持久化存儲(AOF與RDB兩種模式)

Redis中數據存儲模式有2種&#xff1a;cache-only,persistence; cache-only即只做為“緩存”服務&#xff0c;不持久數據&#xff0c;數據在服務終止后將消失&#xff0c;此模式下也將不存在“數據恢復”的手段&#xff0c;是一種安全性低/效率高/容易擴展的方式&#xff1b;pe…

C語言試題112之一個數如果恰好等于它的因子之和,這個數就稱為“完數”。例如 6=1+2+3.編程 找出 1000 以內的所有完數。

?作者簡介:大家好我是碼莎拉蒂,CSDN博客專家?????? ??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款模擬面試、刷題神器?? 點擊跳轉進入網站 1、題目 題目:一個數如果恰好等于它的因子之和,這個數就稱為“完數”。例如 6=1+2+3.編程 找出 …