欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

springIOC容器依赖注入的原理解析

程序员文章站 2022-10-03 14:48:08
门面模式:抽离出个门面类,将一系列底层操作整合成一个操作。模版模式:围绕抽象类,实现通用逻辑,定义模版结构,部分逻辑由子类实现 :复用代码,反向控制。 模版方法(方法的组合)、具体方法(自带方法)、钩子方法(视情况实现的方法)、抽象方法(必须由子类实现的方法)策略模式:委派模式:责任链模式:后置处理器事件监听器模式:单例模式:getObjectFromFactoryBeanSpring抓住主心骨进行学习:解析配置定位与注册对象注入对象全局掌握核心接口和类解决了关键的问题:将...
  • 工厂模式:供外部使用的FactoryBean 和内部使用的ObjectFactory 通过其getObject获取到的才是想要的对象。
  • 门面模式:抽离出个门面类,将一系列底层操作整合成一个操作。
  • 模版模式:围绕抽象类,实现通用逻辑,定义模版结构,部分逻辑由子类实现 :复用代码,反向控制。
    模版方法(方法的组合)、具体方法(自带方法)、钩子方法(视情况实现的方法)、抽象方法(必须由子类实现的方法)
  • 策略模式:
  • 委派模式:
  • 责任链模式:后置处理器
  • 事件监听器模式: 容器初始化各阶段都有各种事件发布,由所注册的监听器进行处理
  • 单例模式:getObjectFromFactoryBean 容器本身是单例的 枚举方式, Bean默认也是单例的,在doGetBean()方法从三级缓存中获取Bean实例时也用到了双重校验锁。

一、 Spring

抓住主心骨进行学习:

  • 解析配置

  • 定位与注册对象

  • 注入对象

全局掌握核心接口和类

  • 解决了关键的问题:将对象之间的关系转而配置来管理

  • 依赖注入–依赖关系在Spring的IoC容器中管理

通过把对象包装在Bean中达到管理对象和进行额外操作的目的

1.Bean和BeanDefination

  • Bean的本质是java对象,只是这个对象的生命周期由容器来管理
  • 不需要为了创建Bean而在原来的java类上添加任何额外的限制
  • 对java对象的控制体现在配置上

根据配置,生成用来描述Bean的BeanDefination,常用属性:

  • 作用范围scope(@Scope) singleton session prototype globalsession request
  • 懒加载lazy-init(@Lazy):决定Bean实例是否延迟加载
  • 首选primary(@Primary):设置为true的Bean会是优先的实现类
  • factory-bean和factory-method(@Configuration @Bean)
    使用factory-bean中的factory-method进行创建bean,如果只有factory-method通过class的factory-method设置的方法

容器初始化主要做的事情(主要脉络)
springIOC容器依赖注入的原理解析

  • 解析配置
  • 定位与注册对象

BeanDefinition家族:
BeanDefinition全介绍

springIOC容器依赖注入的原理解析

spring源码分析之BeanDefinition相关

Bean的继承关系是在BeanDefinition里设置Parentname属性体现的。

  1. AttributeAccessor 接口提供相关属性attribute的设置和读取操作设置属性可以通过xml的设置
  2. BeanMetadataElement接口获取一个包含元数据元素的配置源对象
  3. AttributeAccessorSupport抽象类 实现了AttributeAccessor接口 使用Map来提供属性的访问和操作
  4. BeanMetadataAttributeAccessor类继承AttributeAccessorSupport实现BeanMetadataElement 新增的功能是访问BeanMetadataAttribute类型的属性。

Spring中Bean的继承属性是通过BeanDefinition中的parent属性进行标识的,可以获取到父Bean的配置,后续在Bean的创建时会有个合并Bean方法,就是来处理parent属性的。

2.Spring容器

BeanFactory 是所有容器的根接口

补充:BeanFactory和FactoryBean的区别: FactoryBean不是容器,实现FactoryBean接口的Bean可以自定义Bean的创建方法。

//对FactoryBean的转义定义,提供获取FactoryBean的方式,
// 如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义String FACTORY_BEAN_PREFIX = “&”;

简单容器:springIOC容器依赖注入的原理解析

DefaultListableBeanFactory是第一个可以实际使用的BeanFactory 内部有一个 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

复杂容器(通常也叫上下文):springIOC容器依赖注入的原理解析

术语

组件扫描:自动发现应用容器中需要创建的Bean。

自动装配:自动满足Bean之间的依赖。

复杂容器

ApplicationContext:public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver

EnvironmentCapable: 只有一个方法getEnviroment();获取一些启动变量

ListableBeanFactory:以列表形式操作Bean,如获取Bean的个数

HierarchicalBeanFactory:支持Bean的分级

MessageSource:国际化相关接口

ResourcePatternResolver:加载配置相关接口

ApplicationEventPublisher:具备事件发送的能力

传统基于XML配置的容器

FileSystemXmlApplicationContext:从文件系统中加载配置

ClassPathXmlApplicationContext:从classpath加载配置

