基於代理類實現Spring AOP

 

目錄

 

 

 

 

ProxyFactoryBean類

雖然直接使用代理就能夠建立代理的實例,但須要本身寫建立代理的方法,好比JDK動態代理:測試

 1     ........
 2     //建立代理方法,參數是目標接口的實例
 3     public Object createProxy(UserInterface user){
 4         this.user=user;   //初始化目標接口實例
 5 
 6         ClassLoader classLoader=JdkProxy.class.getClassLoader();  //獲取當前類的類加載器,當前類類名.class.getClassLoader()
 7         Class[] classArr=user.getClass().getInterfaces();  //獲取目標接口實例實現的所有接口
 8 
 9         //參數:當前類的類加載器,目標接口實例實現的全部接口,當前類的實例
10         return Proxy.newProxyInstance(classLoader,classArr,this);
11     }

 

CGLIB代理:this

1     //建立代理,參數是Object類型
2     public Object createProxy(Object target){
3         Enhancer enhancer=new Enhancer();   //建立一個動態類對象
4         enhancer.setSuperclass(target.getClass());  //將這個動態類對象的父類/基類設置爲目標類(須要加強的類)
5         enhancer.setCallback(this);  //設置回調
6         return  enhancer.create();  //返回建立的代理
7     }

 

什麼亂七八糟的過程、方法,我哪記得住。ProxyFactoryBean類可解決此問題。spa

 

ProxyFactoryBean是FactoryBean接口的一個實現類,FactoryBean接口的做用是實例化一個Bean,ProxyFactoryBean類的做用實例化一個Bean的代理,咱們在xml文件中配置代理便可,沒必要手寫建立代理的方法。代理

 

 

 

基於JDK動態代理的Spring  AOP實現

一、新建包user,用於寫被代理的類、接口。包下新建接口UserInterface日誌

1 public interface UserInterface {
2     public void addUser();
3     public void alterUser();
4     public void deleteUser();
5 }

 

再新建一個實現類Usercode

 1 public class User implements UserInterface {
 2     @Override
 3     public void addUser() {
 4         //模擬添加用戶
 5         System.out.println("正在添加用戶");
 6         System.out.println("添加用戶成功");
 7     }
 8 
 9     @Override
10     public void alterUser() {
11         //模擬修改用戶信息
12         System.out.println("正在修改用戶信息");
13         System.out.println("修改用戶信息成功");
14     }
15 
16     @Override
17     public void deleteUser() {
18         //模擬刪除用戶
19         System.out.println("正在刪除用戶");
20         System.out.println("刪除用戶成功");
21     }
22 }

 

 

 

