Spring AOP动态代理原理与实现方式 (转)

标签: spring aop 代理 | 发表时间:2014-03-28 11:47 | 作者:tanzhen-1988
出处:http://www.iteye.com

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接口代码

 

  1. public interface IUserServ {  
  2.     List<User> findAllUser();  
  3.     int deleteUserById(User user);  
  4.     int saveUser(User user);  
  5. }  

 


UserServImpl实现类代码

 

  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实现类代码

 

  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测试类代码

 

  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类代码

 

  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测试类代码:

 

  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)

 

  1. <bean id="userServTarget" class="com.tarena.biz.impl.UserServImpl"/>   

 

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

 

  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)

 

  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)

 

  1. <!-- 定义通知 -->  
  2.         <!-- 前置通知 -->  
  3.         <bean id="beforeLogAdvice" class="com.tarena.advice.BeforeLogAdvice"/>  
  4.         <!-- 后置通知 -->  
  5.         <bean id="afterLogAdvice" class="com.tarena.advice.AfterLogAdvice"/>  

 


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

 

  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):

 

  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中

 

  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中

 

  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>  

 

测试类:

 

  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. }  

 

运行结果
******执行删除方法******
*******执行查询方法*******
开始执行
*******执行添加方法********
执行完毕

 

注:如果要在业务层所有的方法前后添加日志文件,则需要更改为以下配置

 

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

 


运行结果:

开始执行
******执行删除方法******
执行完毕
开始执行
*******执行查询方法*******
执行完毕
开始执行
*******执行添加方法********
执行完毕

 

 

转自:http://blog.csdn.net/lirui0822/article/details/8555691



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [spring aop 代理] 推荐:

Spring AOP 代理机制 JDK&CGLIB

- - 开源软件 - ITeye博客
Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理. (建议优先使用JDK的动态代理). 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理. 所有该目标类型实现的接口都将被代理. 若该目标对象没有实现任何接口,则创建一个CGLIB代理. 如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以.

Spring AOP详解

- - Java - 编程语言 - ITeye博客
        最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP来解决. 一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容. 例如,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 1.对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况.

Spring AOP动态代理原理与实现方式 (转)

- - 开源软件 - ITeye博客
AOP:面向切面、面向方面、面向接口是一种横切技术. 1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物   . 3.安全验证: Spring AOP---OOP升级  . 静态代理原理:目标对象:调用业务逻辑    代理对象:日志管理. 表示层调用--->代理对象(日志管理)-->调用目标对象.

Spring AOP监控SQL执行

- - CSDN博客架构设计推荐文章
         对数据库连接池Proxool比较熟悉的读者,都知道Proxool可以记录SQL执行内容和时间等信息日志. 我们可以将该日志记录专门的SQL日志文件,对于查找执行特别耗时的SQL起了不小的作用. 对于一些其他连接池,没有该特性时,本文介绍Spring AOP切面方法来记录SQL日志.

Spring AOP 实现原理与 CGLIB 应用

- - 博客 - 伯乐在线
来源: IBM Developerworks. 简介: AOP(Aspect Orient Programming),也就是面向方面编程,作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等.

使用spring AOP获得session的思路

- - RSS - IT博客云
由于Spring 的AOP面向切面编程,与Servlet容器没有任何关联,所以想要获得Session会话比较麻烦. 当然Struts2同样不依赖Servlet容器,可以在Spring AOP中可以使用 com.opensymphony.xwork2.ActionContext,就可以获得 Session.

Spring AOP + Redis缓存数据库查询

- - 编程语言 - ITeye博客
我们希望能够将数据库查询结果缓存到Redis中,这样在第二次做同样的查询时便可以直接从redis取结果,从而减少数据库读写次数. 必须要做到与业务逻辑代码完全分离. 从缓存中读出的数据必须与数据库中的数据一致. 如何为一个数据库查询结果生成一个唯一的标识. Key),能唯一确定一个查询结果,同一个查询结果,一定能映射到同一个.

AOP代理失效的两种情况

- - Shaojunying's Blog
被代理对象的private方法不能被代理. cdlib技术使用子类来代理父类,子类看不到父类的private,也就无法代理private方法. JDK动态代理是基于接口来实现的,而接口不允许有private方法,也就不会有出现这个问题. 如果方法a()调用处于同一个类内的方法b(),那么相当于this.b(),这里的this代表没有被代理的方法.

基于 Annotation 拦截的 Spring AOP 权限验证方法

- - 企业架构 - ITeye博客
转自: http://www.ibm.com/developerworks/cn/java/j-lo-springaopfilter/index.html. 使用 Annotation 可以非常方便的根据用户的不同角色,分配访问 Java 方法的权限. 在 Java Web 开发中,使用这种方法,可以提高系统的松耦合度,方便维护.

Spring aop 原理及各种应用场景

- - 开源软件 - ITeye博客
AOP是Aspect Oriented Programing的简称,面向切面编程. AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理、缓存、对象池管理以及日志记录. AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中. AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强.