Spring中的通知(Advice)和顧問(Advisor)
在Spring中,目前我學(xué)習(xí)了幾種增強(qiáng)的方式,和大家分享一下
之前的話:
1.AOP (Aspect Oriented Programming 面向切面編程)
在軟件業(yè),AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預(yù)編譯方式和運(yùn)行期動態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是OOP的延續(xù),是軟件開發(fā)中的一個(gè)熱點(diǎn),也是Spring框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生范型。利用AOP可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)的效率。
面向?qū)ο缶幊淌菑摹眷o態(tài)角度】考慮程序的結(jié)構(gòu),而面向切面編程是從【動態(tài)角度】考慮程序運(yùn)行過程。
AOP底層,就是采用【動態(tài)代理】模式實(shí)現(xiàn)的。采用了兩種代理:JDK動態(tài)代理和CGLIB動態(tài)代理。
基本術(shù)語(一些名詞):
(1)切面(Aspect)
切面泛指[交叉業(yè)務(wù)邏輯]。事務(wù)處理和日志處理可以理解為切面。常用的切面有通知(Advice)與顧問(Advisor)。實(shí)際就是對主業(yè)務(wù)邏輯的一種增強(qiáng)。
(2)織入(Weaving)
織入是指將切面代碼插入到目標(biāo)對象的過程。代理的invoke方法完成的工作,可以稱為織入。
(3) 連接點(diǎn)(JoinPoint)
連接點(diǎn)是指可以被切面織入的方法。通常業(yè)務(wù)接口的方法均為連接點(diǎn)
(4)切入點(diǎn)(PointCut)
切入點(diǎn)指切面具體織入的方法
注意:被標(biāo)記為final的方法是不能作為連接點(diǎn)與切入點(diǎn)的。因?yàn)樽罱K的是不能被修改的,不能被增強(qiáng)的。
(5)目標(biāo)對象(Target)
目標(biāo)對象指將要被增強(qiáng)的對象。即包含主業(yè)務(wù)邏輯的類的對象。
(6)通知(Advice)
通知是切面的一種實(shí)現(xiàn),可以完成簡單的織入功能。通知定義了增強(qiáng)代碼切入到目標(biāo)代碼的時(shí)間點(diǎn),是目標(biāo)方法執(zhí)行之前執(zhí)行,還是執(zhí)行之后執(zhí)行等。切入點(diǎn)定義切入的位置,通知定義切入的時(shí)間。
(7)顧問(Advisor)
顧問是切面的另一種實(shí)現(xiàn),能夠?qū)⑼ㄖ愿鼮閺?fù)雜的方式織入到目標(biāo)對象中,是將通知包裝為更復(fù)雜切面的裝配器。
AOP是一種思想,而非實(shí)現(xiàn)
AOP是基于OOP,而又遠(yuǎn)遠(yuǎn)高于OOP,主要是將主要核心業(yè)務(wù)和交叉業(yè)務(wù)分離,交叉業(yè)務(wù)就是切面。例如,記錄日志和開啟事務(wù)。
一:前置增強(qiáng)和后置增強(qiáng)

源碼介紹:
1.User.java

View Code
2.IDao.java

View Code
3.UserDao.java

View Code
4.IUserBiz.java

View Code
5.UserBiz.java

View Code
6.LoggerAfter.java(后置增強(qiáng))

View Code
7.LoggerBefore.java(前置增強(qiáng))

View Code
8.applicationContext.xml(Spring配置文件)

View Code
當(dāng)然,針對AOP的配置也可以使用代理對象 ProxyFactoryBean 代理工廠bean來實(shí)現(xiàn),在測試類中:IUserBiz biz=(IUserBiz)ctx.getBean("serviceProxy");

View Code
9.MyTest.java

View Code
10.log4j.properties(日志的配置文件)

View Code
當(dāng)然,別忘了引入我們需要的jar包啊!
常用的jar:

二:異常拋出增強(qiáng)和環(huán)繞增強(qiáng)

源碼介紹:
1.User.java

View Code
2.UserService.java

View Code
3.ErrorLog.java(異常拋出增強(qiáng))

View Code
4.AroundLog(環(huán)繞增強(qiáng))

View Code
5.applicationContext.xml(Spring配置文件)

View Code
6.MyTest.java

View Code
三:注解增強(qiáng)方式實(shí)現(xiàn)前置增強(qiáng)和后置增強(qiáng)

源碼介紹:
1.UserService.java

View Code
2.AnnotationAdvice.java(注解增強(qiáng))

