回顾总结

到目前为止,Spring源码中AbstractApplicationContext#refresh方法的已经解读到第11个方法finishBeanFactoryInitialization,前10个方法介绍了:

  • BeanFactory的准备,创建,刷新,个性化BeanFactory的扩展点,自定义属性解析;
  • 环境信息Environment的加载(包括环境变量、系统变量等);
  • BeanDefinition的加载,解析,自定义xml的方式;
  • BeanFactoryPostProcessor的注册与执行流程,BeanDefinitionRegistryPostProcessor的解析,ConfigurationClassPostProcessorSpring注解的解析过程(@Component、@PropertySources、@PropertySource、@ComponentScans、@ComponentScan、@Import等注解的解析),Spring Boot 是如何通过@Configuration+@Import + ImportSelector进行自动装配的等;
  • BeanPostProcessor的注册流程;
  • 国际化,Spring事件驱动的加载执行过程;

finishBeanFactoryInitialization 解析过程

接下来开始解析SpringBean的创建过程,上源码:

  1. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  2. // Initialize conversion service for this context.
  3. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
  4. beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
  5. // 设置转换服务,转换服务用来对属性值进行解析的
  6. beanFactory.setConversionService(
  7. beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  8. }
  9. // Register a default embedded value resolver if no BeanFactoryPostProcessor
  10. // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
  11. // at this point, primarily for resolution in annotation attribute values.
  12. // 如果之前没有注册过任何 BeanFactoryPostProcessor(例如 PropertySourcesPlaceholderConfigurer bean),
  13. // 则注册一个默认的嵌入值解析器:此时,主要用于解析注释属性值。
  14. if (!beanFactory.hasEmbeddedValueResolver()) {
  15. beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
  16. }
  17. // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
  18. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  19. for (String weaverAwareName : weaverAwareNames) {
  20. getBean(weaverAwareName);
  21. }
  22. // Stop using the temporary ClassLoader for type matching.
  23. beanFactory.setTempClassLoader(null);
  24. // Allow for caching all bean definition metadata, not expecting further changes.
  25. // 允许缓存所有 bean 定义元数据,而不是期望进一步的更改
  26. beanFactory.freezeConfiguration();
  27. // Instantiate all remaining (non-lazy-init) singletons.
  28. // 实例化所有剩余的(非惰性初始化)单例
  29. beanFactory.preInstantiateSingletons();
  30. }
  • 判断是否存在转换服务,有就设置
  • 判断是否有内置的值解析器,没有就创建一个处理占位符的解析器
  • 实例化LoadTimeWeaverAware,进行早期的Bean的创建
  • 停止使用临时的类加载器
  • 冻结BeanDefinition的元数据信息,防止被修改
  • 开始实例化所有的单例bean对象

除了beanFactory.preInstantiateSingletons() 方法,其他都是Bean创建的准备,接下来一个一个分析,首先是转换服务的设置。

转换服务ConversionService的初始化

方法一开始设置了一个转换服务,这个转换服务在Spring中还是非常的重要的,比如我们xml中配置一个String 类型的属性值,但是在Bean的定义中是一个Integer类型的,这时Spring就会自动帮我们转出来,他是怎么做的呢?

Spring中有几个比较重要的接口:

  • Converer 用于将对象S转换为对象T
  • ConverterFactory 一个转换工厂,能够将对象S转成一类对象R的子集T,比如可以将字符串S转换为TInteger、Long等)Number类型R的子集
  • GenericConverter支持多种类型之间互相转换。

Spring转换器接口ConversionService 的默认实现是DefaultConversionService,这个默认的转换器实现中,内置了很多的转换器,比如:

  1. public static void addDefaultConverters(ConverterRegistry converterRegistry) {
  2. addScalarConverters(converterRegistry);
  3. addCollectionConverters(converterRegistry);
  4. converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
  5. converterRegistry.addConverter(new StringToTimeZoneConverter());
  6. converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
  7. converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
  8. converterRegistry.addConverter(new ObjectToObjectConverter());
  9. converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
  10. converterRegistry.addConverter(new FallbackObjectToStringConverter());
  11. converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
  12. }
  13. public static void addCollectionConverters(ConverterRegistry converterRegistry) {
  14. ConversionService conversionService = (ConversionService) converterRegistry;
  15. // 数组转集合
  16. converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
  17. // 集合转数组
  18. converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
  19. converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
  20. converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
  21. converterRegistry.addConverter(new MapToMapConverter(conversionService));
  22. // 数组转字符串
  23. converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
  24. converterRegistry.addConverter(new StringToArrayConverter(conversionService));
  25. converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
  26. converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
  27. converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
  28. converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
  29. converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
  30. converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
  31. converterRegistry.addConverter(new StreamConverter(conversionService));
  32. }
  33. private static void addScalarConverters(ConverterRegistry converterRegistry) {
  34. converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
  35. converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
  36. converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
  37. converterRegistry.addConverter(new StringToCharacterConverter());
  38. converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
  39. converterRegistry.addConverter(new NumberToCharacterConverter());
  40. converterRegistry.addConverterFactory(new CharacterToNumberFactory());
  41. converterRegistry.addConverter(new StringToBooleanConverter());
  42. converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
  43. converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
  44. converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));
  45. converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
  46. converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));
  47. converterRegistry.addConverter(new StringToLocaleConverter());
  48. converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
  49. converterRegistry.addConverter(new StringToCharsetConverter());
  50. converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
  51. converterRegistry.addConverter(new StringToCurrencyConverter());
  52. converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
  53. converterRegistry.addConverter(new StringToPropertiesConverter());
  54. converterRegistry.addConverter(new PropertiesToStringConverter());
  55. converterRegistry.addConverter(new StringToUUIDConverter());
  56. converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
  57. }

