Spring AOP動態代理原理與實現方式


AOP:面向切面、面向方面、面向接口是一種橫切技術
橫切技術運用:
1.事務管理: (1)數據庫事務:(2)編程事務(3)聲明事物:Spring AOP-->聲明事物   
2.日志處理:
3.安全驗證: Spring AOP---OOP升級  
  

靜態代理原理:目標對象:調用業務邏輯    代理對象:日志管理
表示層調用--->代理對象(日志管理)-->調用目標對象

動態代理原理:spring AOP采用動態代理來實現
(1)實現InvocationHandler接口

(2)創建代理類(通過java API)

Proxy.newProxyInstance(動態加載代理類,代理類實現接口,使用handler);

(3)調用invoke方法(虛擬機自動調用方法)

日志處理
 //調用目標對象
 method.invoke("目標對象","參數");
 日志處理

通過代理對象--(請求信息)-->目標對象---(返回信息)----> 代理對象

 

Spring 動態代理中的基本概念

1、關注點(concern)
   一個關注點可以是一個特定的問題,概念、或者應用程序的興趣點。總而言之,應用程序必須達到一個目標
   安全驗證、日志記錄、事務管理都是一個關注點
   在oo應用程序中,關注點可能已經被代碼模塊化了還可能散落在整個對象模型中
2、橫切關注點(crosscutting concern)
   如何一個關注點的實現代碼散落在多個類中或方法中
3、方面(aspect)
   一個方面是對一個橫切關注點模塊化,它將那些原本散落在各處的,
   用於實現這個關注點的代碼規整在一處
4、建議(advice)通知
   advice是point cut執行代碼,是方面執行的具體實現
5、切入點(pointcut)
   用於指定某個建議用到何處
6、織入(weaving)
   將aspect(方面)運用到目標對象的過程
7、連接點(join point)
  程序執行過程中的一個點 

通知類型:
  try{
    //前置通知
         //環繞通知
            //調用目標對象方法
         //環繞通知
    //后置通知
  }catch(){
    //異常通知
  }finally{
    //終止通知
  }

 

流程圖

 

一.靜態代理原理實例:

項目結構圖:                                                                    

IUserServ接口代碼

[java]  view plain  copy
 
  1. public interface IUserServ {  
  2.     List<User> findAllUser();  
  3.     int deleteUserById(User user);  
  4.     int saveUser(User user);  
  5. }  


UserServImpl實現類代碼

[java]  view plain  copy
 
  1. public class UserServImpl implements IUserServ {  
  2.     public int deleteUserById(User user) {  
  3.         System.out.println("******執行刪除方法******");  
  4.         return 0;  
  5.     }  
  6.     public List<User> findAllUser() {  
  7.         System.out.println("*******執行查詢方法*******");  
  8.         return null;  
  9.     }  
  10.     public int saveUser(User user) {  
  11.         System.out.println("*******執行添加方法********");  
  12.         return 0;  
  13.     }  
  14. }  

UserServProxyImpl實現類代碼

[java]  view plain  copy
 
  1. //代理類:完成日志輸出  
  2. public class UserServProxyImpl implements IUserServ {  
  3.     // 訪問目標對象(UserServImpl)  
  4.     // 代理對象(UserServProxyImpl)  
  5.     // 創建目標對象  
  6.     private IUserServ iuserServ ;//= new UserServImpl();  
  7.   
  8.     public UserServProxyImpl(IUserServ iuserServ){  
  9.         this.iuserServ = iuserServ;  
  10.     }  
  11.     public int deleteUserById(User user) {  
  12.         beforeLog();  
  13.         //調用目標對象里方法  
  14.         iuserServ.deleteUserById(user);  
  15.         afterLog();  
  16.         return 0;  
  17.     }  
  18.   
  19.     public List<User> findAllUser() {  
  20.         beforeLog();  
  21.         //調用目標對象里方法  
  22.         iuserServ.findAllUser();  
  23.         afterLog();  
  24.         return null;  
  25.     }  
  26.   
  27.     public int saveUser(User user) {  
  28.         beforeLog();  
  29.         //調用目標對象里方法  
  30.         iuserServ.saveUser(user);  
  31.         afterLog();  
  32.         return 0;  
  33.     }  
  34.   
  35.     private void beforeLog() {  
  36.         System.out.println("開始執行");  
  37.     }  
  38.       
  39.     private void afterLog() {  
  40.         System.out.println("執行完畢");  
  41.     }  
  42. }  

ActionTest測試類代碼

