跳过正文
  1. 博客/
  2. 后端/
  3. 框架/

AOP再思考

4 分钟· ·
后端 框架 Java
作者
Allen
一个强大、轻量级的 Hugo 主题。
目录

我记得之前写过Spring AOP相关的文章,但是最近在观看Cat源代码的时候发现 @Aspect 这个注解与AspectJ这个项目,查阅了不少博客,感觉还是云里雾里,这篇博客就是基于博客以及自己实际实验与测试搞懂 Spring AOP和 AspectJ之间的关系

AOP是什么
#

网上的资料很多,我就不写那些烂大街的总结了,其实很简单,我们先了解一下Python的装饰器

我们定义一个装饰器

def cost(f):
def _real(*arg, **kward):
start = time.time()
reslt = f(*arg, **kward)
end = time.time()
print(‘cost’, end - start, ‘ seconds’)
Return _real

然后我们可以将这个装饰器放在任何方法上

@cost
def run():
print(“run")

我们运行任何代码都可以使用这个装饰器,其实AOP的核心要求也就是这个,只要我定义了一个方法,我能让我想让的函数都能执行这个,这就叫解耦,Python怎么实现这个呢,其实很简单,一个语法糖,编译器会把这个cost函数重新定义为一个新函数,具体原理我就不介绍了,我就把拆解后的代码写出来吧

def _run():
print(“run”)

def run():
return cost(_run)()

然后在Java中虽然也有@ 这个用法,但是那个是注解,它的作用就是一个文档,我们可以反编译有注解的类,只要这个注解的RetentionPolicy是RetentionPolicy.RUNTIME  它就会在保留在字节码上面

Spring AOP怎么实现
#

那Spring AOP如何实现在字节码上面读取到注解,然后实现AOP功能呢,答案就是反射(这也是为啥Spring启动很慢的原因,反射太慢了)

通过Class.getAnnotation 这个方法,我们可以获取到类上面所有的注解,Spring 作为Bean工厂,对应所有的Bean它都会去读取它的注解,根据注解来灵活组合功能

拿到注解后,如何实现把你想切入的代码插入到这个被注解到类上面呢。Java是一个面对对象的语言,它要想改变类的方法,只能通过继承

继承有两张实现方式,假如类实现了接口,则使用JDK动态代理方式,即该类的newProxyInstance方法,第二种就是使用CGLib动态代理,自动生成类的字节码

为什么能实现呢,因为所有的bean都是Spring来控制生成的。因为获取bean的时候,我们也能知道这个bean的类名,所以Spring 也提供了通过类名来控制切面,这就是自己来实现的好处,Python的装饰器只有被装饰的类才有用,而Spring由于自己实现了这套逻辑,所以它其实也支持通过类名来进行AOP

Spring AOP的局限性
#

前面说到Spring AOP的原理,我们提到了一个关键就是被代理的都只能是bean,这就带来一个局限性,对应非bean的类,我们无法代理,不信你可以试试在Spring Boot里面切入一个非bean的三方如org.apache.ibatis.executor.BaseExecutor

那对应这种非Spring Bean的没法做到了吗,这里就要提到一个框架 AspectJ

这个就是AOP的鼻祖,Spring其实切面名字也叫Aspect,虽然实现不是用的AspectJ,但是用了它的注解类,这个框架强在哪呢,首先它提供了两种AOP方式
编译时候,修改编译的字节码,或者编译后增强
运行的时候,通过agent修改字节码

编译时候修改字节码,我们比较熟悉的lombok就是这样的,是通过jdk1.6 javax.annotation.processing.Processor 引入的一种agent插入方式,通过SPI技术,你只要实现了这个类即可修改字节码

编译后修改字节码是通过 JDK1.5 提供的 java.lang.instrument.Instrumentation来实现的,原理其实都很简单,使用SPI进行替换增强,感兴趣可以根据资料自己实操一下

AspectJ和Spring的关系
#

AspectJ 对应Spring 来说只是提供了注解类,Spring用代理类或者CGLIB实现了自己的逻辑

总结
#

之所以写这篇博文,主要是在学习JVM的时候发现对于JAVA的注解,Spring如何实现的自己不是很清楚,所以顺藤摸瓜,把这条链路给整明白,当然还有很多细节还没有深挖,比如AspectJ是怎么将切面代码进行字节码转编译,它是如何做到不出现循环依赖的

要想深刻理解AspectJ和Spring实现原理,非常推荐使用Arthas使用jad将运行的类给反编译下来,通过这种,你就不会停留在网上各种花里胡哨的文字上面,Spring的确是一个很牛逼的框架,封装了很多东西,定义了很多规范,要想理解这些,必须深入到源代码层次

AspectJ原理

https://blog.csdn.net/qq_40378034/article/details/115281684

Spring Boot AOP 原理

https://juejin.cn/post/6844903721101426701

https://www.cnblogs.com/tuyang1129/p/12878549.html#:~:text=Spring%20%E7%9A%84%20AOP%20%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86,%E9%87%8D%E5%86%99%E7%9A%84%E4%BB%A3%E7%90%86%E6%96%B9%E6%B3%95%E3%80%82

https://blog.mythsman.com/post/5d301cf2976abc05b34546be/

Lombok原理

https://blog.mythsman.com/post/5d2c11c767f841464434a3bf/

相关文章

压测心得
2 分钟
后端 框架 Java
Stream源码(1):如何实现去重
3 分钟
后端 框架 Java Stream
Dubbo浅探
3 分钟
后端 框架 Java Dubbo
Spring Cloud Alibaba浅探
2 分钟
后端 框架 Java SpringBoot
SpringCloud浅析
5 分钟
后端 框架 Java SpringBoot
JVM之ClassLoader的思考
后端 框架 Java JVM