View Code
注:
java.lang.Object[] getArgs():獲取連接點(diǎn)方法運(yùn)行時(shí)的入?yún)⒘斜?br>
Signature getSignature() :獲取連接點(diǎn)的方法簽名對象
java.lang.Object getTarget() :獲取連接點(diǎn)所在的目標(biāo)對象
java.lang.Object getThis() :獲取代理對象本身
3.applicationContext.xml(Spring配置文件)

View Code
4.MyTest.java

View Code
四: 顧問(Advisor)實(shí)現(xiàn)前置增強(qiáng)
通知Advice是Spring提供的一種切面(Aspect)。但其功能過于簡單,只能
將切面織入到目標(biāo)類的所有目標(biāo)方法中,無法完成將切面織入到指定目標(biāo)方法中。
顧問Advisor是Spring提供的另一種切面。其可以完成更為復(fù)雜的切面織入功能。PointcutAdvisor是顧問的一種,可以指定具體
的切入點(diǎn)。顧問將通知進(jìn)行了包裝,會根據(jù)不同的通知類型,在不同的時(shí)間點(diǎn),將切面織入到不同的切入點(diǎn)。
PointcutAdvisor接口有兩個(gè)較為常用的實(shí)現(xiàn)類:
:NameMatchMethodPointcutAdvisor 名稱匹配方法切入點(diǎn)顧問
:RegexpMethodPointcutAdvisor 正則表達(dá)式匹配方法切入點(diǎn)顧問
<property name="pattern" value=".do."></property> 表示方法全名(包名,接口名,方法名)
運(yùn)算符 名稱 意義
. 點(diǎn)號 表示任意單個(gè)字符
- 加號 表示前一個(gè)字符出現(xiàn)一次或者多次
- 星號 表示前一個(gè)字符出現(xiàn)0次或者多次
=====默認(rèn)Advisor自動代理生成器
DefaultAdvisorAutoProxyCreator
=====BeanName自動代理生成器
BeanNameAutoProxyCreator
實(shí)例:

源碼介紹:
1.ISomeService.java


](javascript:void(0); "復(fù)制代碼")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package service; //接口
public interface ISomeService { //待實(shí)現(xiàn)的方法
public void doFirst(); public void doSecond();
}</pre>

](javascript:void(0); "復(fù)制代碼")
2.SomeServiceImpl.java