可以说是非常的丰富的,基本上常见都是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源码中:

  1. @Override
  2. public void afterPropertiesSet() {
  3. this.conversionService = createConversionService();
  4. ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
  5. }
  6. protected GenericConversionService createConversionService() {
  7. return new DefaultConversionService();
  8. }
  9. // 构造函数
  10. public DefaultConversionService() {
  11. // 添加默认的转换器
  12. addDefaultConverters(this);
  13. }

可以看到这个ConversionServiceFactroyBean就是用来初始化转换器的,并且这个类还提供了扩展,可以自定义转换器加入到转换器集合中。

自定义转换器

自定义String转Integer类型的转换器:

  1. /**
  2. * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
  3. * @since 1.0
  4. **/
  5. public class StringToIntegerConverter implements Converter<String,Integer> , ConditionalConverter {
  6. @Override
  7. public Integer convert(String source) {
  8. return NumberUtils.parseNumber(source,Integer.class);
  9. }
  10. @Override
  11. public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
  12. System.out.println(sourceType.getType());
  13. System.out.println(targetType.getType());
  14. return true;
  15. }
  16. }

逻辑非常简单,直接调用Spring提供的工具类进行转换。

配置xml:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. 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">
  6. <context:component-scan base-package="com.redwinter.selfconverter"/>
  7. <!--配置转化器-->
  8. <bean class="org.springframework.context.support.ConversionServiceFactoryBean">
  9. <property name="converters">
  10. <set>
  11. <bean class="com.redwinter.selfconverter.StringToIntegerConverter"/>
  12. </set>
  13. </property>
  14. </bean>
  15. </beans>

创建转换器客户端:

  1. /**
  2. * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
  3. * @since 1.0
  4. **/
  5. @Service
  6. public class MyConverter {
  7. private final ConversionService conversionService;
  8. public MyConverter(ConversionService conversionService) {
  9. this.conversionService = conversionService;
  10. }
  11. public void test(String source){
  12. System.out.println(conversionService.convert(source, Integer.class));
  13. }
  14. }

创建测试:

  1. /**
  2. * @author <a href="2360564660@qq.com">redwinter</a>
  3. * @since 1.0
  4. **/
  5. public class FactoryBeanTest {
  6. @Test
  7. public void test(){
  8. MyClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("spring-factory.xml");
  9. MyConverter myConverter = context.getBean(MyConverter.class);
  10. myConverter.test("12345");
  11. }
  12. }

输出:

  1. class java.lang.String
  2. class java.lang.Integer
  3. 12345

分析完转换服务,接下来分析 值解析器的添加。

默认的值解析器

  1. // 省略代码.....
  2. // Register a default embedded value resolver if no BeanFactoryPostProcessor
  3. // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
  4. // at this point, primarily for resolution in annotation attribute values.
  5. // 如果之前没有注册过任何 BeanFactoryPostProcessor(例如 PropertySourcesPlaceholderConfigurer bean),
  6. // 则注册一个默认的嵌入值解析器:此时,主要用于解析注释属性值。
  7. if (!beanFactory.hasEmbeddedValueResolver()) {
  8. beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
  9. }
  10. // 省略代码.....

首先判断了容器中是否存在嵌入的值解析器,如果没有就添加一个进去,这里添加进去的是StringValueResolver,点击resolvePlaceHolders方法进去,最终会在AbstractPropertyResolver#resolvePlaceholders中创建一个PropertyPlaceholderHelper

  1. private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
  2. // 前缀为 ${ ,后缀为 },值的分隔符为 : ,比如 ${username:zhansan} username没有的话,后面的为默认的值
  3. return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
  4. this.valueSeparator, ignoreUnresolvablePlaceholders);
  5. }

