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

古城微笑少年丶 2023-07-24 14:42 40阅读 0赞

目录

为什么要说动态代理:

动态代理的实现:

动态代理的分类:

Spring 的AOP

一、 使用AspectJ

二、使用xml配置文件


为什么要说动态代理:

动态代理可以实现为对象提供一种代理,以控制这个对象的访问。

我们通常会用到在执行一批操作之前,例如对数据库数据进行增删改,需要保证这一部分操作的完整性,如果出现问题需要回滚到操作之前的状态。而这样的操作在业务上有很多。

最简单粗暴的做法,就是在每一个方法调用之前先加载这一步安全控制方法。

代理模式正是做了这样一件事情,它实现被代理类的接口,并引入被代理类,我们调用UserManagerImpleProxy类来通过代理类实现添加的功能。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3htbDE5OTY_size_16_color_FFFFFF_t_70

  1. /*
  2. * 静态代理类
  3. * 实现接口UserManager
  4. * 引入UserManagerImpl
  5. * */
  6. public class UserManagerImplProxy implements UserManager {
  7. private UserManagerImpl userManagerImpl;
  8. // 构造方法
  9. public UserManagerImplProxy(UserManagerImpl userManagerImpl) {
  10. this.userManagerImpl = userManagerImpl;
  11. }
  12. // 具体方法
  13. public void addUser(String username, String password) {
  14. // 执行检查方法
  15. checkSecurity();
  16. // 调用具体类的实现方法
  17. userManagerImpl.addUser(username, password);
  18. }
  19. public void delUser(int userId) {
  20. checkSecurity();
  21. userManagerImpl.delUser(userId);
  22. }
  23. public String findUserById(int userId) {
  24. checkSecurity();
  25. return userManagerImpl.findUserById(userId);
  26. }
  27. public void modifyUser(int userId, String username, String password) {
  28. checkSecurity();
  29. userManagerImpl.modifyUser(userId, username, password);
  30. }
  31. // 检查的方法
  32. private void checkSecurity() {
  33. System.out.println("-------checkSecurity-------");
  34. }
  35. }

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

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

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

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

动态代理的实现:

采用最原始的动态代理模式

来实现在加载add方法之前,打印一句话———-checkSecurity———

  1. public class SecurityHandler implements InvocationHandler {
  2. private Object targetObject;
  3. public Object createProxyInstance(Object targetObject) {
  4. this.targetObject = targetObject;
  5. return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
  6. targetObject.getClass().getInterfaces(),
  7. this);
  8. }
  9. public Object invoke(Object proxy, Method method, Object[] args)
  10. throws Throwable {
  11. System.out.println("-------checkSecurity-------");
  12. //调用目标方法
  13. Object ret = method.invoke(targetObject, args);
  14. return ret;
  15. }
  16. }

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

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

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

执行结果:

20200412162851280.png

动态代理的分类:

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

  1. JDK动态代理,是有接口的类的代理
  2. CGlib动态代理,是没有接口的

Spring 的AOP

spring框架实现了这样的功能,不需要我们自己手动去写动态代理来实现这样的功能,减轻了开发过程中的工作量,更加规范方便。并且,spring会根据需要代理的这个类有无接口来自己选择需要使用的代理类型。

但也不是绝对的,我们也可以在有接口的情况下,强制使用CGlib动态代理:

在spring的配置文件中添加(applicationConfiger.xml):

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

一、 使用AspectJ

通过在spring的配置文件中配置

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

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

这里涉及到一些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,直接使用配置文件配置需要切入的类,切入的方法位置,以及具体执行的内容都可以在这里配置

  1. <!--
  2. 以add开头的方法
  3. <aop:pointcut id="addAddMethod" expression="execution(\* add\*(..))"/>
  4. -->
  5. <!--
  6. com.bjpowernode.spring包下所有的类所有的方法
  7. <aop:pointcut id="addAddMethod" expression="execution(\* com.bjpowernode.spring.\*.\*(..))"/>
  8. -->
  9. <aop:pointcut id="addAddMethod" expression="execution(\* com.bjpowernode.spring.\*.add\*(..)) || execution(\* com.bjpowernode.spring.\*.del\*(..))"/>
  10. <aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>

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

运行结果:

20200412163130494.png

发表评论

表情:
评论列表 (有 0 条评论,40人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring AOP动态代理

    动态代理 一、什么是动态代理 动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变

    相关 深入理解Spring AOP代理

    一开始我对Spring AOP还是属于一知半解的状态,这几天遇到一个问题,加上又查看了一些Spring相关知识,感觉对这个问题有了更深刻的认识。所以写下来分享一下。 我们知道

    相关 Spring AOP&&动态代理(一)

    AOP :面向切面的编程 是对OOP的扩展 OOP :引入封装、继承、多态等概念来建立一种对象层次结构;OOP允许开发者定义纵向关系,而不能处理横向关系;像类似日志记录、