View Code
3.MyMethodBeforeAdvice.java


](javascript:void(0); "復(fù)制代碼")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package aop; //前置增強(qiáng)
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
@Override public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("目標(biāo)方法執(zhí)行之前執(zhí)行");
}
}</pre>
[
](javascript:void(0); "復(fù)制代碼")
4.applicationContext.xml(Spring配置文件)


](javascript:void(0); "復(fù)制代碼")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 目標(biāo)對象 -->
<bean id="someService" class="service.SomeServiceImpl"></bean>
<!-- 切面:通知 -->
<bean id="beforeAdvice" class="aop.MyMethodBeforeAdvice"></bean>
<!-- *********************************************** -->
<!-- 1.*:NameMatchMethodPointcutAdvisor 名稱匹配方法切入點(diǎn)顧問 -->
<!-- 切面:顧問 顧問(Advisor)要包裝通知(Advice) -->
<bean id="beforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<!--指定需要增強(qiáng)的方法:這里是doFirst()方法,而doSecond()方法則不會增強(qiáng) -->
<property name="mappedName" value="doFirst"></property>
<!-- 也可以使用mappedNames指定多個(gè)方法
<property name="mappedNames" value="doFirst,doSecond"></property> -->
</bean>
<!-- *********************************************** -->
<!-- 2.*:RegexpMethodPointcutAdvisor 正則表達(dá)式匹配方法切入點(diǎn)顧問 -->
<!-- 切面: 顧問 顧問(Advisor)要包裝通知(Advice) -->
<!-- <bean id="beforeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<property name="pattern" value=".*doF.*t"></property>
</bean> -->
<!-- *********************************************** -->
<!-- 代理工廠Bean -->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="someService"></property>
<property name="interceptorNames" value="beforeAdvisor"></property>
</bean>
</beans></pre>
[
](javascript:void(0); "復(fù)制代碼")
5.MyTest.java


](javascript:void(0); "復(fù)制代碼")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package test; //對顧問(Advisor)測試
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.ISomeService; public class MyTest {
@Test public void testOne(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService)ctx.getBean("serviceProxy");
service.doFirst();
service.doSecond();
}
}</pre>
[

Spring中的通知(Advice)和顧問(Advisor)
在Spring中,目前我學(xué)習(xí)了幾種增強(qiáng)的方式,和大家分享一下
之前的話:
1.AOP (Aspect Oriented Programming 面向切面編程)
在軟件業(yè),AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預(yù)編譯方式和運(yùn)行期動態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是OOP的延續(xù),是軟件開發(fā)中的一個(gè)熱點(diǎn),也是Spring框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生范型。利用AOP可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)的效率。
面向?qū)ο缶幊淌菑摹眷o態(tài)角度】考慮程序的結(jié)構(gòu),而面向切面編程是從【動態(tài)角度】考慮程序運(yùn)行過程。
AOP底層,就是采用【動態(tài)代理】模式實(shí)現(xiàn)的。采用了兩種代理:JDK動態(tài)代理和CGLIB動態(tài)代理。
基本術(shù)語(一些名詞):
(1)切面(Aspect)
切面泛指[交叉業(yè)務(wù)邏輯]。事務(wù)處理和日志處理可以理解為切面。常用的切面有通知(Advice)與顧問(Advisor)。實(shí)際就是對主業(yè)務(wù)邏輯的一種增強(qiáng)。
(2)織入(Weaving)
織入是指將切面代碼插入到目標(biāo)對象的過程。代理的invoke方法完成的工作,可以稱為織入。
(3) 連接點(diǎn)(JoinPoint)
連接點(diǎn)是指可以被切面織入的方法。通常業(yè)務(wù)接口的方法均為連接點(diǎn)
(4)切入點(diǎn)(PointCut)
切入點(diǎn)指切面具體織入的方法
注意:被標(biāo)記為final的方法是不能作為連接點(diǎn)與切入點(diǎn)的。因?yàn)樽罱K的是不能被修改的,不能被增強(qiáng)的。
(5)目標(biāo)對象(Target)
目標(biāo)對象指將要被增強(qiáng)的對象。即包含主業(yè)務(wù)邏輯的類的對象。
(6)通知(Advice)
通知是切面的一種實(shí)現(xiàn),可以完成簡單的織入功能。通知定義了增強(qiáng)代碼切入到目標(biāo)代碼的時(shí)間點(diǎn),是目標(biāo)方法執(zhí)行之前執(zhí)行,還是執(zhí)行之后執(zhí)行等。切入點(diǎn)定義切入的位置,通知定義切入的時(shí)間。
(7)顧問(Advisor)
顧問是切面的另一種實(shí)現(xiàn),能夠?qū)⑼ㄖ愿鼮閺?fù)雜的方式織入到目標(biāo)對象中,是將通知包裝為更復(fù)雜切面的裝配器。
AOP是一種思想,而非實(shí)現(xiàn)
AOP是基于OOP,而又遠(yuǎn)遠(yuǎn)高于OOP,主要是將主要核心業(yè)務(wù)和交叉業(yè)務(wù)分離,交叉業(yè)務(wù)就是切面。例如,記錄日志和開啟事務(wù)。
一:前置增強(qiáng)和后置增強(qiáng)

源碼介紹:
1.User.java

View Code
2.IDao.java

View Code
3.UserDao.java

View Code
4.IUserBiz.java

View Code
5.UserBiz.java

View Code
6.LoggerAfter.java(后置增強(qiáng))

View Code
7.LoggerBefore.java(前置增強(qiáng))

View Code
8.applicationContext.xml(Spring配置文件)

View Code
當(dāng)然,針對AOP的配置也可以使用代理對象 ProxyFactoryBean 代理工廠bean來實(shí)現(xiàn),在測試類中:IUserBiz biz=(IUserBiz)ctx.getBean("serviceProxy");

View Code
9.MyTest.java

View Code
10.log4j.properties(日志的配置文件)

View Code
當(dāng)然,別忘了引入我們需要的jar包啊!
常用的jar:

二:異常拋出增強(qiáng)和環(huán)繞增強(qiáng)

源碼介紹:
1.User.java

View Code
2.UserService.java

View Code
3.ErrorLog.java(異常拋出增強(qiáng))

View Code
4.AroundLog(環(huán)繞增強(qiáng))

View Code
5.applicationContext.xml(Spring配置文件)

View Code
6.MyTest.java

View Code
三:注解增強(qiáng)方式實(shí)現(xiàn)前置增強(qiáng)和后置增強(qiáng)

源碼介紹:
1.UserService.java

View Code
2.AnnotationAdvice.java(注解增強(qiáng))

