-
AOP基本概念及特點
一、什么是AOP及實現方式
AOP:Aspect ?Oriented ?Programming,面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。
主要功能:日志記錄,性能統計,安全控制,事物處理,異常處理等等。(事務處理:任何操作數據庫的方法,尤其是增刪改,需要事物的處理,使用AOP時,執行到某種類型的方法,或者某一層的類,自動開啟事物和獲取連接、提交事物、關閉事物的步驟,簡化了操作)
AOP實現方式:1——預編譯方式。(AspectJ)
? ? ? ? ? ? ? ? ? ? ? ? ?2——運行期動態代理。(JDK動態代理、CGLib動態代理)
? ? ? ? ? ? ? ? ? ? ? ?(SpringAOP、JbossAOP)
切面的理解:例如系統中有產品管理、訂單管理、服務管理等等模塊(子功能),任一模塊(子功能),都需要記錄它的日志,控制它的事物,以及做一些安全驗證的功能。但是如果在每一個模塊(子功能)中去設計日志、事物、安全驗證,會帶來大量工作量,尤其當項目達到一定規模時,比如修改日志記錄的方式,那么則需要修改每一個模塊的日志功能,通過切面方式對開發人員是不可見的,當執行任一模塊時,通過預編譯或者運行期動態代理方式,都會去記錄它的日志,實現了一處寫功能,處處可實現的方式,對于開發人員允許不知道有這樣功能的存在,這樣就簡化了操作(修改日志、編寫事物等),業務功能橫向走,切面垂直業務功能。
二、AOP基本概念(切面—>織入—>目標對象—>切入點—>連接點—>通知—>引入—>AOP代理:“切面”要執行的動作,通過“織入”與所有功能模塊進行聯系,又通過“目標對象”找到具體功能模塊,通過“切入點”將要查找某個功能的某個方法,通過“連接點”找到該功能的特定方法的開始,通過“通知”將要執行何種切面的動作,通過“引入”引入該動作用到的對象和方法,通過“AOP代理”實現該方法。)
AOP相關概念:
1、切面(Aspect):一個關注點(事物),這個關注點可能會橫切多個對象(產品管理、訂單管理)。
2、連接點(Joinpoint):程序執行過程中的某個特定的點。(一個類中執行的某個方法的開始)。
3、通知(Advice):在切面的某個特定的連接點上執行的動作(方法執行的時候,切面額外執行的動作,比如說方法執行時,開啟事物提交功能)。
4、切入點(Pointcut):匹配連接點的斷言,在AOP中通知和一個切入點表達式關聯(在切面中匹配一個具體的連接點(某個功能的方法的開始))。
5、引入(Introduction):在不修改類代碼的前提下,修改的是字節碼文件,為類添加新的方法和屬性。(類似于編譯期動態的修改class文件去增加新的屬性和方法,源代碼中并沒有這樣的屬性和方法),取決于使用AspectJ和SpringAOP的實現方式,使用方式不同,為類添加屬性和方法的方式是有區別的。
6、目標對象(Target Object):被一個或者多個切面所通知的對象。(例如存在一個訂單的service(對象)和一個商品的service(對象),執行該模塊的功能時,切面會通知相對應的service,在執行數據庫操作時,去加上事物的控制,這兩個service就是目標對象)。
7、AOP代理(AOP ?Proxy):AOP框架創建的對象,用來實現切面契約(aspect contract),包括通知方法執行等功能(這里執行的方法指的是切面里的執行相應操作所用到的方法)開發時不知道這個對象存在的,也不清楚會創建成什么樣子。
8、織入(Weaving):把切面連接到其他的應用程序類型或者對象上,并創建一個被通知的對象,分為:編譯時織入、類加載時織入、執行時織入。(使切面和對象(模塊功能)牽連起來)
Spring官方文檔給出的解釋:
Advice(通知)的類型(在切面某個特定連接點執行的動作)
前置通知(Before advice):在某連接點(join point)(某個功能方法開始執行前)之前執行的通知,但不能阻止連接點前的執行(除該方法外的其他方法的執行)(除非它拋出一個異常)。
返回后通知(After returning advice):在某連接點(方法)正常完成后執行的通知(將要執行的切面功能)。
拋出異常后通知(After throwing advice):在方法拋出異常退出時執行的通知(將要執行切面的功能)。
后通知(After(finally)advice):當某連接點(方法)退出的時候執行的通知(切面將要執行的功能)(不論是正常返回還是異常退出都會執行的通知)。
環繞通知(Around Advice):包圍一個連接點(join point)的通知(在整個方法的內部都有切面要執行的功能存在,不分前后)。
Spring框架中AOP的用途:
用途1:提供了聲明式的企業服務(也可以是其他服務,例如互聯網服務),特別是EJB(重量級框架)的替代服務的聲明。
用途2:允許用戶定制自己的方面(切面),以完成OOP(面向對象編程,模擬世界中行為和方式,可以理解為實現一個功能的順序)與AOP(橫切的方式,可以理解為各個功能之間橫切的一種功能)的互補使用,可以實現自己橫切的功能。
三、Spring中的AOP(Spring中AOP的實現方式以及特點)
特點1:純java實現,無需特殊的編譯過程(普通代碼需要加載才能執行),不需要控制類加載器層次。可以更好的進行控制(使用類加載器應當謹慎,容易方法生ClassNotFound異常這種情況)。
特點2:目前只支持方法執行連接點(通知Spring Bean的方法執行),(在執行某方法時,才去執行切面的功能)。
特點3:SpringAOP不是為了提供最完整的AOP實現(盡管它非常強大);而是側重于提供一種AOP實現和Spring IoC容器之間的整合,用于幫助解決企業應用中的常見問題。
特點4:SpringAOP不會與AspectJ(完整的、全面的AOP解決方案)競爭,也不會提供綜合全面的AOP解決方案。
有接口和無接口的SpringAOP實現區別:(一個類是否實現了某個接口,例如一個ProductService接口,一個ProductServiceImpl實現了該接口,在這類上切入的切面就是有接口的AOP)
SpringAOP對于有接口的使用標準的JavaSE動態代理作為AOP代理,這使得任何接口(或者接口集)都可以被代理。
SpringAOP對于沒有接口的實現類中使用CGLIB代理(如果一個業務對象并沒有實現一個接口)
四、Schema-based 實現AOP(基于配置文件實現AOP)
五、API實現AOP(基于API實現AOP)
六、AspectJ實現AOP
查看全部 -
基于Schema-based配置的AOP實現
Spring所有的切面和通知器都必須放在<aop:config>內(可以配置多個<aop:config>元素),每一個<aop:config>可以包含aspect,pointcout和advisor元素(它們必須按照這個順序進行聲明)。
<aop:config>的配置大量使用了Spring的自動代理機制。
該配置的含義:一個類作為切面來聲明,切面Id是myAspect,也就是說程序執行某個功能模塊時,通過pointcut和Joinpoint執行該切面的功能。
案例:兩個類,一個類作為切面類,另一個作為業務類。(注意:<beans>中添加約束,xmlns:aop="http://www.springframework.org/schema/aop
)
代碼:
<?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"
? ? ? ? xmlns:aop="http://www.springframework.org/schema/aop"
? ? 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 ?
? ? ? ? http://www.springframework.org/schema/aop
? ? ? ? http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
? ? ? ? <bean id="aspectImooc" class="aspect.AspectImooc"></bean>
? ? ? ? <bean id="ServiceImooc" class="aspect.ServiceImooc"></bean>
? ? ? ? <aop:config>
? ? ? ? <aop:aspect id="aspectImoocAop" ref="aspectImooc"></aop:aspect>
? ? ? ? </aop:config>
查看全部 -
配置切入點Pointcut(找到某個功能模塊的具體方法)
execution用于匹配某個功能模塊的具體方法。
以下SpringAOP和Spring支持的AspectJ都支持。
圖一的1:執行所有的public方法時,進行切入(執行相應切面的相應功能)。
圖一的2:執行所有的set方法時,進行切入。
圖一的3:執行com.xy.service.AccountService類下的所有方法時,進行切入。
圖一的4:執行com.xyz.service包下的所有方法時,進行切入。
圖一的5:執行com.xyz.service包下以及子包下的所有方法時,進行切入。
以下為SpringAOP自己支持的。
SpringAOP其他的匹配。
圖一的1:匹配目標對象。
圖一的2:匹配參數的。
圖一的3:按注解匹配的。
圖一的4:按注解匹配的。
圖一的5:按注解匹配的。
圖二的1:參數里采用了何種注解。
圖二的2:采用了何種bean。
圖二的3:bean是以何種形式進行結尾的。(這些可以去Spring官方文檔和搜索引擎搜索,會有很全面的解釋,這些切入點會有很多類型,在具體使用時,可以查找相應文檔)
完整的AOP配置(根據不同業務需求,查找相關文檔,進行不同的expression表達式配置)
案例1:表示執行service包下的所有類的任何類型的方法時,進行切入。
案例2:表示執行SystemArchitecture的businessService()方法時,才進行切入。
案例3:執行AspectBiz中的所有方法,都會進行切入。
配置以某個單詞結尾的類:例如上面Aspect編寫成*。
查看全部 -
Advice應用(advice的配置)
Before advice的兩種配置方式
<aop:before ?pointcut-ref="切入點的Id" ?method="切面的方法"/>。直接設置切入點(就不用單獨設置<aop:point-cut/>標簽了)
<aop:before ?pointcut="execution(* ?com.xyz.myapp.dao..(..))" ?method="doAccessCheck"/>
案例:(前置)
步驟1:編寫邏輯功能類、切面功能類、并編寫各自的方法。
代碼:
步驟2:編寫XML配置,實例化邏輯功能類的Bean和切面功能類的Bean,配置切面、切入點、通知。
案例:(After return advice返回后通知)
<aop:after-returning>returning屬性,限制方法的返回值,例如retVal。(arg-names為參數名稱,它是嚴格匹配的。)
步驟1:編寫邏輯功能類、切面功能類、并編寫各自的方法(和以上相似,只不過切面類多了一個方法)。
步驟2:編寫XML配置,實例化邏輯功能類的Bean和切面功能類的Bean,配置切面、切入點、通知。
案例:(拋出異常后通知After throwing advice,<aop:after-throwing>標簽多了一個throwing屬性,它是限制可被傳遞的異常參數名稱)
步驟1:編寫邏輯功能類、切面功能類、并編寫各自的方法(編寫發生異常后切面要執行的方法)
步驟2:編寫XML配置,實例化邏輯功能類的Bean和切面功能類的Bean,配置切面、切入點、拋出異常通知。
案例:(后置通知After (finally) advice)
步驟1:編寫邏輯功能類、切面功能類、并編寫各自的方法(編寫邏輯方法執行后要執行的切面方法)
步驟2:編寫XML配置,實例化邏輯功能類的Bean和切面功能類的Bean,配置切面、切入點、拋出異常通知。
案例:(環繞通知After (finally) advice)
步驟1:編寫邏輯功能類、切面功能類、并編寫各自的方法(編寫邏輯方法執行后要執行的切面方法)
步驟2:編寫XML配置,實例化邏輯功能類的Bean和切面功能類的Bean,配置切面、切入點、拋出異常通知。
查看全部 -
Introductions應用(簡介的advice,作用:在不改變對象和類的代碼的前提下,為對象和類添加新的方法。)
Introduction定義:簡介允許一個切面聲明一個實現指定接口的通知對象,并且提供一個接口實現類(接口的實現類,來代表實例化好的Bean)來代表這些對象。
Introductions的實現:由<aop:aspect>中的<aop:declare-parents>元素聲明,該元素用于聲明所有匹配的類型,擁有一個新的parent(接口聲明的對象可以指向getBean獲得的對象)。
<aop:declare-parents>屬性
type-matching="expression表達式(表示匹配什么樣的類型)。
implement-interface="接口的路徑"(切面類的接口)。
default-impl=“切面類的路徑”。
案例:
步驟1:編寫邏輯功能接口(限制邏輯功能),并提供它的實現類。
public interface ServiceInterfaceFunction {
public void print();
}
public class ServiceFunction implements ServiceInterfaceFunction {
@Override
public void print() {
System.out.println("邏輯功能接口實現類的方法執行了");
}
}
public class ServiceGongNeng {
}
步驟2:配置<aspect:config>(有接口的SpringAOP實現,使用JavaSE動態代理,也就是JDK代理,JDK動態代理只能對實現了接口的類生成代理,而不能針對類,所以要配置<aop:declare-parents>)。
步驟3:測試
@Test
public void testaop(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-aop-schema-advice.xml");
ServiceInterfaceFunction sif=(ServiceInterfaceFunction)ac.getBean("serviceGongNeng",ServiceGongNeng.class);
sif.print();
}
結果:正常可以執行接口實現類里的方法。(注意:types-matching="springAOP.ServiceGongNeng"沒有execution)
重點擴展:所有基于配置文件的aspects,只支持單例模式(Single Model)。
查看全部 -
Advisors
1、advisor就像一個小的自包含的方面,只有一個advice。
2、切面自身通過一個bean表示,并且必須實現某個advice接口,同時,advisor也可以很好的利用AspectJ的切入點表達式(后面介紹AspectJ實現AOP時會介紹到AspectJ的切入點表達式)。
3、Spring通過配置文件中<aop:advisor>元素支持advisor,實際使用中,大多數情況下它會和transactional advice(事物相關的advice)配合使用(也可以有其他使用方式,根據項目或者工程的實際情況來決定)。
4、為了定義一個advisor的優先級以便讓advice可以有序,可以使用order屬性來定義advisor的順序。
advisor的配置(<tx:advice>是對于事物的相關聲明,通過這種方式使用advisor,這種方式是在使用Spring事物的控制時,經常使用的方式)
案例:(沒有講解advisor的案例,講解了環繞通知的一個案例,適用場景:統計邏輯功能方法的調用次數,例如統計某方法調用3次,就不讓調用了)
步驟1:
public class ServiceFunction {
public void print1(){
System.out.println("邏輯功能方法1執行了");
}
public void print2(){
System.out.println("邏輯功能方法2執行了");
throw new RuntimeException();
}
}
步驟2:
public class AspectFunction {
private int max_times;
public int getMax_times() {
return max_times;
}
public void setMax_times(int max_times) {
this.max_times = max_times;
}
public Object aspectFunction(ProceedingJoinPoint pjp) throws Throwable{
int num_times=0;
do{
num_times++;
try{
System.out.println(num_times+"次");
return pjp.proceed();
}catch(RuntimeException e){
}
}while(num_times<=max_times);
System.out.println("方法總共執行了"+num_times+"次");
return null;
}
}
步驟3:
查看全部 -
Spring的API實現AOP功能
1、這是Spring1.2的歷史用法,現在Spring4.0仍然支持。
2、這是SpringAOP的基礎
使用Spring的API實現AOP的原因:Schema-based(配置文件)和AspectJ實現AOP完全可以滿足我們使用AOP的功能,使用API是因為這是SpringAOP的基礎,可以加深我們應用和了解SpringAOP的理解(無論是注解方式、還是XML配置文件的方式,最終實現的基礎都是和API密切關系的)。
3、現在SpringAOP的用法也是基于歷史的,只是更簡便了(在Spring1.2時配置文件很多,還需要了解各種配置)。
Spring的API
一、Pointcut(作為一個接口,有幾個實現類)
1、Pointcut接口的實現類之一:NameMatchMethodPointcut,根據方法名字進行匹配。
2、該類有一個成員變量:mappedNames(它是一個集合,存放用于匹配的方法的名稱),匹配方法的集合。
配置文件的例子:(sa開頭的所有方法進行切入)
二、Before advice(原理:單獨寫一個前置通知的類,并實現MethodBeforeAdvice接口,并實現該接口的方法,)
1、一個簡單的通知類型。
2、執行邏輯方法前被調用,不需要MethodInvocation對象。
3、前置通知可以在連接點執行之前插入自定義行為,但不能改變返回值。
案例:(會使用到后面的知識,暫時編寫這些)
public class MoocBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] object, Object target) throws Throwable {
System.out.println("前置通知的方法:" + method.getName() + " ? " + target.getClass().getName());
}
}
三、Throws advice
1、如果連接點(執行的業務方法)出現異常,throws advice在方法返回后被調用。
2、如果throws-advice的方法拋出異常,那么它將覆蓋原有異常。
3、接口org.springframework.aop.ThrowAdvice不包含任何方法,僅僅是一個聲明,其實現類一定要實現類似這樣的方法,void afterThrowing([Method, args, target],ThrowableSubclass);
Throws advice發生異常的說明
public void afterThrowing(Exception ex);//只有一個異常。
public void afterThrowing(RemoteException ex);//另外一種類型的異常。
?public void afterThrowing(Method method,Object[] args(目標參數),Object target(對象),Exception ex);
?public void afterThrowing(Method method,Object[] args(目標參數),Object target(對象),ServletException ex(其他類型的異常));
結論:異常通知(如上)方法參數里必須有異常,其他參數可有可無的。
拋出異常通知案例:
public class MoocThrowsAdvice implements ThrowsAdvice{
public void afterThrows(Exception ex){
System.out.println("拋出異常通知執行了");
}
public void afterThrows(Method method,Object[] object,Object target,Exception ex){
System.out.println("拋出異常通知執行了");
}
}
四、After Returning advice
1、返回后通知必須實現org.springframework.aop.AfterReturningAdvice接口(它和基于配置文件的配置方式是一樣的)
2、可以訪問返回值(但不能修改返回值)、被調用的方法、方法的參數和目標。
3、如果拋出異常,將會拋出攔截器鏈,替代返回值。
返回后通知案例
public class MoocAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("返回后通知方法執行了"+method.getName()+" ?"+target.getClass().getName()+" ?"+returnValue);
}
}
五、Interception around advice(環繞通知)
1、Spring的切入點模型使得切入點可以獨立與advice重用,以針對不同的advice可以使用相同的切入點(和之前基于XML配置文件的AOP實現方式是一樣的,切入點可以放在外面單獨地去定義,通過Point reference,在每一個業務邏輯方法中都可以引用相同的切入點,當然,每個Advice也可以定義自己的Point cut)。
案例:
public class MoocMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object obj = null;
System.out.println("環繞前通知實現了");
obj = invocation.proceed();
System.out.println("環繞后通知實現了");
return null;
}
}
六、Introduction advice(和XML配置中的Introduction advice一樣的功能)
1、Spring把引入通知作為一種特殊的攔截通知。
2、如果使用API實現AOP,則需要IntroductionAdvisor和IntroductionInterceptor這兩個接口。
3、Introduction advice僅適用于類,不能和任何其它切入點一起使用。
如下為基于XML配置的Introduction advice內容
一個Spring test suite的例子
1、如果調用lock()方法,希望所有的setter方法拋出LockedException異常(使用場景:如使物體不可變,AOP典型例子)
2、需要完成繁重任務的IntroductionInterceptor,不是去實現該接口,而是使用org.springframework.aop.support.DelegatingIntroductionInterceptor
查看全部 -
ProxyFactoryBean及相關內容(上)
1、通過Spring API創建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
2、ProxyFactoryBean可以完全控制切入點和通知(advice)以及他們的順序。(和其它工廠Bean實現方式是一樣的,都引入了一個中間層)
假如有這樣一種情況:定義了一個Bean的Id為foo的ProxyFactoryBean,那么引用foo這個對象,看到的將不是ProxyFactoryBean本身,而是ProxyFactoryBean這個類通過getObject()方法創建的對象。
getObject():將創建一個AOP代理包裝一個目標對象。(ProxyFactoryBean通過這種方式達到代理的目的)
3、使用ProxyFactoryBean或者其它IoC相關類來創建AOP代理的最重要好處是因為通知和切入點也可以由IoC來管理。
4、當被代理類沒有實現任何接口,使用CGLIB代理,否則使用JDK代理。
5、通過設置proxyTargetClass為true,可強制使用CGLIB代理(無論代理類是否實現接口)
6、如果目標類實現了一個(或者多個)接口,那么創建代理的類型將依賴ProxyFactoryBean的配置。(因為ProxyFactoryBean里有一個ProxyInterfaces屬性,該屬性可以查看該類實現了哪些接口)
7、如果ProxyFactoryBean的proxyInterfaces屬性被設置為一個或者多個全限定接口名(包括包名、類名、完整的名稱),基于JDK的代理將被創建。
8、如果ProxyFactoryBean的proxyInterfaces屬性沒有被設置,但是目標類實現了一個(或者更多)接口,那么ProxyFactoryBean將自動檢測到這個目標類已經實現了至少一個接口,創建一個基于JDK的代理。
例子:創建基于接口的代理
ProxyFactoryBean工作原理:首先,定義了一個Id為personTarget的Bean,又定義了一個Id為myAdvisor的Bean,定義了一個Id為debugInterceptor的Bean,重點在接下來的定義,定義了一個Id為person的Bean,但是對應的并不是Person類,而是Spring提供的ProxyFactoryBean這個類,并且這個Bean配置里有一個屬性名為proxyInterfaces的<property>配置,并且它的value值為Person的路徑,另外一個<property>標簽屬性名稱為targer并且指向personTarget(這個類是Person的具體實現類),當我們get()Id為person這個Bean時,返回的并不是ProxyFactoryBean的對象,而是ProxyFactoryBean里的getObject()返回的對象,它返回的是屬性名為target指向的personTarget的對象,通過ProxyFactoryBean創建代理的時候,可以指定它的interceptor,interceptorNames屬性,是一個集合,可以通過List、Value指定它的具體內容。
案例:(通過Spring API實現通知)
各種通知的類前面已經寫好了,所以主要進行XML配置。
public interface BizLogic {
String save();
}
public class BizLogicImpl implements BizLogic {
@Override
public String save() {
System.out.println("BizLogicImpl: BizLogicImpl save");
return "BizLogicImpl save";
}
}
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.xsd"?
? ? ? ? default-autowire="constructor">
? ?<bean id="afterReturningAdvice" class="springAPI.MoocAfterReturningAdvice" ></bean>
? ? ? <bean id="beforeAdvice" class="springAPI.MoocBeforeAdvice" ></bean>
? ? ? <bean id="methodInterceptor" class="springAPI.MoocMethodInterceptor" ></bean>
? ? ? <bean id="throwsAdvice" class="springAPI.MoocThrowsAdvice" ></bean>
? ?<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
? ?<property name="mappedNames">
? ?<list>
? ?<value>sa*</value>
? ?</list>
? ?</property>
? ?</bean>
? ?<bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
? ?<property name="advice" ref="beforeAdvice"></property>
? ?<property name="pointcut" ref="pointcutBean"></property>
? ?</bean>
? ?<bean id="bizLogicImplTarget" class="springAPI.BizLogicImpl"></bean>
? ?<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
? ?<property name="target"><ref bean="bizLogicImplTarget"/></property>
? ?<property name="interceptorNames">
? ?<list>
? ?<value>afterReturningAdvice</value>
? ? ? <value>beforeAdvice</value>
? ? ? <value>methodInterceptor</value>
<!-- ? ? ? <value>throwsAdvice</value>
?--> ??
? ?</list>
? ?</property>
? ?</bean>?
? ?
?</beans>
測試:
@Test
public void testApi(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc.xml");
BizLogic bl=(BizLogic)ac.getBean("bizLogicImpl");
bl.save();
}
結果:
前置通知的方法:save ? springAPI.BizLogicImpl
環繞前通知實現了
BizLogicImpl: BizLogicImpl save
環繞后通知實現了
返回后通知方法執行了save ?springAPI.BizLogicImpl ?null
查看全部 -
AspectJ介紹及Pointcut注解應用
1、@AspectJ的風格類似純java注解的普通java類。
2、Spring可以使用AspectJ來做切入點解析。
3、AOP的運行時仍舊是純的SpringAOP,對AspectJ的編譯器或者織入無依賴性。
Spring中配置@AspectJ
1、對@AspectJ支持可以使用XML或者Java注解類型的配置(@EnableAspectJAutoProxy使用AspectJ進行自動代理)
2、前提:確保AspectJ的aspectjweaver.jar庫包含在應用程序(版本1.6.8或更高版本)的classpath中,以Spring4.0.5為基準的,其他版本可能不一樣(可以根據具體情況查找Spring具體文檔)。
aspect注解的具體使用
1、@AspectJ切面使用@Aspect注解配置,擁有@Aspect的任何bean將被Spring自動識別并應用。
2、@Aspect注解的類可以有方法和字段,他們也可能包括切入點(pointcut),通知(Advice)和引入(introduction)聲明。
3、@Aspect注解是不能夠通過類路徑自動檢測發現的,所以需要配合使用@Component注釋或者在xml配置bean(@Aspect注解的類配置在xml中)。
4、一個類中的@Aspect注解標識它為一個切面,并且將自己從自動代理中排出。
如何定義一個切入點
1、一個切入點通過一個普通的方法定義來提供,并且切入點表達式使用@Pointcut注解,方法返回類型必須為void
2、定義一個名為'anyOldTransfer',這個切入點將匹配任何名為“transfer”的方法的執行。
切入點支持哪些定義方式
組合Pointcut
1、切入點表達式可以通過&&、||和!進行組合,也可以通過名字引用切入點表達式。
2、通過組合,可以建立更加復雜的切入點表達式。
如何定義良好的pointcuts
1、AspectJ是編譯期的AOP
2、檢查代碼并匹配連接點與切入點的代價是昂貴的。
3、一個好的切入點應該包括以下幾點
【a】選擇特定類型的連接點,如:execution,get,set,call,handler。
【b】確定連接點范圍,如:within,withincode。
【c】匹配上下文信息,如:this,target,@annotation。
查看全部 -
Advice定義及實例(AspectJ中advice的定義)
一、Before advice:對于切面類使用@Aspect修飾類,相當于<aop:aspect>配置,在方法上添加@Before(“execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))”),相當于<aop:before>標簽,@Component修飾類,省略了配置<bean>標簽。
案例(前置通知):
步驟1:業務方法
public class MoocBiz {
public void print(){
System.out.println("業務方法執行了");
}
}
步驟2:切面方法
@Aspect
public class MoocAspect {
@Before("execution(* *Biz.*(..))")
public void beforeAdvice(){
System.out.println("前置通知方法執行了");
}
}
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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
? ? ? ? http://www.springframework.org/schema/aop?
? ? ? ? http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="src"></context:component-scan>
<bean id="moocBiz" class="MoocBiz"></bean>
<bean id="moocAspect" class="MoocAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
測試:
@Test
public void testMooc(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc2.xml");
MoocBiz mb=(MoocBiz) ac.getBean("moocBiz");
mb.print();
}
結果:
前置通知方法執行了
業務方法執行了
可以定義共享的pointcut(一個方法上使用@Pointcut("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))"),然后@Before("pointcut()")修飾另一個方法上
@Aspect
public class MoocAspect {
@Pointcut("execution(* *Biz.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void beforeAdvice(){
System.out.println("前置通知方法執行了");
}
}
二、After returning advice
和前置通知相似。
有時候需要在通知體內得到返回的實際值,可以使用@AfterReturning綁定返回值的形式,如果知道返回值類型可以寫類型,如果不知道,可以寫Object。
后置通知案例:
public class MoocBiz {
public void print(){
System.out.println("業務方法執行了");
}
}
@Aspect
public class MoocAspect {
@Pointcut("execution(* *Biz.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void beforeAdvice(){
System.out.println("前置通知方法執行了");
}
@AfterReturning(pointcut="pointcut()",returning="o")
public void afterAdvice(Object o){
System.out.println("AfterReturning:"+o);
}
}
<context:component-scan base-package="src"></context:component-scan>
<bean id="moocBiz" class="MoocBiz"></bean>
<bean id="moocAspect" class="MoocAspect"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
結果:
前置通知方法執行了
業務方法執行了
AfterReturning:null
三、After throwing advice
有時候需要在通知體內得到異常的實際值,可以使用@AfterReturning綁定返回值的形式
案例:
@AfterThrowing(pointcut="pointcut()",throwing="t")
public void afterThrowAdvice(RuntimeException t){
System.out.println("異常:"+t.getMessage());
}
public class MoocBiz {
public void print(){
System.out.println("業務方法執行了");
throw new RuntimeException("運行時異常");
}
}
結果:
前置通知方法執行了
業務方法執行了
異常:運行時異常
四、After(finally)advice
最終通知必須準備處理正常和異常兩種返回情況,它通常用于釋放資源。(類似于try-catch中finally的功能)
@After("pointcut()")
public void afterAdvice(){
System.out.println("后置通知執行了");
}
前置通知方法執行了
業務方法執行了
后置通知執行了
異常:運行時異常
五、環繞通知(Around Advice)
1、環繞通知使用@Around注解來聲明,通知方法的第一個參數必須是ProceedingJoinPoint類型
2、在通知方法內部會調用ProceedingJoinPoint的proceed()方法會導致執行真正的方法,同時可以傳入一個Object[]對象,數組中的值將被作為參數傳遞給方法。
案例:
@Around("pointcut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("環繞前置通知方法執行了========");
Object o=pjp.proceed();
System.out.println("環繞后置通知方法執行了========");
return o;
}
結果:
環繞前置通知方法執行了========
前置通知方法執行了
業務方法執行了
環繞后置通知方法執行了========
后置通知執行了
AfterReturning:null
查看全部 -
Advice擴展
一、給advice傳遞參數
【a】方式一:在通知注解里寫參數,通過&& args(參數名,..)傳入參數,該參數可以是自己定義的類型,也可以為String等類型。(通常獲取參數,進行一些判斷或者日志的記錄)
【a】方式二:切入點和通知分開寫的方式,在切入點注解里寫參數,在通知注解里引用切入點注解的方法名。
【b】運行時,生效的一個注解,這兩個注解修飾在方法上,在通知注解里通過&& @annotation(運行時注解修飾的方法名,首字母小寫),如果某個方法使用了運行時注解,則一定會給value()附一個值,在通知方法里可以通過方法參數,調用該方法得到該值(一般記錄該注解用了哪些方法,或者用在判斷該方法上是否加了某注解,后者判斷獲取到的值是哪值)
查看全部 -
Bean的生命周期:
分為4個階段
定義
初始化
使用
銷毀
查看全部 -
Bean的作用域
singleton:單例,指一個Bean容器中只存在一份。
prototype:每次請求(每次使用)創建新的實例,destroy方式不生效。
request:每次http請求創建一個實例且僅在當前request內有效。
session:同上,每次http請求擦黃健,當前session內有效。
global session:基于portlet的web中有效(portlet定義了global session),如果是在web中,同session
查看全部 -
Bean配置項
Id:在整個IoC容器中的唯一標識。
Class:具體要實例化的哪個類。
Scope:作用域。
Constructor arguments:構造器的參數。
Properties:屬性。
Autowiring mode:自動裝配的模式。
lazy-initialization mode:懶加載模式。
Initialzation/destruction method:初始化/銷毀的方法。
理論上,處理class之外其他都可以不配置。但想要從Bean容器中得到某一個實例,有兩種方式,一種是通過Id,另一種是通過Bean 的類型。
查看全部 -
設值注入例子
調用set方法
查看全部
舉報