Spring實現AOP的4種方式

Spring實現AOP的4種方式

?

? 先了解AOP的相關術語:
1.通知(Advice):
通知定義了切面是什么以及何時使用。描述了切面要完成的工作和何時需要執行這個工作。
2.連接點(Joinpoint):
程序能夠應用通知的一個“時機”,這些“時機”就是連接點,例如方法被調用時、異常被拋出時等等。
3.切入點(Pointcut)
通知定義了切面要發生的“故事”和時間,那么切入點就定義了“故事”發生的地點,例如某個類或方法的名稱,Spring中允許我們方便的用正則表達式來指定
4.切面(Aspect)
通知和切入點共同組成了切面:時間、地點和要發生的“故事”
5.引入(Introduction)
引入允許我們向現有的類添加新的方法和屬性(Spring提供了一個方法注入的功能)
6.目標(Target)
即被通知的對象,如果沒有AOP,那么它的邏輯將要交叉別的事務邏輯,有了AOP之后它可以只關注自己要做的事(AOP讓他做愛做的事)
7.代理(proxy)
應用通知的對象,詳細內容參見設計模式里面的代理模式
8.織入(Weaving)
把切面應用到目標對象來創建新的代理對象的過程,織入一般發生在如下幾個時機:
(1)編譯時:當一個類文件被編譯時進行織入,這需要特殊的編譯器才可以做的到,例如AspectJ的織入編譯器
(2)類加載時:使用特殊的ClassLoader在目標類被加載到程序之前增強類的字節代碼
(3)運行時:切面在運行的某個時刻被織入,SpringAOP就是以這種方式織入切面的,原理應該是使用了JDK的動態代理技術

Spring提供了4種實現AOP的方式:
1.經典的基于代理的AOP
2.@AspectJ注解驅動的切面
3.純POJO切面
4.注入式AspectJ切面

首先看經典的基于代理的AOP:
Spring支持五種類型的通知:
Before(前)? org.apringframework.aop.MethodBeforeAdvice
After-returning(返回后) org.springframework.aop.AfterReturningAdvice
After-throwing(拋出后) org.springframework.aop.ThrowsAdvice
Arround(周圍) org.aopaliance.intercept.MethodInterceptor
Introduction(引入) org.springframework.aop.IntroductionInterceptor

值的說明的是周圍通知,他是由AOP Alliance中的接口定義的而非Spring,周圍通知相當于前通知、返回后通知、拋出后通知的結合(傳說中的完全體?好吧,我看日和看多

了)還有引入通知怎么玩我還沒搞清楚,等心無雜念的時候玩玩

這東西怎么玩?這么幾個步驟:
1.創建通知:實現這幾個接口,把其中的方法實現了
2.定義切點和通知者:在Spring配制文件中配置這些信息
3.使用ProxyFactoryBean來生成代理

具體做法。。。大晚上的就舉個睡覺的例子吧:

首先寫一個接口叫Sleepable,這是一個牛X的接口,所有具有睡覺能力的東西都可以實現該接口(不光生物,包括關機選項里面的休眠)

package test.spring.aop.bean

public interface Sleepable{
?
??? void sleep();?
}

然后寫一個Human類,他實現了這個接口

package test.spring.aop.bean

public Human implements Sleepable{
???
?? /*這人莫非跟寡人差不多?
??? *除了睡覺睡的比較好之外其余的什么也不會做?*/
?? public void sleep(){
????? System.out.println("睡覺了!夢中自有顏如玉!");
?? }

}


好了,這是主角,不過睡覺前后要做些輔助工作的,最基本的是脫穿衣服,失眠的人還要吃安眠藥什么的,但是這些動作與純粹的睡覺這一“業務邏輯”是不相干的,如果把

這些代碼全部加入到sleep方法中,是不是有違單一職責呢?,這時候我們就需要AOP了。

編寫一個SleepHelper類,它里面包含了睡覺的輔助工作,用AOP術語來說它就應該是通知了,我們需要實現上面的接口

package test.spring.aop.bean;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