XmlWebApplicationContext:用于Web应用程序的容器

目前比较流行的容器

AnnotationConfigServletWebServerApplicationContext

AnnotationConfigReactiveWebServerApplicationContext

AnnotationConfigApplicationContext

注解和xml配置方式的容器是不一样的,主要学习容器的共性 refresh()方法是主流容器都实现的方法,

refresh()大致功能 用到了模版模式,是一个非常典型的模版方法。

  • 容器初始化、配置解析
  • BeanFactoryPostProcessor和BeanPostProcessor的注册和激活
  • 国际化配置

……

ps:AbstractApplicationContext

3.资源加载

弄清Resource、ResourceLoader和容器之间的微妙关系。
springIOC容器依赖注入的原理解析

Resource:能实现加载配置 如从类路径,文件等 只支持读操作。

ServletContextResource:

ClassPathResource:用来访问类加载路径里的资源,例如可自动搜索WEB-INF/classes目录下的配置文件。

FileSystemResource: 访问系统文件资源

强大的资源加载方式
springIOC容器依赖注入的原理解析

ResourceLoader: 根据传入的资源地址,自动识别classpath: file: 风格 按需返回特定的Resource getResource(String location) getClassLoader() 用到了策略模式

ResourcePatternResolver: 加持了getResource方法,Resource[] getResources(String locationPattern) throws IOException; 可以返回多个Resource.

ApplicationContext实现了ResourcePatternResolver接口,

AbstractApplicationContext继承了DefaultResourceLoader,且内部有个函数可以返回PathMatchingResourcePatternResolver,在构造函数时进行调用,赋值给成员变量。 这是任何ApplicationContext的实现都支持统一资源加载的原因.

/** ResourcePatternResolver used by this context. */ /** 此上下文使用的 ResourcePatternResolver 资源模式解析器 */ private ResourcePatternResolver resourcePatternResolver; /**
	 * Create a new AbstractApplicationContext with no parent.
	 * 创建一个没有父元素的新的AbstractApplicationContext
	 */ public AbstractApplicationContext() { this.resourcePatternResolver = getResourcePatternResolver(); } /**
	 * 返回此 ResourcePatternResolver资源模式解析器,
	 * 用于将多个资源的位置按照模式解析到资源实例中。
	 * 默认是org.springframework.core.io.support.PathMatchingResourcePatternResolver。
	 * 支持ant风格的位置模式。
	 * 可以在子类中重写,用于扩展解析策略,例如在web环境中。在需要解决位置模式时不要调用此函数。
	 * 相反,调用上下文的getResources方法,它将委托给ResourcePatternResolver。
	 * @return 此应用上下文的 ResourcePatternResolver 资源模式解析器
	 * @see #getResources
	 * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
	 */ protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this); } //--------------------------------------------------------------------- // ResourcePatternResolver 接口的实现 //---------------------------------------------------------------------  @Override public Resource[] getResources(String locationPattern) throws IOException { return this.resourcePatternResolver.getResources(locationPattern); } 

BeanDefinitionReader 主要方法 loadBeanDefinitions

利器ResourceLoader的使用者 BeanDefinitionReader

  • 读取BeanDefinition
  • BeanDefinitionRegistry 可以根据配置文件将BeanDefinition注册到容器里
    -springIOC容器依赖注入的原理解析

BeanDefinitionRegistry getRegistry() 将BeanDefinition注册到BeanDefinition的注册表中

getBeanNameGenerator() 为匿名的 bean生成id

BeanDefinitionReader学习过程中的关键词

location、BeanDefinitionReader

Resource 、BeanDefinitionRegistry

Resourceloader、DefaultListableBeanFactory

4.BeanDefinition的注册

本质就是将配置的bean解析成beandefinition并将其注册到defaultListableBeanFactory容器的beanDefinitionMap中。

xml配置的资源定位、加载、解析、注册全链路解析
springIOC容器依赖注入的原理解析

5.小结:

springIOC容器依赖注入的原理解析

xml方式 BeanDefinition的注册是在refresh方法进行。

注解方式会分为三种BeanDefinition进行处理, 内置BeanDefinition 容器构造函数时进行处理、@Configuration 容器构造函数中调用register方法构造 剩下的在refresh()容器级别后置处理器处理的

二、详解SpringIoC容器的初始化 【打通refresh方法的全链路】

容器初始化主要做的事情(主线脉络)

即AbstractApplicationContext.refresh()

前沿知识:

1.后置处理器PostProcessor

本身也是一种需要注册到容器里的Bean

  • 其里面的方法会在特定的时机被容器调用
  • 实现不改变容器或者Bean核心逻辑的情况下对Bean进行扩展
  • 对Bean进行包装,影响其行为、修改Bean的内容等

大类分为容器类别的后置处理器以及Bean级别的后置处理器

BeanDefinitionRegistryPostProcessor

BeanFactoryPostProcessor

BeanPostProcessor

前两个属于容器级别、后一个属于Bean级别

