【框架】从动态代理理解spring AOP

目录

为什么要说动态代理:

动态代理的实现:

动态代理的分类:

Spring 的AOP

一、 使用AspectJ

二、使用xml配置文件


为什么要说动态代理:

动态代理可以实现为对象提供一种代理,以控制这个对象的访问。
我们通常会用到在执行一批操作之前,例如对数据库数据进行增删改,需要保证这一部分操作的完整性,如果出现问题需要回滚到操作之前的状态。而这样的操作在业务上有很多。
最简单粗暴的做法,就是在每一个方法调用之前先加载这一步安全控制方法。
代理模式正是做了这样一件事情,它实现被代理类的接口,并引入被代理类,我们调用UserManagerImpleProxy类来通过代理类实现添加的功能。
/*
* 静态代理类
* 实现接口UserManager
* 引入UserManagerImpl
* */
public class UserManagerImplProxy implements UserManager {
   
   private UserManagerImpl userManagerImpl;


   // 构造方法
   public UserManagerImplProxy(UserManagerImpl userManagerImpl) {
      this.userManagerImpl = userManagerImpl;
   }


   // 具体方法
   public void addUser(String username, String password) {
      // 执行检查方法
      checkSecurity();
      // 调用具体类的实现方法
      userManagerImpl.addUser(username, password);
   }

   public void delUser(int userId) {
      checkSecurity();
      userManagerImpl.delUser(userId);
   }

   public String findUserById(int userId) {
      checkSecurity();
      return userManagerImpl.findUserById(userId);
   }

   public void modifyUser(int userId, String username, String password) {
      checkSecurity();
      userManagerImpl.modifyUser(userId, username, password);
   }

   // 检查的方法
   private void checkSecurity() {
      System.out.println("-------checkSecurity-------");
   }  
}

这是典型的静态代理,也就是在运行之前代码编辑好的,静态代理避免了修改原有的类,为了实现基本的开闭原则,在不修改原代码的基础上重新写一个代理类,来实现需要添加的功能。

但是同时这也是它的局限,它并没有避免多次调用checkSecurity()这个方法,每一个需要被代理的类都要有一个代理类,每一个代理类下的每一个方法前面都需要添加这样的方法,在代码量很大的情况下,就会很冗余。

所以出现了动态代理,不需要提前写好代理类,在程序运行的时候加载创建需要的代理类。

这也就是为什么要用动态代理的原因。

动态代理的实现:

采用最原始的动态代理模式
来实现在加载add方法之前,打印一句话-------checkSecurity------
public class SecurityHandler implements InvocationHandler {
   
   private Object targetObject;
   
   public Object createProxyInstance(Object targetObject) {
      this.targetObject = targetObject;
      return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                        targetObject.getClass().getInterfaces(),
                        this);
   }
   
   public Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable {
      System.out.println("-------checkSecurity-------");
      
      //调用目标方法
      Object ret = method.invoke(targetObject, args);
      
      return ret;
   }
   
}

这里method.invoke(目标类,目标类参数);只需要把需要被代理的类传给这个SecurityHandler类,它就可以动态创建代理类并执行invoke方法在执行方法之前添加上需要的内容

PS:动态代理,相当于在火腿肠出厂之前,把所有这一批生成的火腿肠,都打上生产日期的码,返回的东西,都是打码后的火腿肠,你在吧火腿肠放入打码器之前,需要先告诉它这个是什么食品:火腿肠。

也就是在需要告诉动态代理,这个需要被代理的类是谁,之后动态代理会执行相应的加工方法invoke(),对传入的类进行处理。

执行结果:

动态代理的分类:

动态代理也分两种,一种是,提供的这个要被代理的类有接口,一个是没有接口的。
  1. JDK动态代理,是有接口的类的代理
  2. CGlib动态代理,是没有接口的

Spring 的AOP

spring框架实现了这样的功能,不需要我们自己手动去写动态代理来实现这样的功能,减轻了开发过程中的工作量,更加规范方便。并且,spring会根据需要代理的这个类有无接口来自己选择需要使用的代理类型。
但也不是绝对的,我们也可以在有接口的情况下,强制使用CGlib动态代理:
在spring的配置文件中添加(applicationConfiger.xml):

<!-- 强制使用CGLIB代理 -->  

<aop:aspectj-autoproxy proxy-target-class="true"/>

 

spring AOP管理的两种风格:实现方式不同

一、 使用AspectJ

通过在spring的配置文件中配置<!-- 启用AspectJ对Annotation的支持 -->   <aop:aspectj-autoproxy/>  

把刚刚动态代理的部分,用这样注解的方式代替标识。

@Aspect
public class SecurityHandler {
   /**
    * 定义Pointcut,Pointcut的名称为addAddMethod(),此方法没有返回值和参数
    * 该方法就是一个标识,不进行调用
    */
   @Pointcut("execution(* add*(..))")
   private void addAddMethod(){};
   
   /**
    * 定义Advice,表示我们的Advice应用到哪些Pointcut订阅的Joinpoint上
    */
   @Before("addAddMethod()")
   //@After("addAddMethod()")
   private void checkSecurity() {
      System.out.println("-------checkSecurity-------");
   }     
}

 这里涉及到一些AOP的基础概念:

* 切面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。

* 连接点(Joinpoint): 在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。

* 通知(Advice): 在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。

* 切入点(Pointcut): 匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

* 引入(Introduction): (也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。 例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。

* 目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。

* AOP代理(AOP Proxy): AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。

* 织入(Weaving): 把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。

二、使用xml配置文件

可以直接省略掉标识@Aspect,直接使用配置文件配置需要切入的类,切入的方法位置,以及具体执行的内容都可以在这里配置

<aop:config>

   <aop:aspect id="securityAspect" ref="securityHandler">

      <!--

      以add开头的方法

      <aop:pointcut id="addAddMethod" expression="execution(* add*(..))"/>

       -->

       <!--

       com.bjpowernode.spring包下所有的类所有的方法

      <aop:pointcut id="addAddMethod" expression="execution(* com.bjpowernode.spring.*.*(..))"/>

       -->

      <aop:pointcut id="addAddMethod" expression="execution(* com.bjpowernode.spring.*.add*(..)) || execution(* com.bjpowernode.spring.*.del*(..))"/>

      <aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>

   </aop:aspect>

</aop:config>

 

// 这是具体的执行类
public class SecurityHandler {
   
   private void checkSecurity(JoinPoint joinPoint) {
      for (int i=0; i<joinPoint.getArgs().length; i++) {
         System.out.println(joinPoint.getArgs()[i]+"-----这是joinPoint.getArgs()的第"+ i);
      }
      
      System.out.println(joinPoint.getSignature().getName()+"------这是joinPoint.getSignature().getName()");
      
      System.out.println("-------checkSecurity-------");
   }     
}

运行结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邢美玲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值