public class SleepHelper implements MethodBeforeAdvice,AfterReturningAdvice{

??? public void before(Method mtd, Object[] arg1, Object arg2)
??? ??? ??? throws Throwable {
??? ??? System.out.println("通常情況下睡覺之前要脫衣服!");
??? }

??? public void afterReturning(Object arg0, Method arg1, Object[] arg2,
??? ??? ??? Object arg3) throws Throwable {
??? ??? System.out.println("起床后要先穿衣服!");
??? }
????
}

然后在spring配置文件中進行配置:
<bean id="sleepHelper" class="test.spring.aop.bean.SleepHelper">
</bean>

OK!現在創建通知的工作就完成了.

第二步是進行配置,這是很令人蛋疼的操作,尤其是這么熱的天,Spring又把東西的名字起的見鬼的長!它為啥不能像usr這種風格呢?

首先要做的是配置一個切點,據說切點的表示方式在Spring中有好幾種,但是常用的只有兩種:1.使用正則表達式 2.使用AspectJ表達式 AspectJ我不是很熟悉(我也是熟悉

黨 or 精通黨?),我還是習慣用正則表達式

Spring使用org.springframework.aop.support.JdkRegexpMethodPointcut來定義正則表達式切點
<bean id="spleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
? <property name="pattern" value=".*sleep"/>
</bean>

pattern屬性指定了正則表達式,它匹配所有的sleep方法

切點僅僅是定義了故事發生的地點,還有故事發生的時間以及最重要的故事的內容,就是通知了,我們需要把通知跟切點結合起來,我們要使用的通知者是:
org.springframework.aop.support.DefaultPointcutAdvisor

<bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
???? <property name="advice" ref="sleepHelper"/>
???? <property name="pointcut" ref="sleepPointcut"/>
</bean>

切入點和通知都配置完成,接下來該調用ProxyFactoryBean產生代理對象了

<bean id="humanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
???? <property name="target" ref="human"/>
???? <property name="interceptorNames" value="sleepHelperAdvisor" />
???? <property name="proxyInterfaces" value="test.spring.aop.bean.Sleepable" />
</bean>

ProxyFactoryBean是一個代理,我們可以把它轉換為proxyInterfaces中指定的實現該interface的代理對象:

import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import test.spring.aop.bean.Sleepable;


public class Test {

??? public static void main(String[] args){
??? ??? ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");
??? ??? Sleepable sleeper = (Sleepable)appCtx.getBean("humanProxy");
??? ??? sleeper.sleep();
??? }
}

程序運行產生結果:
通常情況下睡覺之前要脫衣服!
睡覺啦~夢中自有顏如玉!
起床后要先穿衣服!

OK!這是我們想要的結果,但是上面這個過程貌似有點復雜,尤其是配置切點跟通知,Spring提供了一種自動代理的功能,能讓切點跟通知自動進行匹配,修改配置文件如下:
?<bean id="sleepHelper" class="test.spring.aop.bean.SleepHelper">
? </bean>
? <bean id="sleepAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
??? <property name="advice" ref="sleepHelper"/>
??? <property name="pattern" value=".*sleep"/>
? </bean>
? <bean id="human" class="test.spring.aop.bean.Human">
? </bean>
? <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

執行程序:
public class Test {

??? public static void main(String[] args){
??? ??? ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");
??? ??? Sleepable sleeper = (Sleepable)appCtx.getBean("human");
??? ??? sleeper.sleep();
??? }
}
成功輸出結果跟前面一樣!
只要我們聲明了org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator(我勒個去的,名太長了)就能為方法匹配的bean自動創建代理!

但是這樣還是要有很多工作要做,有更簡單的方式嗎?有!

一種方式是使用AspectJ提供的注解:

package test.mine.spring.bean;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SleepHelper {

??? public SleepHelper(){
??? ????
??? }
????
??? @Pointcut("execution(* *.sleep())")
??? public void sleeppoint(){}
????
??? @Before("sleeppoint()")
??? public void beforeSleep(){
??? ??? System.out.println("睡覺前要脫衣服!");
??? }
????
??? @AfterReturning("sleeppoint()")
??? public void afterSleep(){
??? ??? System.out.println("睡醒了要穿衣服!");
??? }
????
}

用@Aspect的注解來標識切面,注意不要把它漏了,否則Spring創建代理的時候會找不到它,@Pointcut注解指定了切點,@Before和@AfterReturning指定了運行時的通知,注

