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