Spring框架學習07——基於傳統代理類的AOP實現

在Spring中默認使用JDK動態代理實現AOP編程,使用org.springframework.aop.framework.ProxyFactoryBean建立代理是Spring AOP 實現的最基本方式。java

一、通知類型

根據Spring中通知在目標類方法中的鏈接點位置,通知能夠分爲6種類型:正則表達式

(1)環繞通知spring

環繞通知(org.aopalliance.intercept.MethodInterceptor)是在目標方法執行前和執行後實施加強,可應用於日誌記錄、事務處理等功能。編程

(2)前置通知app

前置通知(org.springframework.aop.MethodBeforeAdvice)是在目標方法執行前實施加強,可應用於權限管理等功能。ide

(3)後置返回通知測試

後置返回通知(org.springframework.aop.AfterReturningAdvice)是在目標方法成功執行後實施加強,可應用於關閉流、刪除臨時文件等功能。spa

(4)後置(最終)通知代理

後置通知(org.springframework.aop.AfterAdvice)是在目標方法執行後實施加強,與後置返回通知不一樣的是,無論是否發生異常都要執行該類通知,該類通知可應用於釋放資源。日誌

(5)異常通知

異常通知(org.springframework.aop.ThrowsAdvice)是在方法拋出異常後實施加強,可應用於處理異常、記錄日誌等功能。

(6)引入通知

引入通知(org.springframework.aop.IntroductionInterceptor)是在目標類中添加一些新的方法和屬性,可應用於修改目標類(加強類)。Spring AOP 只支持方法層面的加強,因此該類型的通知只做爲了解便可。

二、切面類型

  • Advisor:表明通常切面,Advice自己就是一個切面,對目標類全部方法進行攔截
  • PointcutAdvisor:表明具備切點的切面,能夠指定攔截目標類哪些方法
  • IntroductionAdvisor:表明引介切面,針對引介通知而使用切面

三、ProxyFactoryBean

ProxyFactoryBean是org.springframework.beans.factory.FactoryBean 接口的實現類,FactoryBean負責實例化一個Bean實例,ProxyFactoryBean負責爲其餘Bean實例建立代理實例。ProxyFactoryBean類的經常使用屬性以下:

  • target:代理的目標對象
  • proxyInterfaces:代理須要實現的接口列表,若是是多個接口,能夠使用如下格式賦值:<list> <value></value> </list>
  • InterceptorNames:須要織入目標的Advice
  • proxyTargetClass:是否對類代理而不是接口,默認爲false,使用JDK動態代理;當爲true時,使用CGLIB動態代理
  • singleton:返回的代理實例是否爲單例,默認爲true
  • optimize:當設置爲true時強制使用CGLIB動態代理

四、Advisor通常切面的實現

在pom.xml中導入AOP聯盟和Spring-aop的相關依賴

<!-- AOP聯盟依賴 -->
<dependency>
  <groupId>aopalliance</groupId>
  <artifactId>aopalliance</artifactId>
  <version>1.0</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>

建立StuDao接口和StuDaoImpl實現類,示例代碼以下:
StuDao接口

public interface StuDao {
    public void save();
    public void modify();
    public void delete();
}

StuDaoImpl實現類

public class StuDaoImpl implements StuDao {
    @Override
    public void save() {
        System.out.println("保存");
    }

    @Override
    public void modify() {
        System.out.println("修改");
    }

    @Override
    public void delete() {
        System.out.println("刪除");
    }
}

建立切面類MyBeforeAdvice,僅實現前置通知

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置加強========");
    }
}

在applicationContext.xml中配置切面並指定代理

<!--配置目標類-->
<bean id="stuDao" class="aop.StuDaoImpl"></bean>
<!--配置前置通知類型-->
<bean id="myBeforeAdvice" class="aop.MyBeforeAdvice"></bean>
<!--Spring AOP 生成代理對象-->
<bean id="stuDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!--配置目標類-->
    <property name="target" ref="stuDao"></property>
    <!--代理實現的接口-->
    <property name="proxyInterfaces" value="aop.StuDao"></property>
    <!--採起攔截的名稱-->
    <property name="interceptorNames" value="myBeforeAdvice"></property>
</bean>

建立測試類

@Test
public void demo(){
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    StuDao stuDao = (StuDao) app.getBean("stuDaoProxy");
    stuDao.save();
    stuDao.modify();
    stuDao.delete();
}

運行結果

五、PointcutAdvisor切點切面的實現

使用普通Advice做爲切面,將對目標類全部方法進行攔截,不夠靈活,在實際開發中常採用帶切點的切面。
經常使用PointcutAdvisor實現類:

  • DefaultPointcutAdvisor最經常使用的切面類型,它能夠經過任意Pointcut和Advice組合定義切面;
  • JdkRegexpMethodPointcut構造正則表達式切點(推薦)

建立StuDao接口和StuDaoImpl實現類,示例代碼以下:
StuDao接口

public interface StuDao {
    public void save();
    public void modify();
    public void delete();
}

StuDaoImpl實現類

public class StuDaoImpl implements StuDao {
    @Override
    public void save() {
        System.out.println("保存");
    }

    @Override
    public void modify() {
        System.out.println("修改");
    }

    @Override
    public void delete() {
        System.out.println("刪除");
    }
}

下面建立一個實現環繞通知的切面類

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation m) throws Throwable {
        //前置加強方法
        check();
        //執行目標方法
        Object obj = m.proceed();
        //後置加強方法
        log();
        return obj;
    }

    public void check(){
        System.out.println("模擬權限控制");
    }
    public void log(){
        System.out.println("模擬日誌記錄");
    }
}

在applicationContext.xml中配置切面並指定代理

<!--配置目標類-->
 <bean id="stuDao" class="aop.StuDaoImpl"></bean>
 <!--配置通知-->
 <bean id="myAspect" class="aop.MyAspect"></bean>
 <!--配置帶切入點的切面-->
 <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
     <!--pattern中配置正則表達式:.表示任意字符 *表示任意次數-->
     <property name="pattern" value=".*"></property>
     <property name="advice" ref="myAspect"></property>
 </bean>
 <!--Spring AOP 生成代理對象-->
 <bean id="stuDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
     <!--配置目標類-->
     <property name="target" ref="stuDao"></property>
     <!--代理實現的接口-->
     <property name="proxyInterfaces" value="aop.StuDao"></property>
     <!--採起攔截的名稱-->
     <property name="interceptorNames" value="myAdvisor"></property>
 </bean>

建立測試類

@Test
public void demo(){
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    StuDao stuDao = (StuDao) app.getBean("stuDaoProxy");
    stuDao.save();
    stuDao.modify();
    stuDao.delete();
}

運行結果

相關文章
相關標籤/搜索