標籤:ati 基礎 work ica public 動態代理 異常 color turn
什麼是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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://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:構造
Regex切點切面,
一般使用這種。帶有切點的切面執行個體
建立一個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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://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學習