Spring学习总结

本文详细介绍了Spring的核心组件——容器与bean,包括BeanFactory和ApplicationContext接口,Bean的生命周期,以及Bean后处理器的工作原理。深入探讨了@Value和@Autowired的装配过程,展示了Spring如何处理依赖注入。同时,分析了AOP的JDK动态代理和Cglib代理,以及Spring如何选择代理。最后,回顾了IOC的概念,解析了Spring如何解决循环依赖问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

容器与bean

1)容器接口

  • BeanFactory 接口,典型功能有:
    • getBean
  • ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:
    • 国际化
    • 通配符方式获取一组 Resource 资源
    • 整合 Environment 环境(能通过它获取各种来源的配置信息)
    • 事件发布与监听,实现组件之间的解耦
  • beanFactory 可以通过 registerBeanDefinition 注册一个 bean definition 对象
    • 我们平时使用的配置类、xml、组件扫描等方式都是生成 bean definition 对象注册到 beanFactory 当中
    • bean definition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
  • beanFactory 需要手动调用 beanFactory 后处理器对它做增强
    • 例如通过解析 @Bean、@ComponentScan 等注解,来补充一些 bean definition
  • beanFactory 需要手动添加 bean 后处理器,以便对后续 bean 的创建过程提供增强
    • 例如 @Autowired,@Resource 等注解的解析都是 bean 后处理器完成的
    • bean 后处理的添加顺序会对解析结果有影响
  • beanFactory 需要手动调用方法来初始化单例
  • beanFactory 需要额外设置才能解析 ${} 与 #{}

2)Bean 的生命周期

  1. 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
    • scope 为 singleton 时,bean 是随着容器一起被创建好并且实例化的
    • scope 为 prototype 时,bean 是随着它被调用的时候才创建和实例化
  2. 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
  3. 初始化:回调各种 Aware 接口,调用对象的各种初始化方法
  4. 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
    • prototype 对象也能够销毁,不过需要容器这边主动调用

3)Bean后处理器

(1)bean 后处理器排序

  1. 实现了 PriorityOrdered 接口的优先级最高
  2. 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序
  3. 其它的排在最后

(2)后处理器作用

  1. @Autowired @Value 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由 bean 后处理器来完成

(3)@Value装配底层

  1. 查看需要的类型是否为 Optional,是,则进行封装(非延迟),否则向下走
  2. 查看需要的类型是否为 ObjectFactory 或 ObjectProvider,是,则进行封装(延迟),否则向下走
  3. 查看需要的类型(成员或参数)上是否用 @Lazy 修饰,是,则返回代理,否则向下走
  4. 解析 @Value 的值
    • 如果需要的值是字符串,先解析 ${ },再解析 #{ }
    • 不是字符串,需要用 TypeConverter 转换
  5. 看需要的类型是否为 Stream、Array、Collection、Map,是,则按集合处理,否则向下走
  6. 在 BeanFactory 的 resolvableDependencies 中找有没有类型合适的对象注入,没有向下走
  7. 在 BeanFactory 及父工厂中找类型匹配的 bean 进行筛选,筛选时会考虑 @Qualifier 及泛型
  8. 结果个数为 0 抛出 NoSuchBeanDefinitionException 异常
  9. 如果结果 > 1,再根据 @Primary 进行筛选
  10. 如果结果仍 > 1,再根据成员名或变量名进行筛选
  11. 结果仍 > 1,抛出 NoUniqueBeanDefinitionException 异常

(4)@Autowired装配底层

  1. @Autowired 本质上是根据成员变量或方法参数的类型进行装配
  2. 如果待装配类型是 Optional,需要根据 Optional 泛型找到 bean,再封装为 Optional 对象装配
  3. 如果待装配的类型是 ObjectFactory,需要根据 ObjectFactory 泛型创建 ObjectFactory 对象装配
    • 此方法可以延迟 bean 的获取
  4. 如果待装配的成员变量或方法参数上用 @Lazy 标注,会创建代理对象装配
    • 此方法可以延迟真实 bean 的获取
    • 被装配的代理不作为 bean
  5. 如果待装配类型是数组,需要获取数组元素类型,根据此类型找到多个 bean 进行装配
  6. 如果待装配类型是 Collection 或其子接口,需要获取 Collection 泛型,根据此类型找到多个 bean
  7. 如果待装配类型是 ApplicationContext 等特殊类型
    • 会在 BeanFactory 的 resolvableDependencies 成员按类型查找装配
    • resolvableDependencies 是 map 集合,key 是特殊类型,value 是其对应对象
    • 不能直接根据 key 进行查找,而是用 isAssignableFrom 逐一尝试右边类型是否可以被赋值给左边的 key 类型
  8. 如果待装配类型有泛型参数
    • 需要利用 ContextAnnotationAutowireCandidateResolver 按泛型参数类型筛选
  9. 如果待装配类型有 @Qualifier
    • 需要利用 ContextAnnotationAutowireCandidateResolver 按注解提供的 bean 名称筛选
  10. 有 @Primary 标注的 @Component 或 @Bean 的处理
  11. 与成员变量名或方法参数名同名 bean 的处理

