Spring_第三章【Spring的AOP】
1:什麼是Spring的aop
AOP(Aspect Oriented Programming),即面向切面程式設計,可以說是OOP(Object Oriented Programming,面向物件程式設計)的補充和完善。OOP引入封裝、繼承、多型等概念來建立一種物件層次結構,用於模擬公共行為的一個集合。不過OOP允許開發者定義縱向的關係,但並不適合定義橫向的關係,例如日誌功能。日誌程式碼往往橫向地散佈在所有物件層次中,而與它對應的物件的核心功能毫無關係對於其他型別的程式碼,如安全性、異常處理和透明的持續性也都是如此,這種散佈在各處的無關的程式碼被稱為橫切(cross cutting),在OOP設計中,它導致了大量程式碼的重複,而不利於各個模組的重用。
AOP技術恰恰相反,它利用一種稱為"橫切"的技術,剖解開封裝的物件內部,並將那些影響了多個類的公共行為封裝到一個可重用模組,並將其命名為"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻為業務模組所共同呼叫的邏輯或責任封裝起來,便於減少系統的重複程式碼,降低模組之間的耦合度,並有利於未來的可操作性和可維護性。
使用"橫切"技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如許可權認證、日誌、事物。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。(例如在使用者購買之前檢查是否登入,使用者修改資料是否是管理員的等等需求上面,將核心關注點和和切關注點拆分出來)
AOP核心概念
1、橫切關注點
對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之為橫切關注點
2、切面(aspect)
類是對物體特徵的抽象,切面就是對橫切關注點的抽象
3、連線點(joinpoint)
被攔截到的點,因為Spring只支援方法型別的連線點,所以在Spring中連線點指的就是被攔截到的方法,實際上連線點還可以是欄位或者構造器
4、切入點(pointcut)
對連線點進行攔截的定義
5、通知(advice)
所謂通知指的就是指攔截到連線點之後要執行的程式碼,通知分為前置、後置、異常、最終、環繞通知五類
6、目標物件
代理的目標物件
7、織入(weave)
將切面應用到目標物件並導致代理物件建立的過程
8、引入(introduction)
在不修改程式碼的前提下,引入可以在執行期為類動態地新增一些方法或欄位
Spring對AOP的支援
Spring中AOP代理由Spring的IOC容器負責生成、管理,其依賴關係也由IOC容器負責管理。因此,AOP代理可以直接使用容器中的其它bean例項作為目標,這種關係可由IOC容器的依賴注入提供。Spring建立代理的規則為:
1、預設使用Java動態代理來建立AOP代理,這樣就可以為任何介面例項建立代理了
2、當需要代理的類不是代理介面的時候,Spring會切換為使用CGLIB代理,也可強制使用CGLIB
AOP程式設計其實是很簡單的事情,縱觀AOP程式設計,程式設計師只需要參與三個部分:
1、定義普通業務元件
2、定義切入點,一個切入點可能橫切多個業務元件
3、定義增強處理,增強處理就是在AOP框架為普通業務元件織入的處理動作
2:實現Spring的aop
spring實現AOP主要有兩種方式
第一種:Spring自身經典的AOP
第二種:AspectJ實現AOP
2.1.1 Spring自身經典的AOP(連線點都當做切點(攔截每一個方法))
介面
public interface studentdao {
public void add();
public void select();
}
介面實現
public class studentdaoImpl implements studentdao{
public void add() {
// TODO Auto-generated method stub
System.out.println("student新增方法");
}
public void select() {
// TODO Auto-generated method stub
System.out.println("student查詢方法");
}
}
通知方法
public class BeforeAdvice implements MethodBeforeAdvice{
public void before(Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("方法執行之前!");
}
}
配置檔案
此配置檔案為傳統aop全部攔截
<bean id="studentdao" class="com.thit.dao.studentdaoImpl"></bean>
<bean id="BeforeAdvice" class="com.thit.dao.BeforeAdvice"></bean>
Spring aop產生代理物件
<bean id="studentProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
目標類來自ProxyFactoryBean 的屬性
<property name="target" ref="studentdao"></property>
實現介面
<property name="proxyInterfaces" value="com.thit.dao.studentdao"></property>
採用攔截的名稱
<property name="interceptorNames" value="BeforeAdvice"/>
強制使用cglib
<property name="optimize" value="true"></property>
</bean>
測試程式碼
public static void main(String[] args) {
test1();
}
static void test1(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
studentdao dao=(studentdao) applicationContext.getBean("studentProxy");
dao.add();
System.out.println("----------");
dao.select();
}
輸出結果是:
環繞方法之前
student新增方法
------------------
環繞方法之前
student查詢方法
2.1.2 Spring自身經典的AOP(制定攔截方法)
介面和介面實現如上所示
通知方法
//環繞通知
public class AroundAdvice implements MethodInterceptor{
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("環繞方法之前");
//執行目標方法
Object o=invocation.proceed();
System.out.println("環繞方法之後");
return o;
}
}
配置檔案新增
<!--目標類 -->
<bean id="studentdao" class="com.thit.dao.studentdaoImpl"></bean>
<!-- 環繞通知 -->
<bean id="AroundAdvice" class="com.thit.dao.AroundAdvice"></bean>
<!-- 配置切入點 .-->
<bean id="AroundAdvice123" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="patterns" value=".*ad.*"></property>
<property name="advice" ref="AroundAdvice"></property>
</bean>
<!--Spring aop產生代理物件-->
<bean id="studentProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--目標類來自ProxyFactoryBean 的屬性 -->
<property name="target" ref="studentdao"></property>
<!-- 實現介面 -->
<property name="proxyInterfaces" value="com.thit.dao.studentdao"></property>
<!-- 採用攔截的名稱 -->
<property name="interceptorNames" value="AroundAdvice123"/>
</bean>
測試方法
public static void main(String[] args) {
test1();
}
static void test1(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
studentdao dao=(studentdao) applicationContext.getBean("studentProxy");
dao.add();
System.out.println("----------");
dao.select();
}
執行結果:
環繞方法之前
student新增方法
環繞方法之後
----------
student查詢方法
在此思考,隨著需要增強的目標類的增多,例如隨著dao的增加,每一個dao都需要增加切點,springAOP產生的代理物件會增加
那麼配置檔案需要不斷增加,以為每一個代理都是通過org.springframework.aop.framework.ProxyFactoryBean織入切面代理的,導致ProxyFactoryBean維護變大,所以引進動態代理。
動態代理方法一:基於baan名稱的自動代理方式 BeanNameAutoProxyCreator
配置檔案新增
<!-- 傳統aop 全部攔截 -->
<!--目標類 -->
<bean id="studentdao" class="com.thit.dao.studentdaoImpl"></bean>
<!-- 環繞通知 -->
<bean id="AroundAdvice" class="com.thit.dao.AroundAdvice"></bean>
<!-- 配置切入點 .*ad.*,.*select.*-->
<bean id="AroundAdvice123" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="patterns" value=".*ad.*"></property>
<property name="advice" ref="AroundAdvice"></property>
</bean>
<bean id="studentProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*dao1"></property>
<property name="interceptorNames" value="AroundAdvice123"></property>
</bean>
測試方法
void test2(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
studentdao dao=(studentdao) applicationContext.getBean("studentdao");
dao.add();
System.out.println("----------");
dao.select();
}
輸出結果
環繞方法之前
student新增方法
環繞方法之後
----------
student查詢方法
動態代理方法二:基於切面資訊的自動代理方式 DefaultAdvisorAutoProxyCreator
配置檔案新增
<!--目標類 -->
<bean id="studentdao" class="com.thit.dao.studentdaoImpl"></bean>
<!-- 環繞通知 -->
<bean id="AroundAdvice" class="com.thit.dao.AroundAdvice"></bean>
<!-- 配置切入點 .*ad.*,.*select.*-->
<bean id="AroundAdvice123" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="patterns" value=".*ad.*"></property>
<property name="advice" ref="AroundAdvice"></property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
測試程式碼如下
void test2(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
studentdao dao=(studentdao) applicationContext.getBean("studentdao");
dao.add();
System.out.println("----------");
dao.select();
}
輸出結果:
環繞方法之前
student新增方法
環繞方法之後
----------
student查詢方法