【年后跳槽必看篇 - 非广告】老生常态之 Spring AOP/IOC 实现原理
历史优秀文章复盘:
继上一篇 Spring Bean 的生命周期,有提到 BeanPostProcessor 是 AOP 实现的关键子类。本编文章详细讲解一下 AOP 和 IOC 的实现原理。
AOP
什么是 AOP
结合上一篇文章提到的内容:通俗的讲就是当你想要实现对象增强,就可以使用 AOP。不然的话还需要自己创建代理。AOP 就是为了解决 非业务代码抽取 的问题。它的底层技术实现是动态代理,在 Spring 内实现依赖的是 BeanPostProcessor。
AOP 在 Spring 中的几种实现方式
一共有 4 种方式:
注解
XML
JavaConfig
基于 Groovy DSL 配置
一般在实际应用中,主要是注解、XML,少部分会用 JavaConfig。比如常见的业务代码使用注解定义各种对象,责任链这种一般有的会配置定义在 XML。注解解决不了的就会用 JavaConfig。
不同版本 Spring 的 AOP 执行顺序
Spring AOP 常用的注解:
@Before 前置通知:目标方法之前执行
@After 后置通知:目标方法之后执行
@AfterReturning 返回后通知:执行方法结束前执行(异常不执行)
@AfterThrowing 异常通知:出现异常时执行
@Around 环绕通知:环绕目标方法执行
Spring4 下 AOP 的执行顺序:
正常情况:@Before 前置通知 -> @After 后置通知 -> @AfterReturning 正常返回异常情况:@Before 前置通知 -> @After 后置通知 -> @AfterThrowing 方法异常
Spring5 下 AOP 的执行顺序:
正常情况:@Before 前置通知 -> @AfterReturning 正常返回 -> @After 后置通知异常情况:@Before 前置通知 -> @AfterThrowing 异常通知 -> @After 后置通知
Spring AOP 实现原理
Spring AOP 是通过代理模式实现的。具体有两种实现方式,一种是基于 Java 原生的动态代理,一种是基于 cglig 的动态代理。对应的代码实现分别是:CglibAopProxy 和 JdkDynamicAopProxy
同时 Spring AOP 默认使用的是 JDK 动态代理进行 AOP 代理。这使得任何接口都可以被代理。但是 JDK 动态代理有一个缺点,就是不能代理没有接口的类。所以 Spring AOP 使用 CGLIB 代理没有接口的类
但是要注意 Spring Boot 在 2.x 版本后默认使用的是 CGLIB 动态代理了。具体原因是为了解决使用 JDK 代理可能导致的类型转化异常而使用 CGLIB。当然也可以通过参数
spring.aop.proxy-target-class=false
进行修改
为什么 SpringBoot 默认使用 CGLIB 作为代理的实现方式呢?
我觉得可以从两个切入点聊一聊这个事:一是兼容性,另外就是性能。
兼容性:
CGLIB 动态代理可以适用于任何类型的目标类,无论它是否实现了接口。
而 JDK 动态代理只能适用于实现了接口的目标类。
这意味着 CGLIB 动态代理可以覆盖 JDK 动态代理的所有场景,而 JDK 动态代理不能覆盖 CGLIB 动态代理的所有场景。
因此,为了保证 SpringBoot 中的 AOP(面向切面编程)功能可以应用于任何类型的 Bean(无论它是否实现了接口),SpringBoot 默认使用 CGLIB 作为代理的实现方式。
性能:
CGLIB 动态代理在生成代理对象时需要消耗更多的时间和内存资源,因为它需要操作字节码;而 JDK 动态代理在生成代理对象时相对较快,因为它只需要操作反射。
但是,在执行代理方法时,CGLIB 动态代理比 JDK 动态代理要快得多,因为它直接调用目标方法,而不需要通过反射。
而在 SpringBoot 中,通常只会在容器启动时生成一次代理对象,并缓存起来;而在运行时会频繁地执行代理方法。因此,在整体性能上,CGLIB 动态代理比 JDK 动态代理要优越。
什么是 JDK 代理?
JDK 动态代理是通过反射机制来实现的,它要求目标类必须实现一个或多个接口,然后通过 java.lang.reflect.Proxy 类来创建代理对象,并通过 java.lang.reflect.InvocationHandler 接口来实现方法的拦截和增强。
什么是 CGLIB?
CGLIB 是一种基于 ASM 的代码生成库,它可以在运行时动态地生成和修改 Java 字节码,从而实现对 Java 类和接口的扩展和代理。CGLIB 是一种高性能、高质量的代码生成工具,被广泛应用于 Hibernate、Spring AOP 等框架中。
CGLIB 动态代理是通过继承机制来实现的,它不要求目标类必须实现接口,而是通过 net.sf.cglib.proxy.Enhancer 类来创建子类对象作为代理对象,并通过 net.sf.cglib.proxy.MethodInterceptor 接口来实现方法的拦截和增强。
Spring AOP 在业务中常见的使用方式
在实际业务场景中 AOP 的使用频率还是很高的,比如:
一个系统离不开监控,监控基本的的指标有 QPS、RT、ERROR 等等。只要利用注解+AOP 的方式封装一套,主要方法/类上带有自定义的注解,方法被调用时,就会上报你想要的相关监控指标。实现了非业务代码与业务代码分离的效果。
IOC
什么是 IOC
从个人理解简述来说:Spring IOC 解决的是对象管理和对象依赖的问题。以前我们是自己手动 new 出来的对象,现在则是可以将对象交给 Spring 的 IOC 容器进行管理。
IOC 容器可以简单理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系,等我们需要的时候,从工厂里边获取就好了。
关于工厂设计模式可参考文章:
更多的设计模式系列文章:
IOC 的两个重要概念
IOC 有两个重要的概念就是控制反转和依赖注入。
控制反转:指的就是将原有的自己掌控的事交给别人处理,其体现更多的是一种思想或者可以理解为设计模式。
比如:本来以前有我们自己 new 出来的对象,现在交给 IOC 容器,把对象的控制权交给它了。
依赖注入:可以理解为控制反转的实现方式。通俗的讲就是:对象无需自行创建或管理它的依赖关系,依赖关系将被自动注入到需要它们的对象中去。
Spring IOC 有什么好处?或者说为什么要将对象交由给 Spring IOC 容器进行管理呢?
其最主要的好处就是将对象集中统一管理,并且降低耦合度。如果理解了【工厂模式】,其实也就不难理解为什么我们不直接 new 对象了。
好处:
我们可以使用 Spring IOC 可以方便单元测试,对象创建复杂、对象依赖复杂、单例等等。什么都可以交给 Spring IOC
理论上自己 new 出来的都可以解决上面的问题,Spring 在各种场景下有可能并不是最优解。但是 new 出来的对象要自己管理,可能需要利用一些工厂模式等,需要自己实现一整套的东西才可以满足需求。其实这样就无限接近了 Spring 的那一套了。
当然,如果项目中的对象都是 new 一下就完事了。没有多个实现类,那也可以不用 Spring 也没什么问题
但是,Spring 核心并不是仅仅 IOC,除了把对象创建出来,还有一整套的生命周期管理,比如说 AOP 实现的对象增强。
聊聊你对使用 Spring 有什么感受
首先个人觉得对于初学者来讲,Spring 很麻烦,因为需要一大顿的配置才能跑起来。搭建环境更容易出现版本冲突,依赖冲突。解决这些问题很耗时间。但是话又说回来毕竟搭建环境这种事还是很少的。而且 IOC 和 AOP 在业务场景中使用起来确实很方便优雅。搞个注解什么的就欧克了。再加上后来上了 Spring Boot 更加从容了哈哈。
但是,由于 Spring 什么都给我们做了,封装的很好。导致在一些使用上会出现很多奇怪的 BUG。比如说:Spring 很典型的对象的循环依赖问题。同一个接口,多个实现,识别不出我要创建哪个对象。以及事务莫名失效。所以只有充分理解 Spring 整体框架,遇到这些问题才好排查,不然这些问题很是头疼。
如有问题,欢迎加微信交流:w714771310,备注- 技术交流 。或关注微信公众号【码上遇见你】。
好了,本章节到此告一段落。希望对你有所帮助,祝学习顺利。
版权声明: 本文为 InfoQ 作者【派大星】的原创文章。
原文链接:【http://xie.infoq.cn/article/194c6e73f2402906b39408416】。
本文遵守【CC BY-NC-ND】协议,转载请保留原文出处及本版权声明。
评论