1. 程式人生 > >SpringAop-1【傳統的aop】

SpringAop-1【傳統的aop】

學習目標

1.spring中的傳統aop,advisor【瞭解】:都是有一個切點和一個通知組合

    不帶有切點的切面【針對所有方法增強】

    帶有切點的切面【針對某些方法】

    升級版:自動代理

            1.按名稱生成自動代理
            2.根據切面中的意義生成自動代理
2.基於AspectJ【要求掌握】:多個切點和多個切面組合
    基於註解形式
    基於xml形式

什麼是Aop

Aop Aspect Oriented Progaming 面向切面程式設計

Aop採取橫向抽取機制,取代了傳統縱向繼承體系重複性程式碼(效能檢測、事務管理、安全檢查)

 Spring Aop使用純Java實現,不需要專門的編譯和類載入器,在執行期通過代理方式向目標類織入增強的程式碼

AspectJ是一個基於java語音的Aop框架,Spring2.0開始,springAop 引入對AspectJ的支援

Aop底層原理

代理機制,分為JDK動態代理,實現了介面的類生成代理;CGLIB代理機制,通過生成一個虛擬的父類來實現代理

Aop的術語

Joinpoint(連線點):所謂連線點是指那些被攔截到的點,在spring中 指的是方法。

Pointcut(切入點):所謂切入點是指我們要對哪些Joinpoint(連線點)進行攔截。

Advice(通知/增強):所謂通知就是對切入點進行增強。分為:前置通知、後置通知、異常通知、最終通知、環繞通知。

Introduction(引介):引介是一種特殊的通知在不修改類程式碼的前提下, Introduction可以在執行期為類動態地新增一些方法或Field.

Target(目標物件):代理的目標物件。

Weaving(織入):是指把增強應用到目標物件來建立新的代理物件的過程。【spring採用動態代理織入,aspectJ採用編譯器織入和類裝載期織入】

Proxy(代理):一個類被Aop織入增強後,就產生一個結果代理類

Aspect(切面):是切入點和通知的結合。

通知型別

前置通知 org.springframework.aop.MethodBeforeAdvice

* 在目標方法執行前實施增強

後置通知 org.springframework.aop.AfterReturningAdvice

* 在目標方法執行後實施增強

環繞通知 org.aopalliance.intercept.MethodInterceptor

* 在目標方法執行前後實施增強

異常丟擲通知 org.springframework.aop.ThrowsAdvice

* 在方法丟擲異常後實施增強

 不帶有切點的切面

第一步:匯入相對應的架包

spring-beans-3.2.0.RELEASE.jar

spring-context-3.2.0.RELEASE.jar

spring-core-3.2.0.RELEASE.jar

spring-expression-3.2.0.RELEASE.jar

spring-aop-3.2.0.RELEASE.jar

開發的日誌記錄的包:

com.springsource.org.apache.commons.logging-1.1.1.jar        --- 用於整合其他的日誌的包(類似Hibernate中slf4j)

com.springsource.org.apache.log4j-1.2.15.jar

*com.springsource.org.aopalliance-1.0.0.jar

第二步:編寫被代理的類

 UserDao.java

public interface CustomerDao {
	void mai();
}

UserDaoImpl.java

public class CustomerDaoImpl implements CustomerDao{

	@Override
	public void mai() {
		System.out.println("買東西");
	}

}

第三步:編寫增強的程式碼:

/**
 * 
 * @author yj
 * @時間:2018-9-6 下午03:06:02
 * @description:前置增強
 */
public class MyAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method method, Object[] arg1, Object real)
			throws Throwable {
		System.out.println("砍價");
	}
	
}

第四步:生成代理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- 不帶有切點的切面 -->
	<!-- 定義目標物件 -->
	<bean id="customerDao" class="cn.hifits.spring.aop.CustomerDaoImpl"></bean>
	<!-- 定義增強 -->
	<bean id="beforeAdvice" class="cn.hifits.spring.aop.MyAdvice"></bean>

	<!-- Spring支援配置生成代理: -->
	<bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 設定目標物件 -->
		<property name="target" ref="customerDao"/>
		<!-- 設定實現的介面 ,value中寫介面的全路徑 -->
		<property name="proxyInterfaces" value="cn.hifits.spring.aop.CustomerDao"/>
		<!-- 需要使用value:要的名稱 -->
		<property name="interceptorNames" value="beforeAdvice"/>
	</bean>
</beans>

ProxyFactoryBean說明:

生成代理Spring基於ProxyFactoryBean類.底層自動選擇使用JDK的動態代理還是CGLIB的代理.

 屬性:

target : 代理的目標物件