[java]  view plain  copy
 
  1. public class ActionTest {  
  2.     public static void main(String[] args) {  
  3.         //用戶訪問代理對象---信息->目標對象  
  4.         IUserServ iuserServ = new UserServProxyImpl(new UserServImpl());  
  5.         iuserServ.findAllUser();  
  6.     }  
  7. }  

運行結果:

開始執行
*******執行查詢方法*******
執行完畢
二.動態代理實例

項目結構圖:

IUserServ接口代碼與UserServImpl實現類代碼和上述代碼相同

LogHandler類代碼

[java]  view plain  copy
 
  1. public class LogHandler implements InvocationHandler {  
  2.     //目標對象  
  3.     private Object targetObject;  
  4.     /** 
  5.      * 創建動態代理類 
  6.      * @return object(代理類) 
  7.      */  
  8.     public Object createProxy(Object targetObject){  
  9.         this.targetObject = targetObject;  
  10.         return Proxy.newProxyInstance(  
  11.                 targetObject.getClass().getClassLoader(),   
  12.                     targetObject.getClass().getInterfaces(), this);  
  13.     }  
  14.     @Override  
  15.     public Object invoke(Object proxy, Method method, Object[] args)  
  16.             throws Throwable {  
  17.         Object obj = null;  
  18.         try {  
  19.             beforeLog();  
  20.             //obj: 目標對象--->代理對象的返回值--->返回給調用者的信息  
  21.             //this.invoke("目標對象","代理對象給目標對象傳遞參數");  
  22.             //調用目標對象中方法  
  23.             obj = method.invoke(targetObject, args);  
  24.             afterLog();  
  25.         } catch (Exception e) {  
  26.             e.printStackTrace();  
  27.         }  
  28.         return obj;  
  29.     }  
  30.       
  31.     //日志管理方法  
  32.     private void beforeLog(){  
  33.         System.out.println("開始執行");  
  34.     }  
  35.       
  36.     private void afterLog(){  
  37.         System.out.println("執行完畢");  
  38.     }  
  39.   
  40. }  

ActionTest測試類代碼:

[java]  view plain  copy
 
  1. public class ActionTest {  
  2.     public static void main(String[] args) {  
  3.         //創建代理對象iuserServ  
  4.         LogHandler handler = new LogHandler();  
  5.         IUserServ iuserServ = (IUserServ)handler.createProxy(new UserServImpl());  
  6.         iuserServ.deleteUserById(new User());  
  7.     }  
  8. }  

運行結果:
開始執行
******執行刪除方法******
執行完畢
三.Spring AOP使用(2.x版本之前)

項目結構圖:



IUserServ接口代碼與UserServImpl實現類代碼和上述代碼相同

配置步驟:

1、配置目標對象(applicationContext.xml)

[html]  view plain  copy
 
  1. <bean id="userServTarget" class="com.tarena.biz.impl.UserServImpl"/>   

 2、配置通知
(a)前置通知(BeforeLogAdvice)

[java]  view plain  copy
 
  1. public class BeforeLogAdvice implements MethodBeforeAdvice {  
  2.      /** 
  3.         * Method method:調用目標對象的方法 
  4.         * Object[] args:發送給目標對象的參數列表 
  5.         * Object target:目標對象 
  6.         */  
  7.     public void before(Method method, Object[] args, Object target)  
  8.             throws Throwable {  
  9.         beforeLog();  
  10.     }  
  11.     private void beforeLog(){  
  12.         System.out.println("開始執行");  
  13.     }  
  14. }  


(b)后置通知(AfterLogAdvice)

[java]  view plain  copy
 
  1. public class AfterLogAdvice implements AfterReturningAdvice {  
  2.       /** 
  3.         * Object returnValue:目標對象返回值 
  4.         *  Method method:目標對象方法名 
  5.         *  Object[] args:目標對象參數列表 
  6.         *  Object target:目標對象 
  7.         */  
  8.     public void afterReturning(Object returnValue, Method method,  
  9.             Object[] args, Object target) throws Throwable {  
  10.         afterLog();  
  11.     }  
  12.     private void afterLog(){  
  13.         System.out.println("執行完畢");  
  14.     }  
  15. }  

       

(c)在spring容器中,讓容器管理通知(applicationContext.xml)

[html]  view plain  copy
 
  1. <!-- 定義通知 -->  
  2.         <!-- 前置通知 -->  
  3.         <bean id="beforeLogAdvice" class="com.tarena.advice.BeforeLogAdvice"/>  
  4.         <!-- 后置通知 -->  
  5.         <bean id="afterLogAdvice" class="com.tarena.advice.AfterLogAdvice"/>  


