ProxyFactoryBean類介紹spring
Spring的通知類型ide
雖然直接使用代理就能夠建立代理的實例,但須要本身寫建立代理的方法,好比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文件中配置代理便可,沒必要手寫建立代理的方法。代理
一、新建包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 }
運行,控制檯輸出以下
正在檢查權限
權限已夠
正在添加用戶
添加用戶成功
正在記錄日誌
日誌已記錄
代理成功。
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須要的包,則還須要本身去下載、添加這個包。
在例子的切面類中,咱們實現的是MethodInterceptor接口,這個接口是環繞通知的接口,可在目標方法先後實施加強,可用於日誌、事務管理等。
Spring的5種通知類型
通知類型 | 對應接口 | 加強時間 | 常見應用 |
環繞通知 | MethodInterceptor | 在目標方法執行先後實施加強 | 日誌、事務處理 |
前置通知 | MethodBeforeAdvice | 在目標方法執行前進行加強 | 權限管理 |
後置通知 | AfterReturningAdvice | 在目標方法執行後進行加強 | 關閉流、上傳文件、刪除臨時文件等 |
異常通知 | ThrowsAdvice | 在方法拋出異常後進行加強 | 處理異常、記錄日誌等 |
引介通知 | IntroductionInterceptor | 在目標類中添加一些新的屬性、方法 | 修改舊版程序 |
可根據須要,選擇實現接口。
Advice指的就是目標方法。加強其實就是作一些額外的處理。