4)BeanFactory 后处理器

(1)BeanFactory 后处理器的作用

  1. @ComponentScan, @Bean, @Mapper,@Import ,@Configuration 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
  2. 这些扩展功能由不同的 BeanFactory 后处理器来完成,其实主要就是补充了一些 bean 定义
  3. Mapper 接口被 Spring 管理的本质:实际是被作为 MapperFactoryBean 注册到容器中

(2)@Configuration

  1. 配置类其实相当于一个工厂,标注@Bean注解的方法相当于工厂方法
  2. @Bean不支持方法重载,如果有多个重载方法,仅有一个能入选为工厂方法
  3. @Configuration默认会为标注的类生成代理,其目的是保证@Bean方法相互调用时,仍能保持其单例特性
  4. @Configuration中如果含有BeanFactory后处理器,则实例工厂方法会导致配置类提前创建,造成其依赖注入失败。
    • 解决方法:改用静态工厂方法或直接为@Bean依赖注入(局部变量)

(3)@Import

  1. 可以引入单个bean
  2. 可以引入一个配置类
  3. 可以通过selector引入多个类
  4. 可以通过beanDefinition注册器引入
  5. @import和@Bean引入同一个对象问题:
    • 统一配置类中,@Import先解析@Bean后解析
    • 同名定义,默认后面解析的会覆盖前面解析的
    • @Import引入的类实现DeferredImportSelector接口,则会先解析@Bean,再解析@Import

5)Aware 接口

  1. Aware 接口提供了一种【内置】 的注入手段,例如
    • BeanNameAware 注入 bean 的名字
    • BeanFactoryAware 注入 BeanFactory 容器
    • ApplicationContextAware 注入 ApplicationContext 容器
    • EmbeddedValueResolverAware 注入 ${} 解析器
  2. InitializingBean 接口提供了一种【内置】的初始化手段
  3. DisposableBean 接口提供了一种【内置】的销毁手段
  4. 对比:
    • 内置的注入和初始化不受扩展功能的影响,总会被执行
    • 而扩展功能受某些情况影响可能会失效,因此 Spring 框架内部的类常用内置注入和初始化
(1)@Autowired失效分析
  1. 如果出现Java配置类包含BeanFactory后处理器的情况,因此要创建其中的BeanFactory后处理器必须提前创建Java配置类,而此时的Bean后处理器还未准备好,导致@Autowired等注解失效
  2. 解决方法:
    • 用内置依赖注入和初始化取代拓展依赖注入和初始化
    • 用静态工厂方法代替实例工厂方法,避免工厂对象提前被创建

6)初始化与销毁

(1)初始化销毁顺序

初始化方法顺序

  1. @PostConstruct 标注的初始化方法
  2. InitializingBean 接口的初始化方法
  3. @Bean(initMethod) 指定的初始化方法

销毁方法顺序

  1. @PreDestroy 标注的销毁方法
  2. DisposableBean 接口的销毁方法
  3. @Bean(destroyMethod) 指定的销毁方法

7)Scope

  1. 在当前版本的 Spring 和 Spring Boot 程序中,支持五种 Scope

    • singleton,容器启动时创建(未设置延迟),容器关闭时销毁
    • prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
    • request,每次请求用到此 bean 时创建,请求结束时销毁
    • session,每个会话用到此 bean 时创建,会话结束时销毁
    • application,web 容器用到此 bean 时创建,容器停止时销毁
  2. 但要注意,如果在 singleton 注入其它 scope 会发现注入的都是同一个对象,解决方法有:

    • @Lazy
    • @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    • ObjectFactory
    • ApplicationContext.getBean

