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已經是代理物件.