View Code
注:
java.lang.Object[] getArgs():獲取連接點(diǎn)方法運(yùn)行時(shí)的入?yún)⒘斜?br>
Signature getSignature() :獲取連接點(diǎn)的方法簽名對象
java.lang.Object getTarget() :獲取連接點(diǎn)所在的目標(biāo)對象
java.lang.Object getThis() :獲取代理對象本身
3.applicationContext.xml(Spring配置文件)

View Code
4.MyTest.java

View Code
四: 顧問(Advisor)實(shí)現(xiàn)前置增強(qiáng)
通知Advice是Spring提供的一種切面(Aspect)。但其功能過于簡單,只能
將切面織入到目標(biāo)類的所有目標(biāo)方法中,無法完成將切面織入到指定目標(biāo)方法中。
顧問Advisor是Spring提供的另一種切面。其可以完成更為復(fù)雜的切面織入功能。PointcutAdvisor是顧問的一種,可以指定具體
的切入點(diǎn)。顧問將通知進(jìn)行了包裝,會根據(jù)不同的通知類型,在不同的時(shí)間點(diǎn),將切面織入到不同的切入點(diǎn)。
PointcutAdvisor接口有兩個(gè)較為常用的實(shí)現(xiàn)類:
:NameMatchMethodPointcutAdvisor 名稱匹配方法切入點(diǎn)顧問
:RegexpMethodPointcutAdvisor 正則表達(dá)式匹配方法切入點(diǎn)顧問
<property name="pattern" value=".do."></property> 表示方法全名(包名,接口名,方法名)
運(yùn)算符 名稱 意義
. 點(diǎn)號 表示任意單個(gè)字符
- 加號 表示前一個(gè)字符出現(xiàn)一次或者多次
- 星號 表示前一個(gè)字符出現(xiàn)0次或者多次
=====默認(rèn)Advisor自動代理生成器
DefaultAdvisorAutoProxyCreator
=====BeanName自動代理生成器
BeanNameAutoProxyCreator
實(shí)例:

源碼介紹:
1.ISomeService.java


](javascript:void(0); "復(fù)制代碼")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package service; //接口
public interface ISomeService { //待實(shí)現(xiàn)的方法
public void doFirst(); public void doSecond();
}</pre>

](javascript:void(0); "復(fù)制代碼")
2.SomeServiceImpl.java

View Code
3.MyMethodBeforeAdvice.java


](javascript:void(0); "復(fù)制代碼")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package aop; //前置增強(qiáng)
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
@Override public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("目標(biāo)方法執(zhí)行之前執(zhí)行");
}
}</pre>
[
](javascript:void(0); "復(fù)制代碼")
4.applicationContext.xml(Spring配置文件)


](javascript:void(0); "復(fù)制代碼")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 目標(biāo)對象 -->
<bean id="someService" class="service.SomeServiceImpl"></bean>
<!-- 切面:通知 -->
<bean id="beforeAdvice" class="aop.MyMethodBeforeAdvice"></bean>
<!-- *********************************************** -->
<!-- 1.*:NameMatchMethodPointcutAdvisor 名稱匹配方法切入點(diǎn)顧問 -->
<!-- 切面:顧問 顧問(Advisor)要包裝通知(Advice) -->
<bean id="beforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<!--指定需要增強(qiáng)的方法:這里是doFirst()方法,而doSecond()方法則不會增強(qiáng) -->
<property name="mappedName" value="doFirst"></property>
<!-- 也可以使用mappedNames指定多個(gè)方法
<property name="mappedNames" value="doFirst,doSecond"></property> -->
</bean>
<!-- *********************************************** -->
<!-- 2.*:RegexpMethodPointcutAdvisor 正則表達(dá)式匹配方法切入點(diǎn)顧問 -->
<!-- 切面: 顧問 顧問(Advisor)要包裝通知(Advice) -->
<!-- <bean id="beforeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<property name="pattern" value=".*doF.*t"></property>
</bean> -->
<!-- *********************************************** -->
<!-- 代理工廠Bean -->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="someService"></property>
<property name="interceptorNames" value="beforeAdvisor"></property>
</bean>
</beans></pre>
[
](javascript:void(0); "復(fù)制代碼")
5.MyTest.java


](javascript:void(0); "復(fù)制代碼")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package test; //對顧問(Advisor)測試
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.ISomeService; public class MyTest {
@Test public void testOne(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService)ctx.getBean("serviceProxy");
service.doFirst();
service.doSecond();
}
}</pre>

](javascript:void(0); "復(fù)制代碼")