Spring 源码(9)Spring Bean的创建过程的前期准备
回顾总结
到目前为止,Spring
源码中AbstractApplicationContext#refresh
方法的已经解读到第11个方法finishBeanFactoryInitialization
,前10个方法介绍了:
BeanFactory
的准备,创建,刷新,个性化BeanFactory
的扩展点,自定义属性解析;- 环境信息
Environment
的加载(包括环境变量、系统变量等); BeanDefinition
的加载,解析,自定义xml
的方式;BeanFactoryPostProcessor
的注册与执行流程,BeanDefinitionRegistryPostProcessor
的解析,ConfigurationClassPostProcessor
对Spring
注解的解析过程(@Component、@PropertySources、@PropertySource、@ComponentScans、@ComponentScan、@Import
等注解的解析),Spring Boot
是如何通过@Configuration+@Import + ImportSelector
进行自动装配的等;BeanPostProcessor
的注册流程;- 国际化,
Spring
事件驱动的加载执行过程;
finishBeanFactoryInitialization 解析过程
接下来开始解析Spring
对Bean
的创建过程,上源码:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
// 设置转换服务,转换服务用来对属性值进行解析的
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no BeanFactoryPostProcessor
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
// 如果之前没有注册过任何 BeanFactoryPostProcessor(例如 PropertySourcesPlaceholderConfigurer bean),
// 则注册一个默认的嵌入值解析器:此时,主要用于解析注释属性值。
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
// 允许缓存所有 bean 定义元数据,而不是期望进一步的更改
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
// 实例化所有剩余的(非惰性初始化)单例
beanFactory.preInstantiateSingletons();
}
- 判断是否存在转换服务,有就设置
- 判断是否有内置的值解析器,没有就创建一个处理占位符的解析器
- 实例化LoadTimeWeaverAware,进行早期的Bean的创建
- 停止使用临时的类加载器
- 冻结BeanDefinition的元数据信息,防止被修改
- 开始实例化所有的单例bean对象
除了beanFactory.preInstantiateSingletons()
方法,其他都是Bean
创建的准备,接下来一个一个分析,首先是转换服务的设置。
转换服务ConversionService的初始化
方法一开始设置了一个转换服务,这个转换服务在Spring
中还是非常的重要的,比如我们xml
中配置一个String
类型的属性值,但是在Bean
的定义中是一个Integer
类型的,这时Spring
就会自动帮我们转出来,他是怎么做的呢?
在Spring
中有几个比较重要的接口:
Converer
用于将对象S
转换为对象T
ConverterFactory
一个转换工厂,能够将对象S
转成一类对象R
的子集T
,比如可以将字符串S
转换为T
(Integer、Long
等)Number
类型R
的子集GenericConverter
支持多种类型之间互相转换。
Spring
转换器接口ConversionService
的默认实现是DefaultConversionService
,这个默认的转换器实现中,内置了很多的转换器,比如:
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
public static void addCollectionConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
// 数组转集合
converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
// 集合转数组
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
converterRegistry.addConverter(new MapToMapConverter(conversionService));
// 数组转字符串
converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
converterRegistry.addConverter(new StringToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
converterRegistry.addConverter(new StreamConverter(conversionService));
}
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));
converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharsetConverter());
converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCurrencyConverter());
converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}
可以说是非常的丰富的,基本上常见都是Spring提供了,非常贴心。
那么怎么使用呢?
不懂当然是上官网:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#core-convert ,这里可以看到我们只需要将ConversionServiceFactoryBean
配置到Spring容器中就可以了,Spring
内置的转换器就可以工作了,非常方便。
ConversionServiceFactoryBean
实现了FactoryBean
接口和InitializingBean
接口,而InitializingBean#afterPropertiesSet
是初始化Bean
过程中需要执行的。ConversionServiceFactoryBean
源码中:
@Override
public void afterPropertiesSet() {
this.conversionService = createConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}
// 构造函数
public DefaultConversionService() {
// 添加默认的转换器
addDefaultConverters(this);
}
可以看到这个ConversionServiceFactroyBean
就是用来初始化转换器的,并且这个类还提供了扩展,可以自定义转换器加入到转换器集合中。
自定义转换器
自定义String转Integer类型的转换器:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class StringToIntegerConverter implements Converter<String,Integer> , ConditionalConverter {
@Override
public Integer convert(String source) {
return NumberUtils.parseNumber(source,Integer.class);
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
System.out.println(sourceType.getType());
System.out.println(targetType.getType());
return true;
}
}
逻辑非常简单,直接调用Spring
提供的工具类进行转换。
配置xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.redwinter.selfconverter"/>
<!--配置转化器-->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.redwinter.selfconverter.StringToIntegerConverter"/>
</set>
</property>
</bean>
</beans>
创建转换器客户端:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
@Service
public class MyConverter {
private final ConversionService conversionService;
public MyConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void test(String source){
System.out.println(conversionService.convert(source, Integer.class));
}
}
创建测试:
/**
* @author <a href="2360564660@qq.com">redwinter</a>
* @since 1.0
**/
public class FactoryBeanTest {
@Test
public void test(){
MyClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("spring-factory.xml");
MyConverter myConverter = context.getBean(MyConverter.class);
myConverter.test("12345");
}
}
输出:
class java.lang.String
class java.lang.Integer
12345
分析完转换服务,接下来分析 值解析器的添加。
默认的值解析器
// 省略代码.....
// Register a default embedded value resolver if no BeanFactoryPostProcessor
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
// 如果之前没有注册过任何 BeanFactoryPostProcessor(例如 PropertySourcesPlaceholderConfigurer bean),
// 则注册一个默认的嵌入值解析器:此时,主要用于解析注释属性值。
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// 省略代码.....
首先判断了容器中是否存在嵌入的值解析器,如果没有就添加一个进去,这里添加进去的是StringValueResolver
,点击resolvePlaceHolders
方法进去,最终会在AbstractPropertyResolver#resolvePlaceholders
中创建一个PropertyPlaceholderHelper
类
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
// 前缀为 ${ ,后缀为 },值的分隔符为 : ,比如 ${username:zhansan} username没有的话,后面的为默认的值
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, ignoreUnresolvablePlaceholders);
}
如果已经注册过一个BFPP
的占位符解析器的话,就不需要在注册了,BFPP
的占位符解析器就是PropertySourcesPlaceholderConfigurer
,专门用于解析占位符的,比如在xml
中或者yaml
中,配置类似于${jdbc.username}
这种格式的,就会被解析器解析。PropertySourcesPlaceholderConfigurer
这个解析器实现了BeanFactoryPostProcessor
接口,能够对BeanDefinition
进行处理,当然也可以对属性值进行处理。
分析完值解析器,继续往下分析。
Bean创建前的其他准备
// 省略代码.....
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
// 在prepareBeanFactory 准备BeanFactory时设置进去的,如果存在,则开始早期Bean的创建
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
// 停止使用临时的类加载器,这里也是在准备BeanFactory时设置进去的
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
// 允许缓存所有 bean 定义元数据,而不是期望进一步的更改
beanFactory.freezeConfiguration();
// 省略代码.....
这里从容器中获取了AOP
的织入,如果有的话就开始进行早期的Bean
的创建;然后停止了临时的类加载器;然后就是冻结BeanDefinition
的元数据信息。
public void freezeConfiguration() {
this.configurationFrozen = true;
this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
}
点击进来,其实就是设置了标识,防止后期对BeanDefinition
的修改。
这前面的几个判断和方法实际上都是Bean
创建的准备工作,接下来开始分析preInstantiateSingletons
预实例化所有的单例Bean
。
Spring 源码(9)Spring Bean的创建过程的前期准备的更多相关文章
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Spring 源码分析之 bean 依赖注入原理(注入属性)
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
- Spring源码-IOC部分-Bean实例化过程【5】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- dubbo源码分析3-service bean的创建与发布
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- Spring源码分析专题 —— IOC容器启动过程(上篇)
声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...
- 【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- Spring 源码分析之 bean 实例化原理
本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...
- 【Spring源码分析】Bean加载流程概览(转)
转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...
- Spring源码分析:Bean加载流程概览及配置文件读取
很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...
随机推荐
- 模糊查询 like 语句该怎么写?
第 1 种:在 Java 代码中添加 sql 通配符. string wildcardname = "%smi%"; list<name> names = mapper ...
- 简述 synchronized 和 java.util.concurrent.locks.Lock 的异同?
Lock 是 Java 5 以后引入的新的 API,和关键字 synchronized 相比主要相同点: Lock 能完成 synchronized 所实现的所有功能:主要不同点:Lock 有比 sy ...
- Mac SVN Cornerstone 安装使用详解
Cornerstone 是收费软键,这里提供一个破解版 ,安装后需要输入安装密码:www.ifunmac.com 链接:https://pan.baidu.com/s/1LqY2s8vEJAQ9JJh ...
- 我们可以在 hashcode() 中使用随机数字吗?
不行,因为对象的 hashcode 值必须是相同的.参见答案获取更多关于 Java 中 重写 hashCode() 方法的知识.
- Tomcat配置文件之“server.xml”解析
<?xml version="1.0" encoding="UTF-8"?> <!--代表整个Servlet容器组件,是最顶层元素.它里面可以 ...
- C语言之API
C语言之API 1.输入(控制台输入) scanf("%d,%d",&a,&b); 2.输出(打印数值) printf("max=%d\n",c ...
- C语言思维导图—自己整理的
- Architecture Principles
Architecture Principles - Completed Components Name Statement Rationale Implications TOGAF Principle ...
- 登陆界面回车(enter)点击登陆;
<script>//注册按键事件document.onkeydown = keyListener;function keyListener(e) {// 当按下回车键,点buttonif ...
- 小程序wx.createInnerAudioContext()获取不到时长问题
最近在开发小程序中,需要用到音频播放功能.但在初始化时,使用InnerAudioContext.duration获取不到音频的时长. Page({ /** * 生命周期函数--监听页面初次渲染完成 * ...