javascript
Spring AOP学习
什么是AOP
?Spring AOP 面向切面編程,采取橫向抽取機(jī)制,取代了傳統(tǒng)縱向繼承體系重復(fù)性代碼(性能監(jiān)視、事務(wù)管理、安全檢查、緩存)
?使用純Java實(shí)現(xiàn),不需要專(zhuān)門(mén)的編譯過(guò)程和類(lèi)加載器,在運(yùn)行期通過(guò)JDK動(dòng)態(tài)代理或者CGLIB動(dòng)態(tài)代理的方式向目標(biāo)類(lèi)織入增強(qiáng)代碼
?AspectJ ?[??spekt]?是一個(gè)基于Java語(yǔ)言的AOP框架,擴(kuò)展了Java語(yǔ)言,提供了一個(gè)專(zhuān)門(mén)的編譯器,在編譯時(shí)提供橫向代碼的織入。
?
在決定使用哪種框架實(shí)現(xiàn)你的項(xiàng)目之前,有幾個(gè)要點(diǎn)(同樣適用于其他框架)。https://www.oschina.net/translate/comparative_analysis_between_spring_aop_and_aspectj
明確你在應(yīng)用橫切關(guān)注點(diǎn)(cross-cutting concern)時(shí)(例如事物管理、日志或性能評(píng)估),需要處理的是Spring beans還是POJO。
如果正在開(kāi)發(fā)新的應(yīng)用,則選擇Spring AOP就沒(méi)有什么阻力。但是如果你正在維護(hù)一個(gè)現(xiàn)有的應(yīng)用(該應(yīng)用并沒(méi)有使用Spring框架),AspectJ就將是一個(gè)自然的選擇了。
為了詳細(xì)說(shuō)明這一點(diǎn),假如你正在使用Spring AOP,當(dāng)你想將日志功能作為一個(gè)通知(advice)加入到你的應(yīng)用中,用于追蹤程序流程,那么該通知(Advice)就只能應(yīng)用在Spring beans的連接點(diǎn)(Joinpoint)之上。
另一個(gè)需要考慮的因素是,你是希望在編譯期間進(jìn)行織入(weaving),還是編譯后(post-compile)或是運(yùn)行時(shí)(run-time)。Spring只支持運(yùn)行時(shí)織入。如果你有多個(gè)團(tuán)隊(duì)分別開(kāi)發(fā)多個(gè)使用Spring編寫(xiě)的模塊(導(dǎo)致生成多個(gè)jar文件,例如每個(gè)模塊一個(gè)jar文件),并且其中一個(gè)團(tuán)隊(duì)想要在整個(gè)項(xiàng)目中的所有Spring bean(例如,包括已經(jīng)被其他團(tuán)隊(duì)打包了的jar文件)上應(yīng)用日志通知(在這里日志只是用于加入橫切關(guān)注點(diǎn)的舉例),那么通過(guò)配置該團(tuán)隊(duì)自己的Spring配置文件就可以輕松做到這一點(diǎn)。之所以可以這樣做,就是因?yàn)镾pring使用的是運(yùn)行時(shí)織入。如果你使用AspectJ想要做到同樣的事情,你也許就需要使用acj(AspectJ編譯器)重新編譯所有的代碼并且進(jìn)行重新打包。否則,你也可以選擇使用AspectJ編譯后(post-compile)或載入時(shí)(load-time)織入。
Advisor和Aspect的區(qū)別:
Advisor:Spring傳統(tǒng)意義上的切面,支持一個(gè)切點(diǎn)和一個(gè)通知的組合
Aspect:支持多個(gè)切點(diǎn)和多個(gè)通知的組合,真實(shí)開(kāi)發(fā)中常用。
為什么要用AOP
在傳統(tǒng)的面向?qū)ο缶幊虝r(shí),難免有很多重復(fù)的代碼。我們可以把重復(fù)的代碼提出來(lái),放到一個(gè)類(lèi)中,然后統(tǒng)一使用,這樣將來(lái)修改起來(lái)也會(huì)很是方便。
AOP底層原理
就是代理機(jī)制:
* 動(dòng)態(tài)代理:(JDK中使用)
* JDK的動(dòng)態(tài)代理,對(duì)實(shí)現(xiàn)了接口的類(lèi)生成代理.
Spring的AOP代理
JDK動(dòng)態(tài)代理:對(duì)實(shí)現(xiàn)了接口的類(lèi)生成代理。沒(méi)有實(shí)現(xiàn)接口的類(lèi),就無(wú)法生成代理對(duì)象了。
CGLib代理機(jī)制:對(duì)類(lèi)生成代理
代理實(shí)例:
一個(gè)接口:
package com.js.demo10;public interface Greeting {void sayHello(String name); }一個(gè)實(shí)現(xiàn)類(lèi):
package com.js.demo10;public class GreetingImpl implements Greeting {@Overridepublic void sayHello(String name) {before();System.out.println("Hello! " + name);after();}private void before() {System.out.println("Before");}private void after() {System.out.println("After");} }
靜態(tài)代理實(shí)現(xiàn)實(shí)例:
由程序員創(chuàng)建或由特定工具自動(dòng)生成源代碼,再對(duì)其編譯。在程序運(yùn)行前,代理類(lèi)的.class文件就已經(jīng)存在了。
package com.js.demo10;public class GreetingProxy implements Greeting {private GreetingImpl greetingImpl;public GreetingProxy(GreetingImpl greetingImpl) {this.greetingImpl = greetingImpl;}@Overridepublic void sayHello(String name) {before();greetingImpl.sayHello(name);after();}private void before() {System.out.println("Before");}private void after() {System.out.println("After");} }
客戶(hù)端來(lái)調(diào)用:
public class GreetingProxyClient {public static void main(String[] args) { Greeting greetingProxy = new GreetingProxy(new GreetingImpl());greetingProxy.sayHello("Jack");} }問(wèn)題:XxxProxy 這樣的類(lèi)會(huì)越來(lái)越多,如何才能將這些代理類(lèi)盡可能減少呢?最好只有一個(gè)代理類(lèi)。
如果項(xiàng)目中有多個(gè)類(lèi),則需要編寫(xiě)多個(gè)代理類(lèi),工作量大,不好修改,不好維護(hù),不能應(yīng)對(duì)變化。
JDK動(dòng)態(tài)代理實(shí)現(xiàn)實(shí)例:
?JDK內(nèi)置的Proxy動(dòng)態(tài)代理可以在運(yùn)行時(shí)動(dòng)態(tài)生成字節(jié)碼,而沒(méi)必要針對(duì)每個(gè)類(lèi)編寫(xiě)代理類(lèi)。
package com.js.demo10;import java.lang.reflect.Method;import org.springframework.cglib.proxy.InvocationHandler; import org.springframework.cglib.proxy.Proxy;public class JDKDynamicProxy implements InvocationHandler {private Object target;public JDKDynamicProxy(Object target) {this.target = target;}@SuppressWarnings("unchecked")public <T> T getProxy() {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object result = method.invoke(target, args);after();return result;}private void before() {System.out.println("Before");}private void after() {System.out.println("After");} }客戶(hù)端來(lái)調(diào)用:
public class JDKDynamicProxyClient {public static void main(String[] args) {Greeting greeting = new JDKDynamicProxy(new GreetingImpl()).getProxy();greeting.sayHello("Jack");} }問(wèn)題:JDK 給我們提供的動(dòng)態(tài)代理只能代理接口,而不能代理沒(méi)有接口的類(lèi)。
CGLIB動(dòng)態(tài)代理實(shí)現(xiàn)實(shí)例:
CGLIB(Code Generation Library)是一個(gè)開(kāi)源項(xiàng)目,是一個(gè)強(qiáng)大的,高性能,高質(zhì)量的Code生成類(lèi)庫(kù),它可以在運(yùn)行期擴(kuò)展Java類(lèi)與實(shí)現(xiàn)Java接口,通俗說(shuō)cglib可以在運(yùn)行時(shí)動(dòng)態(tài)生成字節(jié)碼。
大概的原理是:使用cglib可以實(shí)現(xiàn)動(dòng)態(tài)代理,即使被代理的類(lèi)沒(méi)有實(shí)現(xiàn)接口,但被代理的類(lèi)必須不是final類(lèi)。cglib繼承被代理的類(lèi),重寫(xiě)方法,織入通知,動(dòng)態(tài)生成字節(jié)碼并運(yùn)行,因?yàn)槭抢^承所以final類(lèi)是沒(méi)有辦法動(dòng)態(tài)代理的。
package com.js.demo10;import java.lang.reflect.Method;import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy;public class CGLibDynamicProxy implements MethodInterceptor {private static CGLibDynamicProxy instance = new CGLibDynamicProxy();private CGLibDynamicProxy() {}public static CGLibDynamicProxy getInstance() {return instance;}@SuppressWarnings("unchecked")public <T> T getProxy(Class<T> cls) {return (T) Enhancer.create(cls, this);}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {before();Object result = proxy.invokeSuper(target, args);after();return result;}private void before() {System.out.println("Before");}private void after() {System.out.println("After");} }客戶(hù)端來(lái)調(diào)用:
public class CGLibDynamicProxyClient {public static void main(String[] args) { Greeting greeting = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);//?Singleton 模式greeting.sayHello("Jack");} }?
結(jié)論:Spring框架,如果類(lèi)實(shí)現(xiàn)了接口,就使用JDK的動(dòng)態(tài)代理生成代理對(duì)象,如果這個(gè)類(lèi)沒(méi)有實(shí)現(xiàn)任何接口,使用CGLIB生成代理對(duì)象.
如果項(xiàng)目中有些類(lèi)沒(méi)有實(shí)現(xiàn)接口,則不應(yīng)該為了實(shí)現(xiàn)動(dòng)態(tài)代理而刻意去抽出一些沒(méi)有實(shí)例意義的接口,通過(guò)cglib可以解決該問(wèn)題。
AOP的術(shù)語(yǔ)
Aspect(切面):是切入點(diǎn)和通知(引介)的結(jié)合,橫切關(guān)注點(diǎn)被模塊化的特殊對(duì)象。即,它是一個(gè)類(lèi)。
Advice(通知/增強(qiáng)):所謂通知是指攔截到Joinpoint之后所要做的事情,即攔截邏輯.切面必須要完成的工作。即,它是類(lèi)中的一個(gè)方法。
通知分為前置通知,后置通知,異常通知,最終通知,環(huán)繞通知
Target(目標(biāo)對(duì)象):代理的目標(biāo)對(duì)象
Proxy(代理):一個(gè)類(lèi)被AOP織入增強(qiáng)后,就產(chǎn)生一個(gè)結(jié)果代理類(lèi)
Joinpoint(連接點(diǎn)):所謂連接點(diǎn)是指那些被攔截到的點(diǎn)。在spring中,這些點(diǎn)指的是方法,因?yàn)閟pring只支持方法類(lèi)型的連接點(diǎn).
Pointcut(切入點(diǎn)):所謂切入點(diǎn)是指我們要對(duì)哪些Joinpoint連接點(diǎn)進(jìn)行攔截的定義,簡(jiǎn)單理解如正則表達(dá)式.
Weaving(織入):是指把增強(qiáng)應(yīng)用到目標(biāo)對(duì)象來(lái)創(chuàng)建新的代理對(duì)象的過(guò)程.
Introduction(引介):引介是一種特殊的通知在不修改類(lèi)代碼的前提下, Introduction可以在運(yùn)行期為類(lèi)動(dòng)態(tài)地添加一些屬性或方法.
spring采用動(dòng)態(tài)代理織入,而AspectJ采用編譯期織入和類(lèi)裝在期織入
?
?
?
Spring按照通知Advice在目標(biāo)方法的連接點(diǎn)位置,通知Advice可分為五類(lèi)(可以分編程式和聲明式實(shí)現(xiàn))
前置通知:org.springframework.aop.MethodBeforeAdvice,在目標(biāo)方法執(zhí)行之前實(shí)施增強(qiáng)
后置通知:org.springframework.aop.AfterReturningAdvice,在目標(biāo)方法執(zhí)行之后實(shí)施增強(qiáng)
環(huán)繞通知:org.aopalliance.intercept.MethodInterceptor,在目標(biāo)方法執(zhí)行前后實(shí)施增強(qiáng)
異常拋出通知:org.springframework.aop.ThrowsAdvice,在方法拋出異常之后實(shí)施增強(qiáng)
引介通知:org.springframework.aop.IntroductionInterceptor,在目標(biāo)類(lèi)中添加一些新的方法和屬性
?
Spring中切面的類(lèi)型:
Advisor:Spring中的傳統(tǒng)切面。
Aspect:都是有一個(gè)切點(diǎn)和一個(gè)通知的組合
Advisor:多個(gè)切點(diǎn)和多個(gè)通知的組合
?
Advisor?:?代表一般切面,Advice本身就是一個(gè)切面,對(duì)目標(biāo)類(lèi)所有方法進(jìn)行攔截(*不帶有切點(diǎn)的切面)
PointcutAdvisor?:?代表具有切點(diǎn)的切面,可以指定攔截目標(biāo)類(lèi)哪些方法帶有切點(diǎn)的切面,針對(duì)某個(gè)方法進(jìn)行攔截
?IntroductionAdvisor?:?代表引介切面,針對(duì)引介通知而使用切面(不要求掌握)
不帶有切點(diǎn)的切面
不帶切點(diǎn)的切面實(shí)例
導(dǎo)入相應(yīng)的jar包:Spring開(kāi)發(fā)基礎(chǔ)包、spring-aop-4.3.7.RELEASE.jar、aopalliance-1.0.jar(AOP聯(lián)盟包)
編寫(xiě)一個(gè)接口Customer:
package com.js.aopStu;public interface CustomerDao {public void add();public void delete();public void update();public void find(); }編寫(xiě)實(shí)現(xiàn)類(lèi)CustomerImpl:
package com.js.aopStu;public class CustomerImpl implements CustomerDao {@Overridepublic void add() { System.out.println("添加客戶(hù)...");}@Overridepublic void delete() { System.out.println("刪除客戶(hù)...");}@Overridepublic void update() { System.out.println("修改客戶(hù)...");}@Overridepublic void find() {System.out.println("查詢(xún)客戶(hù)...");}}編寫(xiě)增強(qiáng)的代碼。新建一個(gè)類(lèi)MyBeforeAdvice,以前置增強(qiáng)為例
package com.js.aopStu;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;/*** 前置增強(qiáng)* 實(shí)現(xiàn)指定接口* @author hdb**/ public class MyBeforeAdvice implements MethodBeforeAdvice{/*** method:執(zhí)行的方法* args:參數(shù)* target:目標(biāo)對(duì)象*/@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("前置增強(qiáng)...");}}配置代理生成代理類(lèi),基于ProxyFactoryBean類(lèi),底層自動(dòng)選擇使用JDK的動(dòng)態(tài)代理還是CGLIB的代理。
配置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" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.xsd"><!-- 定義目標(biāo)對(duì)象 --> <bean id="customerDao" class="com.js.aopStu.CustomerImpl"></bean> <!-- 定義增強(qiáng) --> <bean id="beforeAdice" class="com.js.aopStu.MyBeforeAdvice"></bean><!-- Spring支持配置來(lái)生成代理,基于ProxyFactoryBean類(lèi),底層自動(dòng)選擇使用JDK的動(dòng)態(tài)代理還是CGLIB的代理 --><bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 設(shè)置目標(biāo)對(duì)象 --><property name="target" ref="customerDao"></property><!-- 設(shè)置實(shí)現(xiàn)的接口,value中寫(xiě)接口的全路徑 --><property name="proxyInterfaces" value="com.js.aopStu.CustomerDao"></property><!-- 配置需要攔截的,一定是value,此處對(duì)customerDao中的所有方法攔截 --><property name="interceptorNames" value="beforeAdice"></property></bean></beans>?
我們需要配置一些屬性,不需要都設(shè)置。
lProxyFactoryBean常用可配置屬性 ?target : 代理的目標(biāo)對(duì)象 ?proxyInterfaces : 代理要實(shí)現(xiàn)的接口 ?如果多個(gè)接口可以使用以下格式賦值<list>
??? <value></value>
??? ....
</list>
?proxyTargetClass : 是否對(duì)類(lèi)代理而不是接口,設(shè)置為true時(shí),使用CGLib代理 ?interceptorNames : 需要織入目標(biāo)的Advice ?singleton : 返回代理是否為單實(shí)例,默認(rèn)為單例 ?optimize : 當(dāng)設(shè)置為true時(shí),強(qiáng)制使用CGLib (proxyInterfaces、proxyTargetClass二者互斥,不能同時(shí)存在)編寫(xiě)測(cè)試類(lèi):
package com.js.aopStu;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class TestAOPDemo1 {@Autowired@Qualifier("customerDaoProxy")private CustomerDao customerDao;//不使用增強(qiáng)的情況下 @Testpublic void Demo1(){customerDao.add();customerDao.delete();customerDao.find();customerDao.update();} }帶有切點(diǎn)的切面(常用)
用PointcutAdvisor實(shí)現(xiàn)類(lèi),它有兩個(gè)接口: 1、DefaultPointcutAdvisor:最常用的切面類(lèi)型,它可以通過(guò)任意Pointcut和Advice 組合定義切面 2、RegexpMethodPointcutAdvisor:構(gòu)造正則表達(dá)式切點(diǎn)切面,一般使用這種。帶有切點(diǎn)的切面實(shí)例
新建一個(gè)DAO,創(chuàng)建被代理對(duì)象:
package com.js.demo3; /*** 目標(biāo)對(duì)象* @author hdb**/ public class OrderDao {public void add() { System.out.println("添加訂單...");}public void delete() { System.out.println("刪除訂單...");}public void update() { System.out.println("修改訂單...");}public void find() {System.out.println("查詢(xún)訂單...");} }編寫(xiě)增強(qiáng)類(lèi),這次使用環(huán)繞增強(qiáng):
package com.js.demo3;import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation;/*** 增強(qiáng)的類(lèi)* 使用的是環(huán)繞增強(qiáng)* @author hbd**/ public class MyAroundAdvice implements MethodInterceptor{@Overridepublic Object invoke(MethodInvocation methodInvocation) throws Throwable {System.out.println("環(huán)繞前增強(qiáng)===");Object object=methodInvocation.proceed();//執(zhí)行目標(biāo)對(duì)象的方法System.out.println("環(huán)繞后增強(qiáng)===");return object;}}生成代理:通過(guò)配置的方式:
<?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:util="http://www.springframework.org/schema/util"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/util http://www.springframework.org/schema/util/spring-util.xsd"><!-- 帶有切點(diǎn)的切面 --> <!-- 定義目標(biāo)對(duì)象 --> <bean id="orderDao1" class="com.js.demo3.OrderDao"></bean><!-- 定義增強(qiáng) --> <bean id="aroundAdvice" class="com.js.demo3.MyAroundAdvice"></bean><!-- 定義切點(diǎn)切面: --> <bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><!-- 定義表達(dá)式,規(guī)定哪些方法執(zhí)行攔截 --><!-- . 任意字符 * 任意個(gè) --><!-- <property name="pattern" value=".*"/> --><!-- <property name="pattern" value="cn\.itcast\.spring3\.demo4\.OrderDao\.add.*"/> --><!-- <property name="pattern" value=".*add.*"></property> --><property name="patterns" value=".*add.*,.*find.*"></property><!-- 應(yīng)用增強(qiáng) --><property name="advice" ref="aroundAdvice"/> </bean><!-- 定義生成代理對(duì)象 --> <bean id="orderDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 配置目標(biāo) --><property name="target" ref="orderDao1"></property><!-- 針對(duì)類(lèi)的代理 --><property name="proxyTargetClass" value="true"></property><!-- 在目標(biāo)上應(yīng)用增強(qiáng) --><property name="interceptorNames" value="myPointcutAdvisor"></property> </bean></beans>編寫(xiě)測(cè)試類(lèi):
package com.js.demo3;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class TestAOPDemo1 {@Autowired@Qualifier("orderDaoProxy")private OrderDao orderDao;
@Testpublic void demo1(){orderDao.add();orderDao.delete();orderDao.find();orderDao.update();} }
自動(dòng)代理
前面的案例中,每個(gè)代理都是通過(guò)ProxyFactoryBean織入切面代理,在實(shí)際開(kāi)發(fā)中,非常多的Bean每個(gè)都配置ProxyFactoryBean開(kāi)發(fā)維護(hù)量巨大。
自動(dòng)創(chuàng)建代理(*****基于后處理Bean.在Bean創(chuàng)建的過(guò)程中完成的增強(qiáng).生成Bean就是代理。)
(1)BeanNameAutoProxyCreator根據(jù)Bean名稱(chēng)創(chuàng)建代理
(2)DefaultAdvisorAutoProxyCreator根據(jù)Advisor本身包含信息創(chuàng)建代理
*AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ 注解進(jìn)行自動(dòng)代理
之前我們手動(dòng)創(chuàng)建代理的時(shí)候,注意到一點(diǎn),都是先創(chuàng)建被代理對(duì)象,然后在創(chuàng)建代理的時(shí)候,傳入該被代理對(duì)象。
而下面要學(xué)習(xí)的兩種自動(dòng)代理,是基于后處理bean,在類(lèi)創(chuàng)建的過(guò)程中完成增強(qiáng),生成的bean,就是代理!
基于Bean名稱(chēng)的自動(dòng)代理
先清理配置文件中其他部分,留下最簡(jiǎn)潔的四個(gè)bean配置:
<?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:util="http://www.springframework.org/schema/util"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/util http://www.springframework.org/schema/util/spring-util.xsd"><!-- 定義目標(biāo)對(duì)象 --> <bean id="orderDao2" class="com.helloAOP.autoProxy.OrderDao"></bean> <bean id="customerDao2" class="com.helloAOP.autoProxy.CustomerImpl"></bean> <!-- 定義增強(qiáng) --> <bean id="aroundAdvice2" class="com.helloAOP.autoProxy.MyAroundAdvice"></bean> <bean id="beforeAdvice2" class="com.helloAOP.autoProxy.MyBeforeAdvice"></bean><!-- 自動(dòng)代理:按名稱(chēng)代理 基于后處理bean,后處理bean不需要配置ID--> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"><property name="beanNames" value="*Dao2"/><property name="interceptorNames" value="beforeAdvice2"/> </beans>
編寫(xiě)測(cè)試類(lèi):
package com.helloAOP.autoProxy;import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestAutoProxyByName {@Testpublic void testDemo(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");OrderDao orderDao = (OrderDao)applicationContext.getBean("orderDao2");orderDao.add();orderDao.update();orderDao.delete();orderDao.find();applicationContext.close();} }運(yùn)行測(cè)試:
?
基于AspectJ注解信息的自動(dòng)代理
先清理配置文件中其他部分,留下最簡(jiǎn)潔的四個(gè)bean配置:
<?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:util="http://www.springframework.org/schema/util"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/util http://www.springframework.org/schema/util/spring-util.xsd"><!-- 定義目標(biāo)對(duì)象 --> <bean id="orderDao3" class="com.helloAOP.autoProxy.OrderDao"></bean> <bean id="customerDao3" class="com.helloAOP.autoProxy.CustomerImpl"></bean> <!-- 定義增強(qiáng) --> <bean id="aroundAdvice3" class="com.helloAOP.autoProxy.MyAroundAdvice"></bean> <bean id="beforeAdvice3" class="com.helloAOP.autoProxy.MyBeforeAdvice"></bean> <!-- 定義一個(gè)帶有切點(diǎn)的切面 --> <bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><property name="pattern" value=".*add.*"></property><property name="advice" ref="aroundAdvice3"></property></bean> <!-- 自動(dòng)代理:--> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean> </beans>編寫(xiě)測(cè)試類(lèi):
package com.helloAOP.autoProxy;import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestAutoProxyByAspectJ {@Testpublic void testDemo(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");OrderDao orderDao = (OrderDao)applicationContext.getBean("orderDao3");orderDao.add();orderDao.delete();applicationContext.close();}}運(yùn)行測(cè)試:
AspectJ方面
AspectJ [??spekt] 是一個(gè)基于Java語(yǔ)言的AOP框架 Spring2.0以后新增了對(duì)AspectJ切點(diǎn)表達(dá)式支持 @AspectJ是AspectJ1.5新增功能,通過(guò)JDK5注解技術(shù),允許直接在Bean類(lèi)中定義切面 新版本Spring框架,建議使用AspectJ方式來(lái)開(kāi)發(fā)AOP 通過(guò)配置啟用@AspectJ切面: <?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:util="http://www.springframework.org/schema/util"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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 開(kāi)啟AspectJ自動(dòng)代理--> <aop:aspectj-autoproxy/>AspectJ表達(dá)式:
語(yǔ)法:execution(表達(dá)式)
execution(<訪(fǎng)問(wèn)修飾符>?<返回類(lèi)型><方法名>(<參數(shù)>)<異常>)
execution(“* cn.itcast.spring3.demo1.dao.*(..)”)?????? ---只檢索當(dāng)前包
execution(“* cn.itcast.spring3.demo1.dao..*(..)”)????? ---檢索包及當(dāng)前包的子包.
execution(* cn.itcast.dao.GenericDAO+.*(..))?????????? ---檢索GenericDAO及子類(lèi)
?
AspectJ增強(qiáng):
@Before 前置通知,相當(dāng)于BeforeAdvice
@AfterReturning 后置通知,相當(dāng)于AfterReturningAdvice
@Around 環(huán)繞通知,相當(dāng)于MethodInterceptor
@AfterThrowing 拋出通知,相當(dāng)于ThrowAdvice
@After 最終final通知,不管是否異常,該通知都會(huì)執(zhí)行
@DeclareParents 引介通知,相當(dāng)于IntroductionInterceptor (不要求掌握)
下面詳細(xì)介紹詳細(xì)的使用方法(基于注解、基于XML),并做舉例說(shuō)明。
基于AspectJ的注解開(kāi)發(fā)
第一步:引入相應(yīng)jar包
使用AspectJ需要導(dǎo)入SpringAOP和AspectJ相關(guān)jar包:
1、spring-aop-3.2.7.RELEASE.jar
2、aopalliance-1.0.jar
3、spring-aspects-3.2.7.RELEASE.jar
4、aspectj.weaver-1.6.8.jar
第二步:編寫(xiě)被增強(qiáng)的類(lèi),即目標(biāo)類(lèi)
package com.js.demo8; /*** 目標(biāo)類(lèi)* @author hdb**/ public class UserDao {public void add(){System.out.println("add...");}public int update(){System.out.println("update...");return 1;}public void delete(){System.out.println("delete...");}public void find(){System.out.println("find...");int d=1/0; } }第三步:編寫(xiě)切面類(lèi),即切點(diǎn)和增強(qiáng)的結(jié)合
package com.js.demo8;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;/*** 切面類(lèi):就是切點(diǎn)與增強(qiáng)的結(jié)合* @Aspect:用于定義切面* @Before:用于定義xx增強(qiáng),只使用其value屬性時(shí),value可以省略,其值為表達(dá)式* 表示你想對(duì)哪些類(lèi)進(jìn)行增強(qiáng),可精確到某包、某類(lèi)、某方法,控制靈活* @author hdb**/@Aspect public class MyAspect {@Before(value="execution(* com.js.demo8.UserDao.add(..))")public void before(){System.out.println("前置增強(qiáng)===");}@AfterReturning(value="execution(* com.js.demo8.UserDao.update(..))")public void afterReturnin(){System.out.println("后置增強(qiáng)===");}@Around(value="execution(* com.js.demo8.UserDao.update(..))")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{System.out.println("環(huán)繞前增強(qiáng)===");Object object=proceedingJoinPoint.proceed(); //調(diào)用目標(biāo)方法System.out.println("環(huán)繞后增強(qiáng)===");return object;}@AfterThrowing(value="MyAspect.myPointcut()",throwing="e")public void afterThrowing(Throwable e){System.out.println("不好了,出異常了==="+e.getMessage());}@After(value="MyAspect.myPointcut()")public void after(){System.out.println("最終通知===");}@Pointcut("execution(* com.js.demo1.UserDao.find(..))")private void myPointcut(){}}關(guān)于切點(diǎn)的注解,切點(diǎn)是我們的增強(qiáng)最重要應(yīng)用的方法。為什么要定義一個(gè)切點(diǎn)呢?因?yàn)槲覀兩鲜鲩_(kāi)發(fā)中,很多通知的value表達(dá)式都是重復(fù)的,在實(shí)際開(kāi)發(fā)中,每寫(xiě)一個(gè)通知,就要去寫(xiě)一個(gè)表達(dá)式,很繁瑣。所以我們可以采用定義切點(diǎn)的方式來(lái)解決。例如上面的定義一個(gè)myPointcut切點(diǎn),可以重復(fù)調(diào)用:
@Pointcut("execution(* com.js.demo1.UserDao.find(..))")private void myPointcut(){}第四步:在applicationContext.xml中引入約束,配置bean,并開(kāi)啟自動(dòng)代理
<?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:util="http://www.springframework.org/schema/util"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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 開(kāi)啟AspectJ自動(dòng)生成代理,底層就是AnnotationAwareAspectJAutoProxyCreator--> <aop:aspectj-autoproxy/> <bean id="userDao" class="com.js.demo8.UserDao"></bean> <bean id="myAspect" class="com.js.demo8.MyAspect"></bean> </beans>
第五步:編寫(xiě)測(cè)試類(lèi)
package com.js.demo8;/*** 測(cè)試類(lèi)*/ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class TestAspectDemo {@Autowired@Qualifier("userDao")private UserDao userDao; @Testpublic void demo1(){userDao.add();userDao.delete();userDao.find();userDao.update();} }第六步:運(yùn)行測(cè)試,查看結(jié)果。
基于AspectJ的XML配置方式開(kāi)發(fā)
第一步:引入相應(yīng)jar包
使用AspectJ需要導(dǎo)入SpringAOP和AspectJ相關(guān)jar包:
1、spring-aop-3.2.7.RELEASE.jar
2、aopalliance-1.0.jar
3、spring-aspects-3.2.7.RELEASE.jar
4、aspectj.weaver-1.6.8.jar
第二步:編寫(xiě)被增強(qiáng)的類(lèi),即目標(biāo)類(lèi)
package com.js.demo9;public class ProductDao {public void add(){System.out.println("add...");}public void update(){System.out.println("update...");}public void delete(){System.out.println("delete...");int a = 1/0;}public String find(){System.out.println("find...");return "find Success!";} }
第三步:編寫(xiě)切面類(lèi),即切點(diǎn)和增強(qiáng)的結(jié)合
package com.js.demo9;import org.aspectj.lang.ProceedingJoinPoint;/*** 切面類(lèi)* @author hdb**/ public class MyAspectXML {public void before(){System.out.println("前置通知===");}public void after(Object returnVal){System.out.println("后置通知===返回值:"+returnVal);}public Object around(ProceedingJoinPoint point) throws Throwable{System.out.println("環(huán)繞前通知===");Object object = point.proceed();System.out.println("環(huán)繞后通知===");return object;}public void afterthrowing(Throwable e){System.out.println("糟糕,出錯(cuò)了===錯(cuò)誤信息:"+e.getMessage());}public void afterFinal(){System.out.println("最終通知===");} }?第四步:在applicationContext.xml中引入約束,配置bean,并開(kāi)啟自動(dòng)代理
<?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:util="http://www.springframework.org/schema/util"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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 定義被增強(qiáng)的類(lèi) --> <bean id="productDao" class="com.js.demo9.ProductDao"> </bean> <!-- 定義切面類(lèi) --> <bean id="myAspectXML" class="com.js.demo9.MyAspectXML"></bean> <!-- 定義aop的配置 --> <aop:config> <!-- 定義切點(diǎn) --> <aop:pointcut expression="execution(* com.js.demo9.ProductDao.add(..))" id="myPointcut"/> <aop:pointcut expression="execution(* com.js.demo9.ProductDao.find(..))" id="myAfter"/> <aop:pointcut expression="execution(* com.js.demo9.ProductDao.update(..))" id="myAround"/><aop:pointcut expression="execution(* com.js.demo9.ProductDao.delete(..))" id="myafterthrowing"/> <aop:aspect ref="myAspectXML">
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="myPointcut"/>
<!-- 后置通知 -->
<aop:after-returning method="after" pointcut-ref="myAfter" returning="returnVal"/>
<!-- 環(huán)繞通知 -->
<aop:around method="around" pointcut-ref="myAround"/>
第五步:編寫(xiě)測(cè)試類(lèi)
package com.js.demo9;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class TestAspectXML {@Autowired@Qualifier("productDao")private ProductDao productDao;@Testpublic void demo(){productDao.add();productDao.find();productDao.update();productDao.delete();} }第六步:運(yùn)行測(cè)試,查看結(jié)果。
?參考鏈接:https://blog.csdn.net/dove_knowledge/article/category/6818451
https://my.oschina.net/pingpangkuangmo/blog/517340
? ??https://my.oschina.net/huangyong/blog/161338
? ? ? ? ? ? ? ? ??https://www.cnblogs.com/best/p/5736422.html
轉(zhuǎn)載于:https://www.cnblogs.com/huangdabing/p/9484349.html
總結(jié)
以上是生活随笔為你收集整理的Spring AOP学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 第十三章:位图(三)
- 下一篇: NFS的搭建及配置