Spring AOP和IOC

文章目录

概述

Spring框架中的AOP(Aspect-Oriented Programming)和IOC(Inversion of Control)是两个核心概念,它们在Spring应用中起着重要作用。

  1. IOC(Inversion of Control):
    IOC是一种设计原则,它反转了传统的程序设计中对象控制的方式。在传统的程序设计中,通常由程序自身控制对象的创建和管理,而在IOC容器中,控制权被反转,即由容器来创建和管理对象。在Spring框架中,IOC容器负责管理应用中的对象,它通过依赖注入(Dependency Injection)的方式来将对象之间的依赖关系注入到对象中。这样做的好处是降低了组件之间的耦合度,使得代码更加灵活、可维护和可测试。
  2. AOP(Aspect-Oriented Programming):
    AOP是一种编程范式,它通过在程序中定义切面(Aspect)来实现对横切关注点(Cross-cutting Concerns)的模块化。横切关注点是指跨越应用程序多个模块的功能,如日志记录、事务管理、安全性等。在传统的面向对象编程中,这些关注点往往会与业务逻辑混在一起,导致代码的复杂性和重复性增加。而通过AOP,可以将这些横切关注点与业务逻辑分离开来,以切面的方式进行统一管理和维护。Spring框架提供了强大的AOP支持,通过使用AOP可以很容易地实现诸如日志记录、性能监控、事务管理等功能。
    综上所述,IOC和AOP是Spring框架中两个重要的概念,它们分别通过控制反转和面向切面编程的方式,提高了应用程序的灵活性、可维护性和可扩展性。

AOP

AOP面向切面编程,将代码中重复的部分抽取出来,使用动态代理技术,在不修改源码的基础上对方法进行增强。

如果目标对象实现了接口,默认采用JDK动态代理,也可以强制使用CGLib,如果目标对象没有实现接口,采用CGLib的方式。

AOP 核心概念

