浅谈Spring的AOP实现-代理机制

深藏阁楼爱情的钟 2021-11-11 09:44 379阅读 0赞

说起Spring的AOP(Aspect-Oriented Programming)面向切面编程大家都很熟悉(Spring不是这次博文的重点),但是我先提出几个问题,看看同学们是否了解,如果了解的话可以不用继续往下读:

1. Spring的AOP的实现方式有哪些?

2. 为什么使用代理机制?

3. 它们是怎么实现的?

4. 它们的区别是什么?

下面进入正题,Spring采用代理的方式实现AOP,具体采用了JDK的动态代理和CGLib实现。使用动态代理和CGLib的目的是在现有类的基础上增加一些功能。简单地将就是有一个Proxy类,实现了原始类的方法,并且在原始类的基础上增加了新的功能。那么这么做可以实现很多功能:

  1. 在方法前后进行日志处理。

  2. 进行额外的校验,比如参数的验证功能等。

  3. 实现一些懒加载,也就是实例化的时候如果不去调用真正的方法的时候,这个类的属性就不会存在(Hibernate有这样类似的功能)。

下面咱们用简单的代码实现它是如何进行代理的,首先采用的是JDK的动态代理实现:

定义一个接口:

  1. package com.hqs.proxy;
  2. /** * 操作系统光接口
  3. * @author hqs
  4. * */
  5. public interface OpSystem { public void work();
  6. }

定义一个实现类:

  1. package com.hqs.proxy;
  2. /** * Mac 的实现
  3. * @author hqs
  4. * */
  5. public class Mac implements OpSystem { public void work() {
  6. System.out.println("Mac is running");
  7. }
  8. }

位置来了,我们通过实现JDK自带的反射机制的包的InvocationHandler来进行反射处理,实现它之后需要实现里边的invoke方法,这个invoke方法里边的参数分别为:代理类实例,用于调用method的;method参数是实际执行的方法;args所传输的参数数组。

  1. package com.hqs.proxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. public class OpHandler implements InvocationHandler {
  6. private final OpSystem ops;
  7. public OpHandler(OpSystem ops) {
  8. this.ops = ops;
  9. }
  10. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  11. System.out.println("Before system running");
  12. method.invoke(ops, args);
  13. System.out.println("After system running");
  14. return null;
  15. }
  16. public static void main(String[] args) {
  17. Mac mac = new Mac();
  18. OpHandler oph = new OpHandler(mac);
  19. OpSystem os = (OpSystem)Proxy.newProxyInstance(oph.getClass().getClassLoader(),
  20. mac.getClass().getInterfaces(), oph);
  21. os.work();
  22. System.out.println(os.getClass());
  23. }
  24. }
  25. 输出:
  26. Before system running
  27. Mac is running
  28. After system running
  29. class com.sun.proxy.$Proxy0

然后看到里边的main方法中,代理类实例化对象的方法Proxy.newProxyInstance,这个是JDK的反射方法去实例化代理类,其中有三个参数分别是,去实例化代理类的class loader;所代理的类的所有接口Class数组;hander处理类,用于做拦截使用的类。最后我输出了一下os.getClass(),大家可以看到的是代理类的实例,而不是真正代理类的实例,这么做的好处就是很方便的复用这个代理类,比如你可以重复调用它而不用去重新实例化新类,再一点就是你可以针对不同的方法进行拦截,比如你可以method.getName()去判断调用的方法名字是什么从而更细粒度的拦截方法。咱们继续看用CGLib的实现:

  1. package com.hqs.proxy;
  2. import java.lang.reflect.Method;
  3. import net.sf.cglib.proxy.Enhancer;
  4. import net.sf.cglib.proxy.MethodInterceptor;
  5. import net.sf.cglib.proxy.MethodProxy;
  6. /**
  7. * CGLib Interceptor用于方法拦截
  8. * @author hqs
  9. *
  10. */
  11. public class CGLibInterceptor implements MethodInterceptor {
  12. private final Mac mac;
  13. public CGLibInterceptor(Mac mac) {
  14. this.mac = mac;
  15. }
  16. @Override
  17. public Object intercept(Object obj, Method method, Object[] args,
  18. MethodProxy methodProxy) throws Throwable {
  19. System.out.println("Before system running");
  20. method.invoke(mac, args);
  21. System.out.println("After system running");
  22. return null;
  23. }
  24. public static void main(String[] args) {
  25. Mac mac = new Mac(); //实例而非接口
  26. MethodInterceptor handler = new CGLibInterceptor(mac);
  27. Mac m = (Mac)Enhancer.create(mac.getClass(), handler);
  28. m.work();
  29. System.out.println(m.getClass());
  30. }
  31. }
  32. 输出:
  33. Before system running
  34. Mac is running
  35. After system running
  36. class com.hqs.proxy.Mac$$EnhancerByCGLIB$$1f2c9d4a

首先需要引入cglib包,然后才能使用他的MethodInterptor,它也采用method.invoke实现对代理类的调用。它的代理类创建采用Enhancer的create方法,其中传入了需要创建的类的class,以及Callback对象,因为MethodInterceptor继承了Callback对象。用于指向方法前后进行调用的类。

  1. public interface MethodInterceptor
  2. extends Callback
  3. 这是这两个类的基本实现,那么它们的 区别 是什么呢?
  1. JDK的动态代理只能针对接口和其实现类,如果没有实现类只有接口也是可以代理的,这里就不在举例了。为什么JDK的动态代理只针对接口代理,因为这个是JDK的定义。
  2. 如果不针对接口实现动态代理那就用到了CGLib了,也就是可以针对具体的类进行代理,大家可以参考我的代码。

这些是它们的根本区别,但是Spring推荐使用JDK的动态代理,面向接口去编程。使用CGLib去做代理的时候需要注意,它生成的代理类存放在JVM的Perm space里边,那么是不是生成的代理对象就不进行回收了?其实不是的,不经常回收但是还是回收的,当类被加载,加载类的classLoader什么时候变得对垃圾回收可用的时候才进行回收。也就是你自己创建所有类移除classLoader之后,那么这个classLoader就会被回收,一般非常精通CGLib的话可以进行这块内容深入开发,因为它可以做出amzing的事情如果你熟悉的话。

end:如果觉得本文对你有帮助的话,记得点赞关注哈!想学习更多方面的Java技术的知识的朋友们,可以进我的一个Java高级架构师交流群,里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料,进群即可免费领取哦,群号:680075317,也可以进群一起交流,比如遇到技术瓶颈、面试不过的,大家一些交流学习!

发表评论

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

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

相关阅读

    相关 Spring AOP

    > AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的集中处理。⽐如⽤户登录权限的效验,没学 AOP 之前,我们所

    相关 AOP实现机制 - 代理模式

    AOP实现机制 - 代理模式 代理模式 (1)真是角色更加纯粹!不需要关心一些公共业务。 (2)公共业务也就交给了代理角色。 (3)公共业务大声扩展的时候,方便

    相关 Spring事件机制

    一、同步事件和异步事件 同步事件:在一个线程里,按顺序执行业务,做完一件事再去做下一件事。 异步事件:在一个线程里,做一个事的同事,可以另起一个新的线程执行另一件事,这