1. 程式人生 > >Spring中的AOP(8)

Spring中的AOP(8)

Spring中的AOP

Spring的傳統AOP

AOP:不是由Spring定義.AOP聯盟的組織定義.
Spring中的通知:(增強代碼)

  • 前置通知 org.springframework.aop.MethodBeforeAdvice
    * 在目標方法執行前實施增強

  • 後置通知 org.springframework.aop.AfterReturningAdvice
    * 在目標方法執行後實施增強
  • 環繞通知 org.aopalliance.intercept.MethodInterceptor
    * 在目標方法執行前後實施增強
  • 異常拋出通知 org.springframework.aop.ThrowsAdvice
    * 在方法拋出異常後實施增強
  • 引介通知 org.springframework.aop.IntroductionInterceptor(課程不講.)
    * 在目標類中添加一些新的方法和屬性

Spring中的切面類型

  • Advisor : Spring中傳統切面.
    * Advisor:都是有一個切點和一個通知組合.
    * Aspect:多個切點和多個通知組合.

  • Advisor : 代表一般切面,Advice本身就是一個切面,對目標類所有方法進行攔截(* 不帶有切點的切面.針對所有方法進行攔截)
  • PointcutAdvisor : 代表具有切點的切面,可以指定攔截目標類哪些方法(帶有切點的切面,針對某個方法進行攔截)
  • IntroductionAdvisor : 代表引介切面,針對引介通知而使用切面(不要求掌握)

Spring的AOP的開發

針對所有方法的增強:(不帶有切點的切面)

  • 第一步:導入相應jar包.
    * spring-aop-3.2.0.RELEASE.jar
    * com.springsource.org.aopalliance-1.0.0.jar
  • 第二步:編寫被代理對象:
    * CustomerDao接口
package cn.spring3.demo3;
/*接口類
*/
public interface CustomerDao {
    public void add();
    public void update();
    public void delete();
    public void find();
}

* CustoemrDaoImpl實現類

package cn.spring3.demo3;

/**
 * @author NOP
 * 針對所有方法的增強
 */
public class CustomerDaoImpl implements CustomerDao {
    public void add() {
        System.out.println("添加客戶");
    }
    public void delete() {
        System.out.println("刪除客戶");
    }
    public void find() {
        System.out.println("查詢客戶");
    }
    public void update() {
        System.out.println("修改客戶");
    }
}
  • 第三步:編寫增強的代碼:
package cn.spring3.demo3;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
 * @author NOP
 * 前置增強
 */
public class MyBeforeAdvice implements MethodBeforeAdvice{
    /**
     * method:執行的方法
     * args:參數
     * target:目標對象
     */
    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("前置增強...");
    }
}
  • 第四步:生成代理:(配置生成代理:)

* 生成代理Spring基於ProxyFactoryBean類.底層自動選擇使用JDK的動態代理還是CGLIB的代理.
* 屬性:
target : 代理的目標對象
proxyInterfaces : 代理要實現的接口
如果多個接口可以使用以下格式賦值
<list>
<value></value>
....
</list>
proxyTargetClass : 是否對類代理而不是接口,設置為true時,使用CGLib代理
interceptorNames : 需要織入目標的Advice
singleton : 返回代理是否為單實例,默認為單例
optimize : 當設置為true時,強制使用CGLib

<!-- 傳統方式代理 -->
    <!-- 不帶有切點的切面 -->
    <!-- 目標對象 -->
    <bean id="customerDao" class="cn.spring3.demo3.CustomerDaoImpl"></bean>

    <!-- 定義增強 -->
    <bean id="beforeAdvice" class="cn.spring3.demo3.MyBeforeAdvice"></bean>

    <!-- Srping配置增強代理 -->
    <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 設置目標對象 -->
        <property name="target" ref="customerDao"></property>
        <!-- 設置實現的接口 ,value中寫接口的全路徑-->
        <property name="proxyInterfaces" value="cn.spring3.demo3.CustomerDao"></property>
        <!-- 需要使用的value:要的名稱 -->
        <property name="interceptorNames" value="beforeAdvice"></property>
        <!-- 強制使用CGLib代理 
        <property name="optimize" value="true"/>
        -->
    </bean>
    <!-- 不帶有切點的切面 -->

