Spring AOP學習
什麽是AOP
AOP 面向切面編程
采取橫向抽取機制,取代了傳統縱向繼承體系重復性代碼(性能監視、事務管理、安全檢查、緩存)
Spring AOP使用純Java實現,不需要專門的編譯過程和類加載器,在運行期通過代理方式向目標類織入增強代碼
AspecJ是一個基於Java語言的AOP框架,Spring2.0開始,Spring AOP引入對Aspect的支持,AspectJ擴展了Java語言,提供了一個專門的編譯器,在編譯時提供橫向代碼的織入
AOP底層原理
就是代理機制:
* 動態代理:(JDK中使用)
* JDK的動態代理,對實現了接口的類生成代理.
Spring的AOP代理
JDK動態代理:對實現了接口的類生成代理。沒有實現接口的類,就無法生成代理對象了。
CGLib代理機制:對類生成代理
結論:Spring框架,如果類實現了接口,就使用JDK的動態代理生成代理對象,如果這個類沒有實現任何接口,使用CGLIB生成代理對象.
AOP的術語
Joinpoint(連接點):所謂連接點是指那些被攔截到的點。在spring中,這些點指的是方法,因為spring只支持方法類型的連接點.
Pointcut(切入點):所謂切入點是指我們要對哪些Joinpoint進行攔截的定義.
Advice(通知/增強):所謂通知是指攔截到Joinpoint之後所要做的事情就是通知.通知分為前置通知,後置通知,異常通知,最終通知,環繞通知
Introduction(引介):引介是一種特殊的通知在不修改類代碼的前提下, Introduction可以在運行期為類動態地添加一些屬性或方法.
Target(目標對象):代理的目標對象
Weaving(織入):是指把增強應用到目標對象來創建新的代理對象的過程.
spring采用動態代理織入,而AspectJ采用編譯期織入和類裝在期織入
Proxy(代理):一個類被AOP織入增強後,就產生一個結果代理類
Aspect(切面):是切入點和通知(引介)的結合
不帶有切點的切面
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 { @Override public void add() { System.out.println("添加客戶..."); } @Override public void delete() { System.out.println("刪除客戶..."); } @Override public void update() { System.out.println("修改客戶..."); } @Override public 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:目標對象 */ @Override public 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; //不使用增強的情況下 @Test public 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{ @Override public 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;
@Test public void demo1(){ orderDao.add(); orderDao.delete(); orderDao.find(); orderDao.update(); } }
Spring AOP學習