如果已经注册过一个BFPP的占位符解析器的话,就不需要在注册了,BFPP的占位符解析器就是PropertySourcesPlaceholderConfigurer ,专门用于解析占位符的,比如在xml中或者yaml中,配置类似于${jdbc.username} 这种格式的,就会被解析器解析。PropertySourcesPlaceholderConfigurer 这个解析器实现了BeanFactoryPostProcessor接口,能够对BeanDefinition进行处理,当然也可以对属性值进行处理。

分析完值解析器,继续往下分析。

Bean创建前的其他准备

  1. // 省略代码.....
  2. // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
  3. // 在prepareBeanFactory 准备BeanFactory时设置进去的,如果存在,则开始早期Bean的创建
  4. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  5. for (String weaverAwareName : weaverAwareNames) {
  6. getBean(weaverAwareName);
  7. }
  8. // Stop using the temporary ClassLoader for type matching.
  9. // 停止使用临时的类加载器,这里也是在准备BeanFactory时设置进去的
  10. beanFactory.setTempClassLoader(null);
  11. // Allow for caching all bean definition metadata, not expecting further changes.
  12. // 允许缓存所有 bean 定义元数据,而不是期望进一步的更改
  13. beanFactory.freezeConfiguration();
  14. // 省略代码.....

这里从容器中获取了AOP的织入,如果有的话就开始进行早期的Bean的创建;然后停止了临时的类加载器;然后就是冻结BeanDefinition的元数据信息。

  1. public void freezeConfiguration() {
  2. this.configurationFrozen = true;
  3. this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
  4. }

点击进来,其实就是设置了标识,防止后期对BeanDefinition的修改。

这前面的几个判断和方法实际上都是Bean创建的准备工作,接下来开始分析preInstantiateSingletons 预实例化所有的单例Bean

Spring 源码(9)Spring Bean的创建过程的前期准备的更多相关文章

  1. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  2. Spring 源码分析之 bean 依赖注入原理(注入属性)

         最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...

  3. Spring源码-IOC部分-Bean实例化过程【5】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  4. dubbo源码分析3-service bean的创建与发布

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  5. Spring源码分析专题 —— IOC容器启动过程(上篇)

    声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...

  6. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  7. Spring 源码分析之 bean 实例化原理

    本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...

  8. 【Spring源码分析】Bean加载流程概览(转)

    转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...

  9. Spring源码分析:Bean加载流程概览及配置文件读取

    很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...

随机推荐

  1. vue集成CKEditor构建框架的使用,遇到富文本框不出现工具栏等操作

    官方关于Vue集成CKEditor富文本框的文档:https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/framew ...

  2. 面试问题之计算机网络:TCP如何保证数据可靠传输

    转载于:https://blog.csdn.net/liuchenxia8/article/details/80428157 TCP协议传输的特点主要就是面向字节流.传输可靠.面向连接. TCP保证数 ...

  3. vim recording的使用方法

    使用vim时无意间触碰到q键,左下角出现"recording"这个标识,觉得好奇,遂在网上查了一下,然后这是vim的一个强大功能.他可以录 制一个宏(Macro),在开始记录后,会 ...

  4. RESTful API/Web API

    Microsoft REST API Guidelines Are Not RESTful White House Web API Standards Microsoft REST API Guide ...

  5. 祖先元素transform非none时在Iphone6上引起后代fixed/absolute元素的怪异表现及解决方案

    如题,祖先元素transform非none时,记录一下Iphone6中引起后代元素fixed参考视图怪异表现和解决方案. 层叠关系及参考视图 层叠上下文是HTML元素的三维概念,这些HTML元素在一条 ...

  6. CSS系列——浏览器默认样式

    了解HTML标签在各浏览器当中的默认样式,可以让我们了解,为什么会要写Reset.css,Reset.css当中要怎么写样式最合理.试着思考下面的问题: 为什么会有默认样式? 每个浏览器的默认样式有什 ...

  7. Java/C++实现模板方法模式---数据库操作

    对数据库的操作一般包括连接.打开.使用.关闭等步骤,在数据库操作模板类中我们定义了connDB().openDB().useDB().closeDB()四个方法分别对应这四个步骤.对于不同类型的数据库 ...

  8. cisco packet tracer安装步骤

    一.进入Cisco Networking Academy Builds IT Skills & Education For Future Careers (netacad.com) 二.注册, ...

  9. 【Android开发】【布局】各种TabLayout样式

    Demo

  10. java中的访问控制有什么用?如何用法?请举例

    9.访问控制 [新手可忽略不影响继续学习] 访问控制有什么用?在软件公司里是这么用的,我们想像一种场景,在你的类中,你编了三个私有方法,马克-to-win,别人当然都用不了,但在类外,你也是用不了的, ...