proxyInterfaces : 代理要實現的介面

如果多個介面可以使用以下格式賦值

<list>

    <value></value>

    ....

</list>

proxyTargetClass : 是否對類代理而不是介面,設定為true時,使用CGLib代理

interceptorNames : 需要織入目標的Advice

singleton : 返回代理是否為單例項,預設為單例

optimize : 當設定為true時,強制使用CGLib

測試類

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestFunction {
	@Autowired
	@Qualifier("customerDaoProxy")//注入的是代理物件
	private CustomerDao customerDao;
	@Test
	public void demo1(){
		customerDao.mai();
		
	}
}

 帶有切點的切面(針對目標中的某些方法增強)

第一步:建立被代理的物件

public class OrderDao {
	public void add(){
		System.out.println("增加訂單");
	}
	public void delete(){
		System.err.println("刪除訂單");
	}
}

第二步:編寫增強的程式碼

public class MyAroundAdvice implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		System.out.println("環繞前通知");
		Object result=arg0.proceed();
		System.out.println("環繞後增強");
		return result;
	}
}

第三步:生成代理

	<!-- 帶有切點的切面 -->
	<bean id="orderDao" class="cn.hifits.spring.aop1.OrderDao"></bean>
	<bean id="MyAroundAdvice" class="cn.hifits.spring.aop1.MyAroundAdvice"></bean>
	
	<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="patterns" value="cn.hifits.spring.aop1.OrderDao.add.*"></property>
		<property name="advice" ref="MyAroundAdvice"></property>
	</bean>
	
	<bean id="orderDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target" ref="orderDao"></property>
		<property name="proxyTargetClass" value="true"></property>
		<property name="interceptorNames" value="myPointcutAdvisor"></property>
	</bean>

測試類

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestFunction {
	@Autowired
	@Qualifier("orderDaoProxy")// 注入是真實的物件,必須注入代理物件.
	public OrderDao obj;
	@Test
	public void demo1(){
		obj.add();
		obj.delete();
	}
}

自動代理

前面的案例中,每個代理都是通過ProxyFactoryBean織入切面代理,在實際開發中,非常多的Bean每個都配置ProxyFactoryBean開發維護量巨大

自動建立代理(*****基於後處理Bean.在Bean建立的過程中完成的增強.生成Bean就是代理.)

BeanNameAutoProxyCreator 根據Bean名稱建立代理

DefaultAdvisorAutoProxyCreator 根據Advisor本身包含資訊建立代理

AnnotationAwareAspectJAutoProxyCreator 基於Bean中的AspectJ 註解進行自動代理

BeanNameAutoProxyCreator :按名稱生成代理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- 增強的程式碼 -->
	<bean id="MyAdvice" class="cn.hifits.spring.aop.MyAdvice"></bean>
	<!-- 後處理bean 生成代理 -->
	<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="beanNames" value="*Dao"></property>
		<property name="interceptorNames" value="MyAdvice"></property>
	</bean>
	<!-- 定義目標物件 -->
	<bean id="OrderDao" class="cn.hifits.spring.aop1.OrderDao"></bean>
	<bean id="CustomerDao" class="cn.hifits.spring.aop.CustomerDaoImpl"></bean>

</beans>

 DefaultAdvisorAutoProxyCreator :根據切面中定義的資訊生成代理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- 增強的程式碼 -->
	<bean id="MyAdvice" class="cn.hifits.spring.aop.MyAdvice"></bean>
	<!--  定義一個帶有切點的切面  -->
	<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="pattern" value=".*add.*"/>
		<property name="advice" ref="MyAdvice"/>
	</bean>
	<!-- 自動生成代理 -->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
	<!-- 定義目標物件 -->
	<bean id="OrderDao" class="cn.hifits.spring.aop1.OrderDao"></bean>
	<bean id="CustomerDao" class="cn.hifits.spring.aop.CustomerDaoImpl"></bean>

</beans>

測試類

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class TestFunction {
	@Autowired
	@Qualifier("OrderDao")
//在Bean的生成過程中,就產生了代理物件   跟前面的不一樣,前面的是注入代理物件
	public OrderDao obj;
	
	@Autowired
	@Qualifier("CustomerDao")
	public CustomerDao cdao;
	@Test
	public void demo1(){
		obj.add();
		obj.delete();
		cdao.mai();
	}
}

ProxyFattoryBean和自動代理的區別:

roxyFactoryBean:先有被代理物件,將被代理物件傳入到代理類中生成代理.

自動代理基於後處理Bean.在Bean的生成過程中,就產生了代理物件,把代理物件返回.生成Bean已經是代理物件.