意的是要在注解中傳入切點的名稱

然后我們在Spring配置文件上下點功夫,首先是增加AOP的XML命名空間和聲明相關schema
命名空間:
xmlns:aop="http://www.springframework.org/schema/aop"
schema聲明:
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd

然后加上這個標簽:
<aop:aspectj-autoproxy/> 有了這個Spring就能夠自動掃描被@Aspect標注的切面了

最后是運行,很簡單方便了:
public class Test {

??? public static void main(String[] args){
??? ??? ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");
??? ??? Sleepable human = (Sleepable)appCtx.getBean("human");
??? ??? human.sleep();
??? }
}

下面我們來看最后一種常用的實現AOP的方式:使用Spring來定義純粹的POJO切面

前面我們用到了<aop:aspectj-autoproxy/>標簽,Spring在aop的命名空間里面還提供了其他的配置元素:
<aop:advisor> 定義一個AOP通知者
<aop:after> 后通知
<aop:after-returning> 返回后通知
<aop:after-throwing> 拋出后通知
<aop:around> 周圍通知
<aop:aspect>定義一個切面
<aop:before>前通知
<aop:config>頂級配置元素,類似于<beans>這種東西
<aop:pointcut>定義一個切點

我們用AOP標簽來實現睡覺這個過程:
代碼不變,只是修改配置文件,加入AOP配置即可:
<aop:config>
??? <aop:aspect ref="sleepHelper">
??? <aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))"/>
??? <aop:after method="afterSleep" pointcut="execution(* *.sleep(..))"/>
??? </aop:aspect>
</aop:config>

完!

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

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

相關文章

如何使用Plotly在Python中為任何DataFrame繪制地圖的衛星視圖

Chart-Studio和Mapbox簡介 (Introduction to Chart-Studio and Mapbox) Folium and Geemap are arguably the best GIS libraries/tools to plot satellite-view maps or any other kinds out there, but at times they require an additional authorization to use the Google…

Java入門系列-26-JDBC

認識 JDBC JDBC (Java DataBase Connectivity) 是 Java 數據庫連接技術的簡稱&#xff0c;用于連接常用數據庫。 Sun 公司提供了 JDBC API &#xff0c;供程序員調用接口和類&#xff0c;集成在 java.sql 和 javax.sql 包中。 Sun 公司還提供了 DriverManager 類用來管理各種不…

3.19PMP試題每日一題

在房屋建造過程中&#xff0c;應該先完成衛生管道工程&#xff0c;才能進行電氣工程施工&#xff0c;這是一個&#xff1a;A、強制性依賴關系B、選擇性依賴關系C、外部依賴關系D、內部依賴關系 作者&#xff1a;Tracy19890201&#xff08;同微信號&#xff09;轉載于:https://…

Can't find temporary directory:internal error

今天我機子上的SVN突然沒有辦法進行代碼提交了&#xff0c;出現的錯誤提示信息為&#xff1a; Error&#xff1a;Cant find temporary directory:internal error 然后試了下其他的SVN源&#xff0c;發現均無法提交&#xff0c;并且update時也出現上面的錯誤信息。對比項目文件…

snowflake 數據庫_Snowflake數據分析教程

snowflake 數據庫目錄 (Table of Contents) Introduction 介紹 Creating a Snowflake Datasource 創建雪花數據源 Querying Your Datasource 查詢數據源 Analyzing Your Data and Adding Visualizations 分析數據并添加可視化 Using Drilldowns on Your Visualizations 在可視化…

jeesite緩存問題

jeesite&#xff0c;其框架主要為&#xff1a; 后端 核心框架&#xff1a;Spring Framework 4.0 安全框架&#xff1a;Apache Shiro 1.2 視圖框架&#xff1a;Spring MVC 4.0 服務端驗證&#xff1a;Hibernate Validator 5.1 布局框架&#xff1a;SiteMesh 2.4 工作流引擎…

高級Python:定義類時要應用的9種最佳做法

重點 (Top highlight)At its core, Python is an object-oriented programming (OOP) language. Being an OOP language, Python handles data and functionalities by supporting various features centered around objects. For instance, data structures are all objects, …

Java 注解 攔截器

