Spring 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的动态代理。

对应的代码实现分别是:CglibAopProxyJdkDynamicAopProxy

  • 同时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的方式封装一套,主要方法/类上带有自定义的注解,方法被调用时,就会上报你想要的相关监控指标。实现了非业务代码与业务代码分离的效果
图片[1]-Spring AOP/IOC实现原理-不念博客

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实现的对象增强。

© 版权声明
THE END