可以做一些骚操作,通过后置处理器将第三方框架的类加载到spring 容器中,进而利用相应的功能,目前mybatis就是这么做的。

BeanPostProcessor不能对@Configuration的类进行增强。

2.Aware及其子接口

一般Bean不需要获取容器的状态,如果要从Bean里获取到的容器实例并对其进行操作,就需要Aware。这里需要ApplicationContextAware

不同类型的Aware可以召唤不同的神兽 。
springIOC容器依赖注入的原理解析

3.事件监听器模式

回调函数

往组件注册自定义的方法以便组件在特定场景下调用

监听器将监听感兴趣的事件,一旦事件发生,便做出响应

  • 事件源 event source
  • 事件监听器 event listener
  • 事件对象 event object

Spring的事件驱动模型

事件驱动模型的三大组成部分

事件:ApplicationEvent抽象类 继承EventObject
springIOC容器依赖注入的原理解析

事件监听器: ApplicationListener
springIOC容器依赖注入的原理解析

事件发布器(事件源):ApplicationEventPublisher以及ApplicationEventMulticaster

ApplicationEventPublisher:只有发布事件的能力。

ApplicationEventMulticaster:提供管理listener的方法和发布事件的方法。
springIOC容器依赖注入的原理解析

像Bean和容器只想发布事件不想维护listener。所以进行了分割。

ApplicationEventMulticaster功能是完备的,给ApplicationEventPublisher作为代理去使用。就像ApplicationContext使用 DefaultListableBeanFactory一样。

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { private final DefaultListableBeanFactory beanFactory; /**
	 * Create a new GenericApplicationContext.
	 * @see #registerBeanDefinition
	 * @see #refresh
	 */ public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); } /**
	 * Create a new GenericApplicationContext with the given DefaultListableBeanFactory.
	 * @param beanFactory the DefaultListableBeanFactory instance to use for this context
	 * @see #registerBeanDefinition
	 * @see #refresh
	 */ public GenericApplicationContext(DefaultListableBeanFactory beanFactory) { Assert.notNull(beanFactory, "BeanFactory must not be null"); this.beanFactory = beanFactory; } 

4.手撕spring容器的刷新逻辑。

springIOC容器依赖注入的原理解析

5.小结:

springIOC容器依赖注入的原理解析

三、SpringIoC容器的依赖注入(攻坚Bean实例的创建)

1.

getBean - doGetBean- createBean - doCreateBean

这里是以@Autowire注入为例讲解 学习路线以@Autowire的解析和自动装配进行

doGetBean可以根据scope进行创建,这里以Singleton为例。
springIOC容器依赖注入的原理解析

2.doGetBean

从缓存获取Bean
springIOC容器依赖注入的原理解析

一级缓存 singletonObjects 最终形态的单例实例
二级缓存 earlySingletonObjects 不完备的实例
三级缓存 singletonFactories 这里不存储的不是最终Bean实例,通过他的getObject()方法获取bean实例

主要是为了解决循环依赖的问题。

实例只能存在某一级缓存里, 一级缓存singletonObjects为ConcurrentHashMap 其余为HashMap

3.强攻Bean的创建_CreateBean方法

springIOC容器依赖注入的原理解析

如果在Bean实例化前的后置处理 就创建了Bean就直接返回了。

处理方法覆盖:看是否Bean配置了lookup和replace method属性并做相关的标记。

4.doCreadteBean

springIOC容器依赖注入的原理解析

处理@Autowired和@Value标签: 其实是通过后置处理器 如AutowiredAnnotationBeanPostProcessor会把bean其中被@Autowired修饰的数据封装为InjectionMetadara ,其中InjectedElement就是一个个被@Autowired修饰的

private final Class<?> targetClass; // 当post-processor处理bean时,会解析bean Class的所有属性, // 在解析时会判断属性上是否标有@Value或者@Autowired注解, // 有就解析这个属性值,将解析后结果放入这里 // 保存了被注入元素的全量集合(包括Spirng处理的或者外部处理的) private final Collection<InjectedElement> injectedElements; //和injectedElements一样,不过只保存了由Spring容器默认进行处理的属性或者方法 @Nullable private volatile Set<InjectedElement> checkedElements; 

是否允许提前暴露:

//向容器中缓存单例模式的Bean对象,以防循环引用
//判断是否是早期引用的bean,如果是,则允许其提前暴露引用
//这里判断的逻辑主要有三个:
//1.是否为单例
/2.是否允许循环引用
//3.是否是在创建中的bean

5.循环依赖与三级缓存
springIOC容器依赖注入的原理解析


问题 BeanDefinitionRegistry 和BeanFactory 都存放beandefinition吗?

DefaultListableBeanFactory本身就实现了BeanDefinitionRegistry接口。所以我们使用的IoC容器本身就是BeanDefinitionRegistry的实现类。

BeanPostProcessor责任链模式,各种实现BeanPostProcessor的实现类为Bean提供各种精细化的控制

本文地址:https://blog.csdn.net/weixin_44998135/article/details/108254743