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

 

  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.     public UserServProxyImpl(IUserServ iuserServ){
  8.         this.iuserServ = iuserServ;
  9.     }
  10.     public int deleteUserById(User user) {
  11.         beforeLog();
  12.         //调用目标对象里方法
  13.         iuserServ.deleteUserById(user);
  14.         afterLog();
  15.         return 0;
  16.     }
  17.     public List<User> findAllUser() {
  18.         beforeLog();
  19.         //调用目标对象里方法
  20.         iuserServ.findAllUser();
  21.         afterLog();
  22.         return null;
  23.     }
  24.     public int saveUser(User user) {
  25.         beforeLog();
  26.         //调用目标对象里方法
  27.         iuserServ.saveUser(user);
  28.         afterLog();
  29.         return 0;
  30.     }
  31.     private void beforeLog() {
  32.         System.out.println(“开始执行”);
  33.     }
  34.     private void afterLog() {
  35.         System.out.println(“执行完毕”);
  36.     }
  37. }

 

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. [email protected](代理类)
  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. [email protected]
  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.     private void beforeLog(){
  32.         System.out.println(“开始执行”);
  33.     }
  34.     private void afterLog(){
  35.         System.out.println(“执行完毕”);
  36.     }
  37. }

 

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.     <aop:config>
  7.         <aop:aspect id=”logAspect” ref=”logAdvice”>
  8.             <!– 切入点 –>
  9.             <aop:pointcut id=”beforePointCut”
  10.         expression=”execution(* saveUser*(..))”/>
  11.         <aop:pointcut id=”afterPointCut”
  12.         expression=”execution(* saveUser*(..))”/>
  13.             <!– 织入(通知作用于切入点) –>
  14.             <aop:before method=”beforeLog” pointcut-ref=”beforePointCut”/>
  15.             <aop:after method=”afterLog” pointcut-ref=”afterPointCut”/>
  16.         </aop:aspect>
  17.     </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

Tagged: ,

Comments are closed.