第五步:編寫測試類

package cn.spring3.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 SpringTest3 {

    @Autowired
    //@Qualifier("customerDao")//註入的真實的對象,必須註入代理對象
    @Qualifier("customerDaoProxy")
    private CustomerDao customerDao;
    /*
     * 不帶有切點的切面
     */
    @Test
    public void demo1(){
        customerDao.add();
        customerDao.delete();
        customerDao.find();
        customerDao.update();
    }
}
測試結果:
前置增強...
添加客戶
前置增強...
刪除客戶
前置增強...
查詢客戶
前置增強...
修改客戶

帶有切點的切面:(針對目標對象的某些方法進行增強)

  • PointcutAdvisor 接口:
    • DefaultPointcutAdvisor 最常用的切面類型,它可以通過任意Pointcut和Advice 組合定義切面
    • RegexpMethodPointcutAdvisor 構造正則表達式切點切面

第一步:創建被代理對象.
* OrderDao

package cn.spring3.demo4;
/**
 * @author NOP
 * 目標對象
 */
public class OrderDao {
    public void add() {
        // TODO Auto-generated method stub
        System.out.println("添加訂單");
    }
    public void delete() {
        // TODO Auto-generated method stub
        System.out.println("刪除訂單");
    }
    public void find() {
        // TODO Auto-generated method stub
        System.out.println("查詢訂單");
    }
    public void update() {
        // TODO Auto-generated method stub
        System.out.println("修改訂單");
    }
}

第二步:編寫增強的類:

package cn.spring3.demo4;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * @author NOP 增強的類 使用的是環繞
 */
public class MyAroundAdvice implements MethodInterceptor {
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("環繞前增強。。。");
        Object result = methodInvocation.proceed();// 執行目標對象的方法
        System.out.println("環繞後增強。。。");
        return result;
    }
}

第三步:生成代理:

<!-- 帶有切點的切面 -->
    <!-- 目標對象 -->
    <bean id="orderDao" class="cn.spring3.demo4.OrderDao"></bean>
    <!-- 定義增強 -->
    <bean id="aroundAdvice" class="cn.spring3.demo4.MyAroundAdvice"></bean>
    <!-- ********************************定義切點*************** -->
    <bean id="mypointcutAdviesor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--定義正則表達式,規定哪些方法執行攔截  pattern是一個方法patterns是多個方法-->
        <!-- <property name="pattern" value=".*"/> 這個正則是所有-->
        <!--<property name="pattern" value="cn\.spring3\.demo4\.OrderDao\.delete.*"/>-->
        <!--<property name="pattern" value=".*add.*"/>-->
        <property name="patterns" value=".*add.*,.*update.*"/>
        <!-- 應用增強 -->
        <property name="advice" ref="aroundAdvice"/>
    </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="mypointcutAdviesor"></property>
    </bean>
    <!-- 帶有切點的切面 -->

第四步:編寫測試類

package cn.spring3.demo4;

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 SpringTest1 {
    @Autowired
    //@Qualifier("orderDao")//註入的真實的對象,必須註入代理對象
    @Qualifier("orderDaoProxy")
    private OrderDao orderDao;
    /*
     * 帶有切點的切面
     */
    @Test
    public void demo1(){
        orderDao.add();
        orderDao.delete();
        orderDao.find();
        orderDao.update();
    }
}
測試結果:
環繞前增強。。。
添加訂單
環繞後增強。。。
刪除訂單
查詢訂單
環繞前增強。。。
修改訂單
環繞後增強。。。

自動代理

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

  • 自動創建代理(*****基於後處理Bean.在Bean創建的過程中完成的增強.生成Bean就是代理.)
  • BeanNameAutoProxyCreator 根據Bean名稱創建代理
  • DefaultAdvisorAutoProxyCreator 根據Advisor本身包含信息創建代理
    • * AnnotationAwareAspectJAutoProxyCreator 基於Bean中的AspectJ 註解進行自動代理