3、配置代理對象(applicationContext.xml)  

[html]  view plain  copy
 
  1. <!-- 代理類作用: 生成代理類,織入通知 -->    
  2.   <bean id="userServProxy"   
  3.    class="org.springframework.aop.framework.ProxyFactoryBean">  
  4.    <property name="interfaces">  
  5.    <!-- 可以添加多個接口 -->  
  6.     <list>  
  7.      <value>com.tarena.biz.IUserServ</value>  
  8.     </list>  
  9.    </property>  
  10.    <!-- 引入通知 -->  
  11.    <property name="interceptorNames">  
  12.     <list>  
  13.      <value>beforeLogAdvice</value>  
  14.      <value>afterLogAdvice</value>  
  15.     </list>  
  16.    </property>  
  17.    <!-- 目標對象 -->  
  18.    <property name="target" ref="userServTarget"/>  
  19.   </bean>  


 4.訪問()
Spring容器:通過代理對象調用-->織入通知--->目標對象
程序員:訪問代理對象   

測試類(ActionTest):

[java]  view plain  copy
 
  1. public class ActionTest {  
  2.     public static void main(String[] args) {  
  3.         ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");  
  4.         IUserServ iuserServ = (IUserServ)ac.getBean("userServProxy");  
  5.         iuserServ.deleteUserById(new User());  
  6.         iuserServ.findAllUser();  
  7.     }  
  8. }  

運行結果:

開始執行
******執行刪除方法******
執行完畢
開始執行
*******執行查詢方法*******
執行完畢
四.Spring AOP使用(2.x版本之后)這種方式需要額外添加兩個jar包,

存放位置在spring-framework-2.5.6.SEC01\lib\aspectj文件夾下。

項目結構圖


IUserServ接口代碼與UserServImpl實現類代碼和上述代碼相同

LogAdvice中

[java]  view plain  copy
 
  1. public class LogAdvice {  
  2.     public void beforeLog(){  
  3.         System.out.println("開始執行");  
  4.     }  
  5.     public void afterLog(){  
  6.         System.out.println("執行完畢");  
  7.     }  
  8. }  

applicationContext.xml中

[html]  view plain  copy
 
  1. <!-- spring2.x后 -->  
  2.     <!-- 目標對象 -->  
  3.     <bean id="userServImpl" class="com.tarena.biz.impl.UserServImpl"/>  
  4.     <!-- 通知 -->  
  5.     <bean id="logAdvice" class="com.tarena.advice.LogAdvice"/>  
  6.       
  7.     <aop:config>  
  8.         <aop:aspect id="logAspect" ref="logAdvice">  
  9.             <!-- 切入點 -->  
  10.             <aop:pointcut id="beforePointCut"   
  11.         expression="execution(* saveUser*(..))"/>  
  12.         <aop:pointcut id="afterPointCut"   
  13.         expression="execution(* saveUser*(..))"/>  
  14.               
  15.             <!-- 織入(通知作用於切入點) -->  
  16.             <aop:before method="beforeLog" pointcut-ref="beforePointCut"/>  
  17.             <aop:after method="afterLog" pointcut-ref="afterPointCut"/>  
  18.         </aop:aspect>  
  19.     </aop:config>  

測試類:

[java]  view plain  copy
 
  1. public class ActionTest {  
  2.     public static void main(String[] args) {  
  3.         ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");  
  4.         IUserServ iuserServ = (IUserServ)ac.getBean("userServImpl");  
  5.         iuserServ.deleteUserById(new User());  
  6.         iuserServ.findAllUser();  
  7.         iuserServ.saveUser(new User());  
  8.     }  
  9. }  

運行結果
******執行刪除方法******
*******執行查詢方法*******
開始執行
*******執行添加方法********
執行完畢

注:如果要在業務層所有的方法前后添加日志文件,則需要更改為以下配置

[html]  view plain  copy
 
  1. <aop:pointcut id="beforePointCut"   
  2.         expression="execution(* com.tarena.biz.*.*(..))"/>  
  3.         <aop:pointcut id="afterPointCut"   
  4.         expression="execution(* com.tarena.biz.*.*(..))"/>  


運行結果:

開始執行
******執行刪除方法******
執行完畢
開始執行
*******執行查詢方法*******
執行完畢
開始執行
*******執行添加方法********
執行完畢


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM