1. 程式人生 > >Spring_第三章【Spring的AOP】

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查詢方法