1、切面(aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象

2、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。

3、连接点(joinpoint):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring

中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。

4、切入点(pointcut):对连接点进行拦截的定义

5、通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、

异常、最终、环绕通知五类。

6、目标对象:代理的目标对象

7、织入(weave):将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法

或字段。

Spring的AOP的底层实现原理

Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理。JDK 动态代

理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的

核心是 InvocationHandler 接口和 Proxy 类。如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。

动态代理(cglib 与 JDK)

JDK 动态代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用 Java 动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,就不能使用该机制。而 CGLIB 则可以实现对类的动态代理。

底层基于动态代理模式实现,如果我们的目标类有实现接口的情况下则采用JDK动态代理,如果我们的目标类没有实现接口的情况下则采用cglib动态代理

JDK动态代理跟CGLib动态代理的区别 (ms高频)

Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,

1、如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP

2、如果目标对象实现了接口,可以强制使用 CGLIB 实现 AOP

3、如果目标对象没有实现了接口,必须采用 CGLIB 库,spring 会自动在 JDK 动态代理和 CGLIB 之间转换

  1. JDK动态代理通过反射去实现被代理对象的所有接口,所以JDK只能代理实现了接口的对象,CGLlib对代理类所有方法重写,所以private的方法都不能被代理,不是所有的方法都能被代理
  2. JDK动态代理跟CGLib代理都是在运行期生成新的字节码,但是CGLib实现更为复杂,用的是ASM框架,生成代理类的效率比JDK低
  3. JDK动态代理是通过反射机制去查询所有被代理对象的接口,CGLib代理是通过FastClass机制直接调用方法,所有执行效率,CGLib比JDK代理要高
    4.spring用的是哪个代理模式 bean有实现接口的时候用jdk,没有实现的话模式cglib,也可以强制用cglib
    1JDK Proxy是实现目标对象的接口,而GGLib是继承目标对象
    JDK Proxy和CGLib都是在运行期生成字节码。
    JJDK Proxy是通过反射调用目标对象的方法,而CGLib是采用FastClass机制来调用

动态代理

aop是ioc的一个扩展功能,先有的ioc再有的aop,aop只是在ioc的整个流程中新增的一个扩展点而已:BeanPostPOrcessor

总:aop的概念,应用场景,动态代理

分:bean的创建过程中有一个步骤可以对bean进行扩展实现,aop本身就是一个扩展功能,所以在BeanPostProcess的后置处理方法中来进行实现

1.代理对象的创建过程(advice,切面,切点)

2.通过jdk或者cglib的方式来生成代理对象

3.在执行方法调用的时候,会调用到生成的字节码文件中,会直接找到DynamicAdvisoredInterceptor类中的intercept方法,从此方法开始执行

4.根据之前定义好的通知来生成拦截器链

5.从拦截器链中一次获取每一个通知开始进行执行,在执行过程中,为了方便找到下一个通知是哪个,会有一个InvocationInterceptor的对象,找的时候是从-1的位置一次开始查找并且执行的

Spring Aop有哪些应用场景

Spring Aop有哪些作用

可以解决我们代码重复性问题,对我们代码实现额外功能增强。

前置 后置 环绕 异常 等通知

主要应用场景:日志、权限、事务

常用的场景包括权限认证、自动缓存、错误处理、日志、调试和事务等

日志的采集、权限控制 aop 拦截器、Mybatis mapper、Spring的事务、全局捕获异常、Rpc远程调用接口 (传递就是接口)、代理数据源、自定义注解

Spring的AOP在什么场景下会失效

在以下场景下,Spring 的 AOP 可能会失效:

  1. 内部方法调用:AOP 是通过代理对象实现的,当目标方法在同一个类中的其他方法中被调用时,AOP 无法拦截这些内部方法调用。因为代理对象只能拦截从外部调用的方法。
  2. 非 public 方法调用:AOP 默认只能拦截公共的(public)方法,对于私有的(private)、受保护的(protected)以及默认访问级别的方法,AOP 无法进行拦截。
  3. 静态方法调用:AOP 是基于动态代理实现的,而静态方法是在编译时就确定调用的,不会通过代理对象进行调用,因此 AOP 无法拦截静态方法。
  4. 同一类中的方法互相调用:当同一个类中的方法互相调用时,AOP 也会失效。因为 Spring AOP 是基于代理对象实现的,代理对象只能拦截外部对目标方法的调用,而无法拦截目标方法内部的方法调用。
  5. 直接通过实例调用方法:如果代码直接通过实例对象调用方法,而不是通过 Spring 容器获取的代理对象调用方法,那么 AOP 也无法生效。
    需要注意的是,上述情况对于 Spring 的默认 AOP 实现来说是存在的,但可以通过使用 AspectJ 来解决这些限制。AspectJ 是一个功能更强大的 AOP 框架,它可以在编译期、类加载期或者运行期进行 AOP 操作,可以绕过上述的限制,实现对更多场景的拦截和增强。

IoC

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

IOC的全称是Inversion Of Control,也就是控制反转,它的核心思想是把对象的管理权限

交给容器。应用程序如果需要使用到某个对象实例,直接从IOC容器中去获取就行,这样设计的好处

是降低了程序里面对象与对象之间的耦合性。使得程序的整个体系结构变得更加灵活。

Spring里面很多方式去定义Bean, 比如XML里面的标签、@Service、

@Component、@Repository、@Configuration配置类中的@Bean注解等等。

Spring在启动的时候,会去解析这些Bean然后保存到IOC容器里面。

spring IOC的底层实现

底层实现:工作原理,过程,数据结构,流程,设计模式,设计思想

反射,工厂,涉及模式,关键的几个方法

createBeanFactory,getBean,doGetBean,createBean,doCreateBean,createBeanInstance,populateBean,

加do的方法都是实际干活的方法,不加do的都是在前面套一层

1.先通过createBeanFactory创建出一个Bean工厂(DefaultListableBeanFactory)

2.开始循环创建对象,因为容器中的bean默认都是单例的,所以优先通过getBean,doGetBean从容器中查找

3.找不到的话,通过createBean,doCreateBean方法,以反射的方式创建对象,一般情况下使用的是无参的构造方法(getDeclaredConstructor,newInstance)

4.进行对象的属性填充populateBean

5.进行其他的初始化操作(initalizingBean)

底层实现

1.spring通过解析配置文件或者注解方式加载到DefaultListableBeanFactory工厂

2.DefaultListableBeanFactory向IoC容器注册解析后的BeanDefinition

3.IOC容器本质上就是一个beanDefinitionMap, 注册即将BeanDefinition put到map中,已经注册到IoC容器中,被容器管理起来

4.遍历map,实例化(反射),容器可以进行依赖注入DI填充属性,开始初始化

Spring IoC的实现过程和IOC 工作流程

1.spring通过解析配置文件或者注解方式加载到DefaultListableBeanFactory工厂

2.DefaultListableBeanFactory向IoC容器注册解析后的BeanDefinition

3.IOC容器本质上就是一个beanDefinitionMap, 注册即将BeanDefinition put到map中,已经注册到IoC容器中,被容器管理起来

4.遍历map,实例化(反射),容器可以进行依赖注入DI填充属性,开始初始化

IOC 工作流程

Spring IOC的工作流程大致可以分为三个阶段

第一个阶段,就是IOC容器的初始化

这个阶段主要是根据程序中定义的XML或者注解等Bean的声明方式

通过解析和加载后生成BeanDefinition,然后把BeanDefinition注册到IOC容器。

通过注解或者xml声明的bean都会解析得到一个BeanDefinition实体,实体中包含这个bean中定义的基本属性。

最后把这个BeanDefinition保存到一个Map集合里面,从而完成了IOC的初始化。

IoC容器的作用就是对这些注册的Bean的定义信息进行处理和维护,它IoC容器控制反转的核心。

第二个阶段,完成Bean初始化及依赖注入

然后进入到第二个阶段,这个阶段会做两个事情,通过反射针对没有设置lazy-init属性的单例bean进行初始化。

完成Bean的依赖注入。

第三个阶段,Bean的使用

通常我们会通过@Autowired或者BeanFactory.getBean()从IOC容器中获取指定的bean实例。

另外,针对设置layy-init属性以及非单例bean的实例化,是在每次获取bean对象的时候,

调用bean的初始化方法来完成实例化的,并且Spring IOC容器不会去管理这些Bean。

Spring 的依赖注入

平常的 java 开发中,程序员在某个类中需要依赖其它类的方法,则通常是 new 一个依赖类再调用类实例的方法,这种开发存在的问题是 new 的类实例不好统一管理,spring 提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过 spring 容器帮我们 new 指定实例并且将实例注入到需要该对象的类中。依赖注入的另一种说法是"控制反转",通俗的理解是:平常我们 new 一个实例,这个实例的控制权是我们程序员,而控制反转是指 new 实例工作不由我们程序员来做而是交给 spring 容器来做。

1. 接口注入2. Setter方法注入3. 构造方法注入

相关推荐
F-2H19 分钟前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
苹果酱056721 分钟前
「Mysql优化大师一」mysql服务性能剖析工具
java·vue.js·spring boot·mysql·课程设计
武昌库里写JAVA25 分钟前
【MySQL】7.0 入门学习(七)——MySQL基本指令:帮助、清除输入、查询等
spring boot·spring·毕业设计·layui·课程设计
_oP_i1 小时前
Pinpoint 是一个开源的分布式追踪系统
java·分布式·开源
mmsx1 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
武子康2 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘3 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意3 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
FF在路上4 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进4 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html