二、新建包aspect,用來寫切面類。包下新建類MyAspect,需實現MethodInterceptor接口,此接口是org.aopalliance.intercept包下的接口,不要import導入錯了。(須要aopalliance.jar包的支持

 1 public class UserProxy implements MethodInterceptor {
 2     @Override
 3     public Object invoke(MethodInvocation methodInvocation) throws Throwable {
 4         checkPermission();  //前加強
 5         Object object=methodInvocation.proceed();  //經過invoke()的參數調用
 6         log();  //後加強
 7         return object;
 8     }
 9 
10     public void checkPermission(){
11         //模擬檢查權限
12         System.out.println("正在檢查權限");
13         System.out.println("權限已夠");
14     }
15 
16     public void log(){
17         //模擬記錄日誌
18         System.out.println("正在記錄日誌");
19         System.out.println("日誌已記錄");
20     }
21 }

 

固然,能夠實現其它的接口(Spring的通知類型)。不一樣的接口,提供的功能不一樣。

 

 

三、在xml中配置代理

   <!-- 目標類-->
    <bean id="user" class="user.User" />

    <!-- 切面類-->
    <bean id="myAspect" class="aspect.MyAspect" />

    <!-- 這纔是真正的代理類,class要指定爲ProxyFactoryBean類-->
    <bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--指定接口,若是實現了多個接口,用子元素list來寫-->
        <property name="proxyInterfaces" value="user.UserInterface" />
        <!--指定目標類的實例-->
        <property name="target" ref="user" />
        <!--指定切面類的id/name,只能用value,不能用ref-->
        <property name="interceptorNames" value="myAspect" />
        <!--指定使用的代理,proxyTargetClass是問是否代理類,JDK動態代理是代理接口,CGLIB代理是代理類。false即不是代理類,即便用JDK動態代理-->
        <!--默認就是false,JDK動態代理。此句配置可缺省-->
        <property name="proxyTargetClass" value="false" />
        <!--返回的代理類實例是不是單實例,默認爲true,單實例。此句配置可缺省-->
        <property name="singleton" value="true" />
    </bean>

 ProxyFactoryBean類已經寫好了建立代理代碼,這個類使用setter方法注入依賴,咱們只須要用<property>注入它須要的參數便可。

 

 

四、新建包test,包下新建測試類Test

1 public class Test {
2     public static void main(String[] args) {
3         ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
4         //由於使用的JDK動態代理,代理的是接口,因此要使用接口來聲明
5         UserInterface user=applicationContext.getBean("userProxy", UserInterface.class);
6         user.addUser();
7     }
8 }

 

運行,控制檯輸出以下

正在檢查權限
權限已夠
正在添加用戶
添加用戶成功
正在記錄日誌
日誌已記錄

代理成功。

 

 

 

 

基於CGLIB代理的Spring  AOP實現

JDK動態代理代理的是接口,CGLIB代理的類,因此咱們只須要把上例中有關接口的部分去掉便可。也可一步步從頭開始。

 

一、新建包user,包下新建類User

 1 public class User{
 2     public void addUser() {
 3         //模擬添加用戶
 4         System.out.println("正在添加用戶");
 5         System.out.println("添加用戶成功");
 6     }
 7     
 8     public void alterUser() {
 9         //模擬修改用戶信息
10         System.out.println("正在修改用戶信息");
11         System.out.println("修改用戶信息成功");
12     }
13     
14     public void deleteUser() {
15         //模擬刪除用戶
16         System.out.println("正在刪除用戶");
17         System.out.println("刪除用戶成功");
18     }
19 }

 

 

二、新建包aspect,包下新建切面類MyAspect

 1 public class MyAspect implements MethodInterceptor {
 2     @Override
 3     public Object invoke(MethodInvocation methodInvocation) throws Throwable {
 4         checkPermission();  //前加強
 5         Object object=methodInvocation.proceed();  //經過invoke()的參數調用
 6         log();  //後加強
 7         return object;
 8     }
 9 
10     public void checkPermission(){
11         //模擬檢查權限
12         System.out.println("正在檢查權限");
13         System.out.println("權限已夠");
14     }
15 
16     public void log(){
17         //模擬記錄日誌
18         System.out.println("正在記錄日誌");
19         System.out.println("日誌已記錄");
20     }
21 }

 

 

三、在xml中配置代理

<!-- 目標類-->
    <bean id="user" class="user.User" />

    <!-- 切面類-->
    <bean id="myAspect" class="aspect.MyAspect" />

    <!-- 這纔是真正的代理類-->
    <bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--指定目標類的實例-->
        <property name="target" ref="user" />
        <!--指定切面類的id/name,只能用value,不能用ref-->
        <property name="interceptorNames" value="myAspect" />
        <!--指定使用的代理,proxyTargetClass是問是否代理類,JDK動態代理是代理接口,CGLIB代理是代理類。false即不是代理類,即便用JDK動態代理-->
        <!--默認就是false,JDK動態代理。此句配置可缺省-->
        <property name="proxyTargetClass" value="false" />
        <!--返回的代理類實例是不是單實例,默認爲true,單實例。此句配置可缺省-->
        <property name="singleton" value="true" />
    </bean>

去掉指定接口的那句配置就ok。

 

 

四、新建包test,包下新建測試類Test

1 public class Test {
2     public static void main(String[] args) {
3         ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
4         //要使用目標類來聲明
5         User user=applicationContext.getBean("userProxy", User.class);
6         user.addUser();
7     }
8 }

 

運行,控制檯輸出以下

正在檢查權限
權限已夠
正在添加用戶
添加用戶成功
正在記錄日誌
日誌已記錄

代理成功。

 

 

 

說明

須要2個包:spring-aop.jar,這個是Spring自帶的,不用管。aopalliance.jar,若是使用了maven或IDEA的自動下載Spring所需的包,會自動添加這個包,不用管,若是是手動添加Spring須要的包,則還須要本身去下載、添加這個包。

 

 


Spring的通知類型

在例子的切面類中,咱們實現的是MethodInterceptor接口,這個接口是環繞通知的接口,可在目標方法先後實施加強,可用於日誌、事務管理等。

 

Spring的5種通知類型

 通知類型   對應接口   加強時間   常見應用  
環繞通知     MethodInterceptor     在目標方法執行先後實施加強       日誌、事務處理    
前置通知 MethodBeforeAdvice 在目標方法執行前進行加強 權限管理
後置通知 AfterReturningAdvice 在目標方法執行後進行加強 關閉流、上傳文件、刪除臨時文件等  
異常通知 ThrowsAdvice 在方法拋出異常後進行加強 處理異常、記錄日誌等
引介通知 IntroductionInterceptor    在目標類中添加一些新的屬性、方法 修改舊版程序

 

可根據須要,選擇實現接口。

Advice指的就是目標方法。加強其實就是作一些額外的處理。

相關文章
相關標籤/搜索