自動方式代理沒有切點切面的增強

<!-- 自動方式代理沒有切點切面的增強 -->
    <!-- 目標對象 -->
    <bean id="customerDao" class="cn.spring3.demo3.CustomerDaoImpl"></bean>
    <bean id="orderDao" class="cn.spring3.demo4.OrderDao"></bean>

    <!-- 定義增強 -->
    <bean id="beforeAdvice" class="cn.spring3.demo3.MyBeforeAdvice"></bean>
    <bean id="aroundAdvice" class="cn.spring3.demo4.MyAroundAdvice"></bean>

    <!-- 自動代理:按名稱的代理 基於後處理Bean,後處理Bean不需要配置ID -->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*Dao"/>
        <property name="interceptorNames" value="beforeAdvice"/>
    </bean>

編寫測試類:

package cn.spring3.demo5;
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;
import cn.spring3.demo3.CustomerDao;
import cn.spring3.demo4.OrderDao;

/**
 * @author NOP
 * 自動方式代理沒有切點切面的增強
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest {
    @Autowired
    @Qualifier("orderDao")//不用註入代理,因為類已經是代理了
    private OrderDao orderDao;
    @Autowired
    @Qualifier("customerDao")
    private CustomerDao customerDao;

    @Test
    public void demo1(){
        orderDao.add();
        orderDao.delete();
        customerDao.find();
        customerDao.update();
    }
}
測試結果:
前置增強...
添加訂單
前置增強...
刪除訂單
前置增強...
查詢客戶
前置增強...
修改客戶

自動方式代理 有切點切面的增強

<!-- 自動方式代理 有切點切面的增強 -->
    <!-- 目標對象 -->
    <bean id="customerDao" class="cn.spring3.demo3.CustomerDaoImpl"></bean>
    <bean id="orderDao" class="cn.spring3.demo4.OrderDao"></bean>

    <!-- 定義增強 -->
    <bean id="beforeAdvice" class="cn.spring3.demo3.MyBeforeAdvice"></bean>
    <bean id="aroundAdvice" class="cn.spring3.demo4.MyAroundAdvice"></bean>

    <!-- 定義切點 -->
    <bean id="mypointcutAdviesor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--定義正則表達式,規定哪些方法執行攔截  -->
        <property name="patterns" value=".*add.*,.*update.*"/>
        <!-- 應用增強 -->
        <property name="advice" ref="beforeAdvice"/>
    </bean>

    <!-- 自動代理:按名稱的代理 基於後處理Bean,後處理Bean不需要配置ID -->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*Dao"/>
        <property name="interceptorNames" value="mypointcutAdviesor"/>
    </bean>

編寫測試類:

package cn.spring3.demo5;

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;

import cn.spring3.demo3.CustomerDao;
import cn.spring3.demo4.OrderDao;

/**
 * @author NOP
 * 自動方式代理 有切點切面的增強
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class SpringTest_1 {
    @Autowired
    @Qualifier("orderDao")
    private OrderDao orderDao;
    @Autowired
    @Qualifier("customerDao")
    private CustomerDao customerDao;

    @Test
    public void demo1(){
        orderDao.add();
        orderDao.delete();
        orderDao.update();
        orderDao.find();

        customerDao.add();
        customerDao.delete();
        customerDao.update();
        customerDao.find();
    }
}
測試結果:
前置增強...
添加訂單
刪除訂單
前置增強...
修改訂單
查詢訂單
前置增強...
添加客戶
刪除客戶
前置增強...
修改客戶
查詢客戶

區分基於ProxyFactoryBean的代理與自動代理區別?
***** ProxyFactoryBean:先有被代理對象,將被代理對象傳入到代理類中生成代理.
自動代理基於後處理Bean.在Bean的生成過程中,就產生了代理對象,把代理對象返回.生成Bean已經是代理對象.

Spring中的AOP(8)