場景描述&#xff1a;現在需要對部分Controller或者Controller里面的服務方法進行權限攔截。如果存在我們自定義的注解&#xff0c;通過自定義注解提取所需的權限值&#xff0c;然后對比session中的權限判斷當前用戶是否具有對該控制器或控制器方法的訪問權限。如果沒有相關權限…

醫療大數據處理流程_我們需要數據來大規模改善醫療流程

醫療大數據處理流程Note: the fictitious examples and diagrams are for illustrative purposes ONLY. They are mainly simplifications of real phenomena. Please consult with your physician if you have any questions.注意&#xff1a;虛擬示例和圖表僅用于說明目的。 …

What's the difference between markForCheck() and detectChanges()

https://stackoverflow.com/questions/41364386/whats-the-difference-between-markforcheck-and-detectchanges轉載于:https://www.cnblogs.com/chen8840/p/10573295.html

ASP.NET Core中使用GraphQL - 第七章 Mutation

ASP.NET Core中使用GraphQL - 目錄 ASP.NET Core中使用GraphQL - 第一章 Hello WorldASP.NET Core中使用GraphQL - 第二章 中間件ASP.NET Core中使用GraphQL - 第三章 依賴注入ASP.NET Core中使用GraphQL - 第四章 GrahpiQLASP.NET Core中使用GraphQL - 第五章 字段, 參數, 變量…

POM.xml紅叉解決方法

方法/步驟 1用Eclipse創建一個maven工程&#xff0c;網上有很多資料&#xff0c;這里不再啰嗦。 2右鍵maven工程&#xff0c;進行更新 3在彈出的對話框中勾選強制更新&#xff0c;如圖所示 4稍等片刻&#xff0c;pom.xml的紅叉消失了。。。

JS前臺頁面驗證文本框非空

效果圖&#xff1a; 代碼&#xff1a; 源代碼&#xff1a; <script type"text/javascript"> function check(){ var xm document.getElementById("xm").value; if(xm null || xm ){ alert("用戶名不能為空"); return false; } return …

python對象引用計數器_在Python中借助計數器對象對項目進行計數

python對象引用計數器前提 (The Premise) When we deal with data containers, such as tuples and lists, in Python we often need to count particular elements. One common way to do this is to use the count() function — you specify the element you want to count …

套接字設置為(非)阻塞模式

當socket 進行TCP 連接的時候&#xff08;也就是調用connect 時&#xff09;&#xff0c;一旦網絡不通&#xff0c;或者是ip 地址無效&#xff0c;就可能使整個線程阻塞。一般為30 秒&#xff08;我測的是20 秒&#xff09;。如果設置為非阻塞模式&#xff0c;能很好的解決這個…

經典問題之「分支預測」

問題 來源 &#xff1a;stackoverflow 為什么下面代碼排序后累加比不排序快&#xff1f; public static void main(String[] args) {// Generate dataint arraySize 32768;int data[] new int[arraySize];Random rnd new Random(0);for (int c 0; c < arraySize; c)data…

vi

vi filename :打開或新建文件&#xff0c;并將光標置于第一行首 vi n filename &#xff1a;打開文件&#xff0c;并將光標置于第n行首 vi filename &#xff1a;打開文件&#xff0c;并將光標置于最后一行首 vi /pattern filename&#xff1a;打開文件&#xff0c;并將光標置…

數字圖像處理 python_5使用Python處理數字的高級操作

數字圖像處理 pythonNumbers are everywhere in our daily life — there are phone numbers, dates of birth, ages, and other various identifiers (driver’s license and social security numbers, for example).電話號碼在我們的日常生活中無處不在-電話號碼&#xff0c;…

05精益敏捷項目管理——超越Scrum

00.我們不是不知道它會給我們帶來麻煩&#xff0c;只是沒想到麻煩會有這么多。——威爾.羅杰斯 01.知識點&#xff1a; a.Scrum是一個強大、特意設計的輕量級框架&#xff0c;器特性就是將軟件開發中在制品的數量限制在團隊層級&#xff0c;使團隊有能力與業務落班一起有效地開…

帶標題的圖片輪詢展示

為什么80%的碼農都做不了架構師&#xff1f;>>> <div> <table width"671" cellpadding"0" cellspacing"0"> <tr height"5"> <td style"back…