8)FactoryBean

  1. 它的作用是用制造创建过程较为复杂的产品, 如 SqlSessionFactory, 但 @Bean 已具备等价功能
  2. 被 FactoryBean 创建的产品
    • 会认为创建、依赖注入、Aware 接口回调、前初始化这些都是 FactoryBean 的职责, 这些流程都不会走
    • 唯有后初始化的流程会走, 也就是产品可以被代理增强
    • 单例的产品不会存储于 BeanFactory 的 singletonObjects 成员中, 而是另一个 factoryBeanObjectCache 成员中
  3. 按名字去获取时, 拿到的是产品对象, 名字前面加 & 获取的是工厂对象

AOP

AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能

除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中
  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能
  • 作为对比,之前学习的代理是运行时生成新的字节码

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高
  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

1)JDK动态代理

  1. jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系。代理增强是借助多态来实现,因此成员变量、静态方法、final方法均不能通过代理实现
  2. 反射方法优化:
    • 前16次反射性能较低
    • 第17次调用会生成代理类,优化为非反射调用

2)Cglib代理

  1. 不要求目标实现接口,它生成的代理类是目标的子类,因此代理目标之间是子父关系,因此final类无法被cglib增强
  2. 调用目标方法:
    • method.invoke 是反射调用,必须调用到足够次数才会进行优化
    • methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
    • methodProxy.invokeSuper 是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象

3)Spring选择代理

ProxyFactory 用来创建代理

  • 如果指定了接口,且 proxyTargetClass = false(默认为false),使用 JdkDynamicAopProxy
  • 如果没有指定接口,或者 proxyTargetClass = true,使用 ObjenesisCglibAopProxy
  • 例外:如果目标是接口类型或已经是 Jdk 代理,使用 JdkDynamicAopProxy

4)从 @Aspect 到 Advisor

(1)代理创建器

  1. AnnotationAwareAspectJAutoProxyCreator 的作用
    • 将高级 @Aspect 切面统一为低级 Advisor 切面
    • 在合适的时机创建代理
  2. findEligibleAdvisors 找到有【资格】的 Advisors
    • 有【资格】的 Advisor 一部分是低级的, 可以由自己编写
    • 有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得
  3. wrapIfNecessary
    • 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
    • 它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行

(2)代理创建时机

  1. 代理的创建时机
    • 初始化之后 (无循环依赖时)
    • 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存
  2. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象

5)Spring代理特点

  1. 依赖注入和初始化影响的是原始对象,因此 cglib 不能用 MethodProxy.invokeSuper()
  2. 代理与目标是两个对象,二者成员变量并不共用数据
  3. static 方法、final 方法、private 方法均无法增强,代理增强基于方法重写

IOC

  1. IOC是反转控制,指的是创建对象由Spring容器管理,并由Spring根据配置文件创建对象实例,管理各个实例之间的依赖关系,有利于功能的复用
  2. ID依赖注入和IOC是一个概念,使用不同的角度描述,其应用程序在运行时依赖IOC容器动态注入外部对象
  3. 以前创建对象是由程序员主动创建的,IOC让对象创建不用再 new 了,有Spring容器自动生产,使用Java反射机制完成,程序在运行时根据配置文件动态创建对象以及管理对象,并调用对象的属性和方法
  4. Spring IOC的注入方式:
    • 构造器注入
    • setter注入
    • 注解注入

Spring循环依赖

  1. Spring是如何解决循环依赖问题的?
    • Spring是通过三级缓存解决了循环依赖,其中以及缓存为单例池(singletonObjects),二级缓存为早期曝光对象(earlySingletonObjects),三级缓存为早期曝光对象工厂(singletonFactories)
    • 当A、B两个类发生循环依赖时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化后的对象。当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取所需要的依赖,此时的getBean(a)会从缓存中获取。第一步,先获取到三级缓存中的工厂,第二步,调用对象工厂的getObject方法来获取到对应的对象,在将对象放入到二级缓存中,同时移除三级缓存中的工厂,最后将这个对象注入到B中,紧接着B会走完它剩下的流程。当B创建完成后,会将B再注入到A中,此时A再进行初始化,之后清除二级缓存,将初始化的Bean放入一级缓存,最后A创建完成。
  2. 为什么要用三级缓存?二级缓存能解决问题依赖吗?
    • 如果要使用二级缓存解决循环依赖问题,意味着所有的Bean在实例化就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理
  3. 构造方法及多例循环依赖解决办法:
    • @Lazy
    • @Scope
    • ObjectFactory 或 ObjextProvider
    • Provider
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值