上一集说到,天色渐暗…
BeanFactory接口说:一大堆XxxFactory,都跟我有关,我好累呀【BeanFacotry作为Spring中容器功能的基础,用于存放所有已经加载的bean
】。
XmlBeanFactory争着说到:你还好,一次看不完看不明白,大不了日后有时间慢慢看,用到那个翻那个,慢慢的笔记就理解透了,你这最起码没人和你争风吃醋呀,说罢XmlBeanFactory就瞪向XmlBeanDefinitionReader。
忽然,狂风大作,月色中,有一与BeanFactory样貌体型非常相似的人慢慢向他们三个走来…
上三集的故事:
- DefaultListableBeanFactory这个大管家、以及XmlBeanDefinitionReader和XmlBeanFactory争风吃醋的故事
- 默认标签和自定义标签解析过程
- 加载bean:From缓存Or singletonFactories …
ApplicationContext:竟然有如此与我相似之样貌体型(ApplicationContext在暗指BeanFactory),阁下尊姓大名?
BeanFactory:说我吗
ApplicationContext:对,就是你,那俩瘦的跟柴一样…(暗指XmlBeanFactory与XmlBeanDefinitionReader在Spring中起的作用没他俩大,也就是体型比他俩小一些)
BeanFactory:我叫BeanFactory,是一个接口,那个XmlBeanFactory是我的儿子(默认实现类),那个XmlBeanDefinitionReader我也不知道是哪出来的,说是我儿子XmlBeanFactory想与我有些区别而搞得一个标志他自己特点的东西,我们三个的个人简介你可以点点上面三个链接,我昨天刚大战过,今天懒得张嘴了。over
ApplicationContext:en…,还可以,那就让本大侠介绍一下自己吧。BeanFactory,其实你不知道,咱们都是Spring的手下(Spring中的接口),【ApplicationContext和BeanFacotry两者都是用于加载Bean的】
,我出来就是因为上级觉得你的功能不够,所以Spring提供了另一个接口ApplicationContext,用于扩展BeanFacotry中现有的功能。【不过呢,ApplicationContext包含BeanFactory的所有功能,你有的功能我都有,当比如字节长度对内存有很大的影响时(Applet)这种情况下你上,用你,其余大部分情况下我上,用我ApplicationContext】
。你也累了几天了,你好好坐着,看看咱们俩是啥关系,异同点在哪里。当然,你先看看的家族是啥样子的
- ApplicationContext和BeanFacotry的异同点:
- 相同点:
- ApplicationContext和BeanFacotry两者都是Spring中的接口,都是用于加载Bean的
- 不同点:
- 两个不同的类去加载配置文件在写法上不同
- 使用BeanFactory方式加载XML:BeanFactory bf = new XmlBeanFactory(new ClassPathResource(“demoByHuXxx.xml”));上几篇不就是用这一句层层递进看的源码嘛,这次咱们也一样。
- 使用ApplicationContext方式加载XML:ApplicationContext context = new ClassPathXmlApplicationContext(“demoByHuXxx.xml”);【这一句的意思就是就是
在 ClassPath 中寻找 xml 配置文件,根据 xml 文件内容来构建 ApplicationContext
。当然啦,除了 ClassPathXmlApplicationContext 以外,我们也还有其他构建 ApplicationContext 的方案可供选择】public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { ... // 如果已经有 ApplicationContext 并需要配置成父子关系,那么调用这个构造方法 public ClassPathXmlApplicationContext(ApplicationContext parent) { super(parent); } ... public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } ... public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //设置配置路径,不然咋找到咱们写好的配置文件呀,找到之后根据提供的路径处理成配置文件数组(以分号、逗号、空格、tab、换行符分割) setConfigLocations(configLocations); if (refresh) { //ClassPathXmlApplicationContext中可以将配置文件路径以数组的方式传入,ClassPathXmlApplicationContext可以对数组进行解析并进行加载。而对于解析及功能实现都在refresh()中实现。 refresh();//核心方法 } } ... }
- 两个不同的类去加载配置文件在写法上不同
- 相同点:
- 可以看到,从上面代码中也可以看出其实就是两个点比较关键,
一个是通过setConfigLocations(configLocations)设置配置文件路径
,因为这样你才能找到配置文件呀;第二件事是在refresh()方法中解析配置文件以及实现配置文件中配置好的各个功能
-
通过setConfigLocations(configLocations)设置配置文件路径,
解析给定的路径数组【如果数组中包含特殊符号,如${var},那么在resolvePath中会搜寻匹配的系统变量并替换。】
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext implements BeanNameAware, InitializingBean { ... public void setConfigLocations(@Nullable String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } } ... }
-
在refresh()方法中【为什么是叫 refresh()而不是 init() 这种名字的方法。因为 ApplicationContext 建立起来以后,其实我们是可以通过调用 refresh() 这个方法重建的,refresh() 会将原来的 ApplicationContext 销毁,然后再重新执行一次初始化操作,而不仅仅是init所代表的初始化操作,init肯定代表不了毁旧迎新这个意思呀。】解析配置文件以及实现配置文件中配置好的各个功能,因为咱们
设置了路径之后,便可以根据路径做配置文件的解析以及各种功能的实现了,refresh函数中包含了几乎ApplicationContext中提供的全部功能
,在下面再对代码中的注视进行详细补充:public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { ... @Override public void refresh() throws BeansException, IllegalStateException { //来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛,一波未平一波又起,那岂不是一个完整的好的可以运行的容器都创建不出来了吗 synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing. //准备刷新的上下文环境,做好准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //初始化BeanFactory,并进行XML文件的读取。这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义【BeanDefinition】,注册到 BeanFactory 中【注册的意思也只是说将这些信息都保存到了注册中心】,但是这里说的 Bean 还没有初始化,只是配置信息都提取出来了,说到底这一步就产生了一个 含有beanName-> beanDefinition 之间映射关系的 map //这里将会初始化 BeanFactory、加载 Bean、注册 Bean 等等。但是这步结束后,Bean 并没有完成初始化。这里指的是 Bean 实例并未在这一步生成。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. //对BeanFactory进行各种功能填充,设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //允许子类覆盖这个方法,也就是说这里如果某个子类Bean(可以是咱们自己写的)实现了BeanFactoryPostProcessor接口,那么在容器初始化以后,Spring 会负责调用咱们这个实现了BeanFactoryPostProcessor接口的子类里面的postProcessBeanFactory 方法做一些额外的骚操作处理 //这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化。具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事 //【咱们不是说Spring的大优点就是支持他改造我他集成我嘛,这里不就是提供给子类的的扩展点,不然你让人家怎么下手呢】 postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // Invoke factory processors registered as beans in the context. //激活各种BeanFactory处理器,调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory)回调方法 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //注册拦截Bean创建的Bean处理器【也就是注册 BeanPostProcessor 的实现类】,这里只是注册,真正的调用在getBean时。BeanPostProcessor 接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization分别在Bean 初始化之前和初始化之后得到执行,到这一步时Bean 还没初始化 registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // Initialize message source for this context. //为上下文初始化Message源或者叫不同语言的消息体,也就是进行国际化处理(初始化当前 ApplicationContext 的 MessageSource,国际化) initMessageSource(); // Initialize event multicaster for this context. //初始化应用消息广播器【初始化当前 ApplicationContext 的事件广播器】,并放入applicationEventMulticaster 这个bean中 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //留给子类来初始化其他的bean。 从方法名就可以知道,典型的模板方法(钩子方法),具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前) onRefresh(); // Check for listener beans and register them. //在所有注册的bean中查找Listener bean,注册到消息广播中【注册事件监听器,监听器需要实现 ApplicationListener 接口】 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //初始化非惰性的singleton beans finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //完成刷新过程【说明ApplicationContext 初始化完成】,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources.销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源 destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller.把异常往外抛 throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); contextRefresh.end(); } } } ... }
- 1)通过prepareRefresh()方法进行初始化前的准备工作,在Spring启动的时候提前对必须的变量进行存在性验证。例如
对系统属性或者环境变量进行准备及验证
【在某种情况下项目的使用需要读取某些系统变量】- prepareRefresh()方法中最关键的是最后两句代码:
- initPropertySources方法留给子类覆盖;initPropertySources正符合Spring的开放式结构设计,给用户最大扩展Spring的能力。
用户可以根据自身的需要重写initPropertySources方法,并在方法中进行个性化的属性处理及设置
。 - initPropertySources()用来验证需要的属性文件是否都已经放入环境中,或者说validateRequiredProperties是来对属性进行验证的
- initPropertySources方法留给子类覆盖;initPropertySources正符合Spring的开放式结构设计,给用户最大扩展Spring的能力。
- prepareRefresh()方法中最关键的是最后两句代码:
- 2)通过ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这一句代码初始化BeanFactory,并进行XML文件读取。【
这个obtainFreshBeanFactory方法将会初始化 BeanFactory、加载 Bean、注册 Bean 等等。但是这步结束后,Bean 并没有完成初始化,也就是 Bean 实例并未在这一步生成
。】public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { ... protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //关闭旧的 BeanFactory (如果有),创建新的 BeanFactory,加载 Bean 定义、注册 Bean 等等 refreshBeanFactory(); // 返回刚刚创建的 BeanFactory return getBeanFactory(); } ... }
- ClassPathXmlApplicationContext包含着BeanFactory所提供的一切特征,那么在这一步骤中将会复用BeanFactory中的配置文件读取解析及其他功能,
这一步之后, ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能或者说经过obtainFreshBeanFactory()方法的处理后ApplicationContext就已经拥有了BeanFactory的全部功能,也就是可以进行Bean的提取等基础操作了
。- 我们所说的 Spring 的 Bean指的就是BeanDefinition【
Bean 在代码层面上可以简单认为是 BeanDefinition 的实例。BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。
。】,我们自己定义的各个 Bean 其实会转换成一个个 BeanDefinition 存在于 Spring 的 BeanFactory 中
。public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { // 可以看到默认只提供 sington 和 prototype 两种, // 还有 request, session, globalSession, application, websocket 这几种基于 web 的扩展 String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; // 比较不重要 int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; // 设置父 Bean,这里涉及到 bean 继承,不是 java 继承。一句话就是:继承父 Bean 的配置信息而已 void setParentName(String parentName); // 获取父 Bean String getParentName(); // 设置 Bean 的类名称,将来是要通过反射来生成实例的 void setBeanClassName(String beanClassName); // 获取 Bean 的类名称 String getBeanClassName(); // 设置 bean 的 scope void setScope(String scope); String getScope(); // 设置是否懒加载 void setLazyInit(boolean lazyInit); boolean isLazyInit(); // 设置该 Bean 依赖的所有的 Bean,注意,这里的依赖不是指属性依赖(如 @Autowire 标记的),是 depends-on="" 属性设置的值。 void setDependsOn(String... dependsOn); // 返回该 Bean 的所有依赖 String[] getDependsOn(); // 设置该 Bean 是否可以注入到其他 Bean 中,只对根据类型注入有效,如果根据名称注入,即使这边设置了 false,也是可以的 void setAutowireCandidate(boolean autowireCandidate); // 该 Bean 是否可以注入到其他 Bean 中 boolean isAutowireCandidate(); // 主要的。同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 bean void setPrimary(boolean primary); // 是否是 primary 的 boolean isPrimary(); // 如果该 Bean 采用工厂方法生成,指定工厂名称。一句话就是:有些实例不是用反射生成的,而是用工厂模式生成的 void setFactoryBeanName(String factoryBeanName); // 获取工厂名称 String getFactoryBeanName(); // 指定工厂类中的 工厂方法名称 void setFactoryMethodName(String factoryMethodName); // 获取工厂类中的 工厂方法名称 String getFactoryMethodName(); // 构造器参数 ConstructorArgumentValues getConstructorArgumentValues(); // Bean 中的属性值,后面给 bean 注入属性值的时候会用到 MutablePropertyValues getPropertyValues(); // 是否 singleton boolean isSingleton(); // 是否 prototype boolean isPrototype(); // 如果这个 Bean 是被设置为 abstract,那么不能实例化,常用于作为 父bean 用于继承,其实也很少用...... boolean isAbstract(); int getRole(); String getDescription(); String getResourceDescription(); BeanDefinition getOriginatingBeanDefinition(); }
- 上面提到一个Bean 继承:在初始化 Bean 的地方我们接触到了RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);这里涉及到的就是 中的 parent 属性,这里的继承和 java 语法中的继承没有任何关系,不过思路是相通的。child bean 会继承 parent bean 的所有配置,也可以覆盖一些配置,当然也可以新增额外的配置。Spring 中提供了继承自 AbstractBeanDefinition 的 ChildBeanDefinition 来表示 child bean。
- 如果加上了parent bean 中的 abstract = true以后, Spring 在实例化 singleton beans 的时候会忽略这个 bean。相当于说这个 bean 的作用就是用来充当模板用的 parent bean,那么当模板了的话,此处就必须加上 abstract = true。
- 我们所说的 Spring 的 Bean指的就是BeanDefinition【
- 从obtainFreshBeanFactory源码中可以看到,要想知道obtainFreshBeanFactory的底层工作原理还得点进去到refreshBeanFactory()方法中【此时,看了obtainFreshBeanFactory()方法中的refreshBeanFactory方法后,咱们就大概也可以知道
ApplicationContext 继承自 BeanFactory,但是它不应该被理解为 BeanFactory 的实现类,而是说其内部持有一个实例化的 BeanFactory(DefaultListableBeanFactory)。以后所有的 BeanFactory 相关的操作其实是委托给这个实例来处理的。而不仅仅是简简单单的继承关系、实现代码复用这些...,这也太low了
。】public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { ... @Override protected final void refreshBeanFactory() throws BeansException { //如果 ApplicationContext 中已经加载过 BeanFactory 了,销毁所有 Bean,关闭 BeanFactory。应用中 BeanFactory 本来就是可以有多个的,这里可不是说应用的全局是否有 BeanFactory,而是当前 ApplicationContext 是否有 BeanFactory if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建DefaultListableBeanFactory,这个大管家来了。咱们之前都是BeanFactory bf = new XmlBeanFactory("demoByHuXxx.xml")声明配置文件,前面也看过,这里XmlBeanFactory继承自DefaultListableBeanFactory,并提供了XmlBeanDefinitionReader类型的reader属性,也就是说DefaultListableBeanFactory是容器的基础,那想用容器不得先实例化嘛:所以得先实例化DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); //为了序列化指定id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象 beanFactory.setSerializationId(getId()); //定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖以及设置@Autowired和@Qualifier 注解解析器QualifierAnnotationAutowireCandidateResolver //设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用 customizeBeanFactory(beanFactory); //初始化DocumentReader, 并进行XML文件读取及解析,加载BeanDefinition //加载 Bean 到 BeanFactory 中 loadBeanDefinitions(beanFactory); //使用全局变量记录BeanFactory类实例,因为DefaultListableBeanFactory类型的变量beanFactory是函数内的局部变量,所以要使用全局变量记录解析结果。 this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } ... }
- 上面说了ApplicationContext 继承自 BeanFactory,应该理解为ApplicationContext 内部持有一个实例化的 BeanFactory(
DefaultListableBeanFactory,也就是说DefaultListableBeanFactory是BeanFactory的实现类
)。以后所有的 BeanFactory 相关的操作其实是委托给这个实例来处理的,为什么BeanFactory要选择DefaultListableBeanFactory做自己的代表呢,就是因为DefaultListableBeanFactory能左右逢源,手很长,人脉广,他可以帮BeanFactory做很多很难的别的子类很难做到的事,所以这个货挺重要的,瞅瞅他。
- customizeBeanFactory:
customizeBeanFactory(beanFactory) 就是配置是否允许 BeanDefinition 覆盖、是否允许循环引用
。- 另外,上面在refreshBeanFactory方法中的customizeBeanFactory里面就已经开始了对BeanFactory的扩展,
在基本容器的基础上,增加了是否允许覆盖是否允许扩展的设置并提供了注解@Qualifier和@Autowired的支持【对于允许覆盖和允许依赖的设置这个customizeBeanFactory方法中只是判断了是否为空,如果不为空要进行设置,而想要设置允许覆盖和允许依赖就得使用子类覆盖这个ClassPathXmlApplicationContext然后在】customizeBeanFactory方法中进行设置:super.setXxx...(false);
。- BeanDefinition 的覆盖问题指的就是在配置文件中定义 bean 时使用了相同的 id 或 name,默认情况下,allowBeanDefinitionOverriding 属性为 null,如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖【Spring 默认是不同文件的时候可以覆盖的。】。
- 循环引用指的就是A依赖 B,而 B 依赖 A。或 A 依赖 B,B 依赖 C,而 C 依赖 A。默认情况下,Spring 允许循环依赖,当然如果你在 A 的构造方法中依赖 B,在 B 的构造方法中依赖 A 是不行的。
- 对于定制BeanFactory,Spring提供了可以设置AutowireCandidateResolver,如果在bean加载部分中创建bean时采用autowireByType方式注入,那么默认会使用Spring提供的SimpleAutowireCandidateResolver。好巧不巧,Spring在这个customizeBeanFactory方法中使用了QualifierAnnotationAutowireCandidateResolver,
设置了这个解析器后Spring就可以支持注解方式的注入了:【beanFactory.setAutowireCandidateResolver(new QualifierAnnotaionAutowireCandidateResolver()),这一句就是来用于@Qualifier和@Autowired】
。那肯定的,在QualifierAnnotationAutowireCandidateResolver中一定会提供了解析Qualifier与Autowire注解的方法【解析autowire类型时会首先调用方法Object value = getAutowiredCandidateResolver().getSuggestedValue(descriptor);】,不然这一句代码怎么用于@Qualifier和@Autowired - 在实现配置文件的加载功能中除了我们在前一步中已经初始化的DefaultListableBeanFactory外【refreshBeanFactory方法中开始的不就是DefaultListableBeanFactory beanFactory = createBeanFactory()嘛】,还需要XmlBeanDefinitionReader来读取XML,所以在customizeBeanFactory方法执行之后
执行的loadBeanDefinitions(beanFactory)方法就是初始化XmlBeanDefinitionReader【为指定的beanFactory创建XmlBeanDefinitionReader】
,然后在这个loadBeanDefinitions(beanFactory)方法中最后一句是loadBeanDefinitions(beanDefinitionReader),相当于在初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以通过loadBeanDefinitions(beanDefinitionReader)方法进行配置文件的读取了【在XmlBeanDefinitionReader中已经将之前初始化的DefaultListableBeanFactory注册进去了,所以XmlBeanDefinitionReader所读取的BeanDefinitionHolder都会注册到DefaultListableBeanFactory中,也就是经过此步骤,类型DefaultListableBeanFactory的变量beanFactory已经包含了所有解析好的配置】
- 另外,上面在refreshBeanFactory方法中的customizeBeanFactory里面就已经开始了对BeanFactory的扩展,
- loadBeanDefinitions(DefaultListableBeanFactory beanFactory):
- loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法是根据配置加载各个 Bean,然后放到 BeanFactory 中,而加载进去就是为了解析读取,
读取配置的操作在 XmlBeanDefinitionReader 中,其负责加载配置、解析【所以在loadBeanDefinitions方法中会给这个 BeanFactory 实例化一个 XmlBeanDefinitionReader: XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);】,然后通过一个 XmlBeanDefinitionReader 实例来加载各个 Bean
。 - 然后loadBeanDefinitions(DefaultListableBeanFactory beanFactory)中还有一句比较重要的代码,就是调用了loadBeanDefinitions(XmlBeanDefinitionReader reader)这个重载方法,这个重载方法其实就是来用刚刚初始化的 Reader 开始来加载 、解析xml 配置【比如里面代码中写到:返回 counter,表示总共加载了多少的 BeanDefinition…等,反正经过这个方法处理后,其中一个配置文件终于转换为一颗 DOM 树了:将 xml 文件转换为 Document 对象( Document doc = doLoadDocument(inputSource, resource);)。其中一个配置文件转换成为DOM树后,就开始从根节点开始解析( doRegisterBeanDefinitions(root);代表从 xml 根节点开始解析文件)】
- doRegisterBeanDefinitions(Element root):
- 这个方法中首先通过这一句代码
BeanDefinitionParserDelegate parent = this.delegate
;定义一个 parent,定义一个parent是因为 内部是可以定义 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 节点,从源码分析的角度,我们当做根节点就好了 - 另外,这个方法中有这么几句:
// DefaultBeanDefinitionDocumentReader 116 protected void doRegisterBeanDefinitions(Element root) { ... preProcessXml(root); // 给子类用的钩子方法 parseBeanDefinitions(root, this.delegate); postProcessXml(root); // 给子类用的钩子方法 ... }
- 而这个给子类用的两个钩子方法中间加的这一句,parseBeanDefinitions(root, this.delegate);就跟咱们对标签解析那一篇文章接上了,就是这么神奇。点点看,瞅一瞅
- 这个方法中首先通过这一句代码
- loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法是根据配置加载各个 Bean,然后放到 BeanFactory 中,而加载进去就是为了解析读取,
- 上面说了ApplicationContext 继承自 BeanFactory,应该理解为ApplicationContext 内部持有一个实例化的 BeanFactory(
- ClassPathXmlApplicationContext包含着BeanFactory所提供的一切特征,那么在这一步骤中将会复用BeanFactory中的配置文件读取解析及其他功能,
- 3)通过prepareBeanFactory(beanFactory)方法对上面得到的BeanFactory进行各种功能的填充【进入函数prepareBeanFactory前,Spring已经完成了对配置的解析【Spring 把我们在 xml 配置的 bean 都注册以后,会"手动"注册一些特殊的 bean】】【在bean的初始化后会调用ResourceEditorRegistrar的registerCustomEditors方法进行批量的通用属性编辑器注册。注册后,在属性填充的环节便可以直接让Spring使用这些编辑器进行属性的解析了。】,比如咱们熟悉的@Autowired和@Qualifier这两个注解正是在这一步骤中增加的对他俩自己的支持,不然人家都不认识你俩,你俩还能起啥作用?
prepareBeanFactory(beanFactory)方法中主要也就干了这几件事
:- 首先就是
beanFactory.setBeanClassLoader(getClassLoader())
;设置 BeanFactory 的类加载器,我们知道 BeanFactory 需要加载类,也就需要类加载器,这里设置为加载当前 ApplicationContext 类的类加载器 - 增加对SPEL(Spring Expression Language,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等,并且能与Spring功能完美整合,比如能用来配置bean定义。SpEL是单独模块,只依赖于core模块,不依赖于其他模块,可以单独使用。)语言的支持。在源码中通过代码beanFactory.setBeanExpressionResolver(new StandardBeanExpression Resolver())注册语言解析器,就可以对SPEL进行解析了【
Spring在bean进行初始化的时候会有属性填充的一步,而在这一步中Spring会调用AbstractAutowireCapableBeanFactory类的applyPropertyValues函数来完成功能。就在这个函数中,会通过构造BeanDefinitionValueResolver类型实例valueResolver来进行属性值的解析。同时,也是在这个步骤中一般通过AbstractBeanFactory中的evaluateBeanDefinitionString方法去完成SPEL的解析
。当调用这个方法时会判断是否存在语言解析器,如果存在则调用语言解析器的方法进行解析,用语言解析器的调用主要是在解析依赖注入bean的时候,以及在完成bean的初始化和属性获取后进行属性填充的时候。】
- 上面提到的AbstractAutowireCapableBeanFactory主要是为了以下场景【混用了 xml 和 注解 两种方式的配置方式,咱们项目中不就经常这样干嘛】,采用 @Autowired 注解注入属性值:
- 在AbstractAutowireCapableBeanFactory 中主要实现了:
- 确保 BeanDefinition 中的 Class 被加载
- 让 InstantiationAwareBeanPostProcessor 在这一步有机会返回代理,
调用doCreateBean创建 bean
... Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; ...
- AbstractAutowireCapableBeanFactory中调用doCreateBean创建 bean,那么咱们是不是得看看这个doCreateBean,在这个doCreateBean方法里面,主要就是进行了:
- 先判断是不是 FactoryBean【FactoryBean 适用于 Bean 的创建过程比较复杂的场景,比如数据库连接池的创建。】,不是则实例化 Bean【用createBeanInstance 方法来创建Bean 实例,
其实createBeanInstance方法的目的就是实例化我们指定的类
。】:instanceWrapper = createBeanInstance(beanName, mbd, args);protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { // 确保已经加载了此class Class<?> beanClass = resolveBeanClass(mbd, beanName); // 校验一下这个类的访问权限 if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } if (mbd.getFactoryMethodName() != null) { // 采用工厂方法实例化,不熟悉这个概念的读者请看附录,注意,不是 FactoryBean return instantiateUsingFactoryMethod(beanName, mbd, args); } // 如果不是第一次创建,比如第二次创建 prototype bean。 // 这种情况下,我们可以从第一次创建知道,采用无参构造函数,还是构造函数依赖注入 来完成实例化 boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { // 构造函数依赖注入 return autowireConstructor(beanName, mbd, null, null); } else { // 无参构造函数 return instantiateBean(beanName, mbd); } } // 判断是否采用有参构造函数 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { // 构造函数依赖注入 return autowireConstructor(beanName, mbd, ctors, args); } // 调用无参构造函数,在这个构造函数中会进行实际的实例化过程, 如果不存在方法覆写,那就使用 java 反射进行实例化,否则使用 CGLIB,存在方法覆写,利用 CGLIB 来完成实例化,需要依赖于 CGLIB 生成子类 return instantiateBean(beanName, mbd); }
- FactoryBean 适用于 Bean 的创建过程比较复杂的场景,比如数据库连接池的创建。
- FactoryBean 适用于 Bean 的创建过程比较复杂的场景,比如数据库连接池的创建。
- 这个就是 咱们Bean 里面的 我们定义的类 的实例,很多地方描述成 “bean 实例”:final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
- 类型: Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
- 解决循环依赖的问题:
负责属性装配,因为前面的实例只是实例化了,并没有设值,这里就是设值【用了两个关键方法进行bean的属性装配,相当于实例化后的设置值:实现依赖注入的 populateBean 方法以及回调方法 initializeBean。】
- 先判断是不是 FactoryBean【FactoryBean 适用于 Bean 的创建过程比较复杂的场景,比如数据库连接池的创建。】,不是则实例化 Bean【用createBeanInstance 方法来创建Bean 实例,
- 用了两个关键方法进行bean的属性装配,相当于实例化后的设置值:
- 实现依赖注入的 populateBean 方法:populateBean(…) 方法负责进行属性设值,处理依赖。源码里面主要是这几点:
- 通过名字找到所有属性值,如果是 bean 依赖,先初始化依赖的 bean。记录依赖关系
- 这里有个非常有用的 BeanPostProcessor 进到这里: AutowiredAnnotationBeanPostProcessor。对采用 @Autowired、@Value 注解的依赖进行设值
- 回调方法 initializeBean:属性注入完成后,这一步其实就是处理各种回调了,比如
- 如果 bean 实现了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 接口,回调
- BeanPostProcessor 的 postProcessBeforeInitialization 回调
- 处理 bean 中定义的 init-method,或者如果 bean 实现了 InitializingBean 接口,调用 afterPropertiesSet() 方法。BeanPostProcessor 的两个回调都发生在这边,只不过中间处理了 init-method
- 实现依赖注入的 populateBean 方法:populateBean(…) 方法负责进行属性设值,处理依赖。源码里面主要是这几点:
- 在AbstractAutowireCapableBeanFactory 中主要实现了:
- 上面提到的AbstractAutowireCapableBeanFactory主要是为了以下场景【混用了 xml 和 注解 两种方式的配置方式,咱们项目中不就经常这样干嘛】,采用 @Autowired 注解注入属性值:
- 增加对一些内置类,比如EnvironmentAware、MessageSourceAware的信息注入、设置了依赖功能可忽略的接口、注册一些固定依赖的属性、
增加AspectJ的支持
【也就是,在这里,这个beanFactory是(ConfigurableListableBeanFactory beanFactory的,**beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));】**这一句表示这里添加一个 BeanPostProcessor类型的processor,实现了 Aware 接口的 beans 在初始化的时候是由这个 processor 负责回调,因为有时候我们会为了获取 ApplicationContext 而 implement ApplicationContextAware这些,所以这个BeanPostProcessor类型的processor负责回调ApplicationContextAware、EnvironmentAware、ResourceLoaderAware
。 - 将相关环境变量及属性注册以单例模式注册。这里有个挺好玩的技巧就是,咱们在注入属性时可以方便的把普通属性注入进来,但是像Date类型就无法被Spring识别,比如咱们实体中private Date dataValue;如果直接在配置文件中这样写是会报异常的,因为类型转换不成功,因为
在实体中的dataValue属性是Date类型的,而在XML中配置的却是String类型的
,所以当然会报异常。Spring针对这个类型转换问题提供了两种解决办法:
- 使用自定义属性编辑器:使用自定义属性编辑器,通过继承PropertyEditorSupport,重写setAsText方法,然后将自定义属性编辑器注册到Spring中。在配置文件中引入类型为org.Springframework.beans.factory.config.CustomEditorConfigurer的bean,并在属性customEditors中加入自定义的属性编辑器,其中key为属性编辑器所对应的类型。通过这样的配置,当Spring在注入bean的属性时一旦遇到了java.util.Date类型的属性会自动调用自定义的DatePropertyEditor解析器进行解析,并用解析结果代替配置属性进行注入
- 注册Spring自带的属性编辑器CustomDateEditor:还是得先定义属性编辑器,然后注册到Spring中【在配置文件中将自定义的DatePropertyEditorRegistrar注册进入org.Springframework. beans.factory.config.CustomEditorConfigurer的propertyEditorRegistrars属性中】
- AbstractApplicationContext的prepareBeanFactory方法除了上面几个之外,还有一个
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))
;这句代码的目的就是注册BeanPostProcessor,而咱们到这个方法的参数ApplicationContextAwareProcessor中一看可知,ApplicationContextAwareProcessor实现了BeanPostProcessor接口【之前在bean实例化的时候,也就是Spring激活bean的init-method的前后,会调用BeanPost Processor的postProcessBeforeInitialization方法和postProcessAfterInitialization方法
】,并且在ApplicationContextAwareProcessor的postProcessBeforeInitialization方法中调用了invokeAwareInterfaces,invokeAwareInterfaces方法中的逻辑其实就是实现这些Aware接口的bean在被初始化之后,可以取得一些对应的资源
class ApplicationContextAwareProcessor implements BeanPostProcessor { ... @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || bean instanceof ApplicationStartupAware)) { return bean; } invokeAwareInterfaces(bean); return bean; } //当Spring将ApplicationContextAwareProcessor注册后,那么在invokeAwareInterfaces方法中间接调用的Aware类已经不是普通的bean了,如ResourceLoaderAware、ApplicationEventPublisher Aware等。当然啦,这些在Spring做bean的依赖注入的时候是被忽略的,忽略就是通过prepareBeanFactory方法中的几个ignoreDependencyInterface方法实现的。 private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationStartupAware) { ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup()); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } ... }
- BeanFactoryPostProcessor接口跟BeanPostProcessor类似,可以对bean的定义(配置元数据)进行处理。Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其他的bean之前读取配置元数据,并有可能修改它,咱们也可以配置多个BeanFactoryPostProcessor。另外咱们你也可以
使用BeanPostProcessor改变实际的bean实例(例如从配置元数据创建的对象)
- BeanPostProcessor 概念在 Spring 中也是比较重要的,BeanPostProcessor接口
- 如果我们自己定义一个 bean 实现 BeanPostProcessor 的话,那么咱们自己写的实现的执行时机是在 bean 实例化完成、属性注入完成之后,会执行回调方法,
首先会回调几个实现了 Aware 接口的 bean,然后就开始回调 BeanPostProcessor 的 postProcessBeforeInitialization 方法,之后是回调 init-method,然后再回调 BeanPostProcessor 的 postProcessAfterInitialization 方法
。
- 如果我们自己定义一个 bean 实现 BeanPostProcessor 的话,那么咱们自己写的实现的执行时机是在 bean 实例化完成、属性注入完成之后,会执行回调方法,
- BeanFactoryPostProcessor的作用域范围是容器级的。它只和你所使用的容器有关。如果你在容器中定义一个BeanFactoryPostProcessor,它仅仅对此容器中的bean进行后置处理。BeanFactoryPostProcessor不会对定义在另一个容器中的bean进行后置处理,即使这两个容器都是在同一层次上
对于BeanFactoryPostProcessor的处理主要分两种情况进行【因为对于BeanFactoryPostProcessor的处理,不但要实现注册功能,而且还要实现对后处理器的激活操作,所以需要载入配置中的定义,并进行激活】,一个是对于BeanDefinitionRegistry类的特殊处理,另一种是对普通的BeanFactoryPostProcessor进行处理【一种方式是通过硬编码方式的处理,另一种是通过配置文件方式的处理】。而对于每种情况都需要考虑硬编码注入注册的后处理器以及通过配置注入的后处理器
。具体来说,对于BeanDefinitionRegistry类型的处理类的处理主要包括以下几个:- 对于硬编码注册的后处理器的处理,主要是通过AbstractApplicationContext中的添加处理器方法addBeanFactoryPostProcessor进行添加。添加后的后处理器会存放在beanFactoryPostProcessors中,而在处理BeanFactoryPostProcessor时候会首先检测beanFactoryPostProcessors是否有数据。BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,不但有BeanFactoryPostProcessor的特性,同时还有自己定义的个性化方法,也需要在此调用。所以,这里需要
从beanFactoryPostProcessors中挑出BeanDefinitionRegistryPostProcessor的后处理器,并进行其postProcessBeanDefinitionRegistry方法的激活
。记录后处理器主要使用了三个List完成:
对以上所记录的List中的后处理器进行统一调用BeanFactoryPostProcessor的postProcessBeanFactory方法;对beanFactoryPostProcessors中非BeanDefinitionRegistryPostProcessor类型的后处理器进行统一的BeanFactoryPostProcessor的postProcessBeanFactory方法调用【Spring中大部分功能都是通过后处理器的方式进行扩展的,这是Spring框架的一个特性,但是在调用的时候如果没有进行手动注册其实是不能使用的(在BeanFactory中其实并没有实现后处理器的自动注册,而ApplicationContext中却添加了自动注册功能)】
。
- 对于硬编码注册的后处理器的处理,主要是通过AbstractApplicationContext中的添加处理器方法addBeanFactoryPostProcessor进行添加。添加后的后处理器会存放在beanFactoryPostProcessors中,而在处理BeanFactoryPostProcessor时候会首先检测beanFactoryPostProcessors是否有数据。BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,不但有BeanFactoryPostProcessor的特性,同时还有自己定义的个性化方法,也需要在此调用。所以,这里需要
- 在BeanPostProcessor的处理中只考虑配置文件的方式,因为对于BeanPostProcessor并不需要马上调用,硬编码的方式实现的功能是将后处理器提取并调用,这里并不需要调用,所以就不需要考虑硬解码方式,
只需要将配置文件的BeanPostProcessor提取出来并注册进入beanFactory就可以了
。对于beanFactory的注册,也不是直接注册就可以的。在Spring中支持对于BeanPostProcessor的排序
,比如根据PriorityOrdered进行排序、根据Ordered进行排序或者无序,而Spring在BeanPostProcessor的激活顺序的时候也会考虑对于顺序的问题而先进行排序
。
- BeanPostProcessor 概念在 Spring 中也是比较重要的,BeanPostProcessor接口
- BeanFactoryPostProcessor接口跟BeanPostProcessor类似,可以对bean的定义(配置元数据)进行处理。Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其他的bean之前读取配置元数据,并有可能修改它,咱们也可以配置多个BeanFactoryPostProcessor。另外咱们你也可以
- Spring除了在prepareBeanFactory利用ignoreDependencyInterface等方法实现当bean依赖注入时,忽略那些用不上的XxxAware类之外。但是,我要用上了的话,当然也必不可少地会有注册依赖的功能。当注册了依赖解析后,例如当注册了对BeanFactory.class的解析依赖后,bean的属性注入的时候,一旦检测到属性为BeanFactory类型便会将beanFactory的实例注入进去。
- 首先就是
- 4)
通过postProcessBeanFactory(beanFactory)可以对Spring进行扩展
:Spring之所以强大除了它功能上为大家提供了便例外,还有一方面是它的完美架构,开放式的架构让使用它的程序员很容易根据业务需要扩展已经存在的功能
。这种开放式的设计在Spring中随处可见,例如提供的这一个空的函数实现postProcessBeanFactory来方便程序员在业务上做进一步扩展。 - 5)通过invokeBeanFactoryPostProcessors(beanFactory)方法激活各种BeanFactory处理器
- 6)通过registerBeanPostProcessors(beanFactory)方法注册拦截bean创建的bean处理器,这里只是注册,真正的调用是在getBean时候。
- 【
Spring中大部分功能都是通过后处理器的方式进行扩展的,这是Spring框架的一个特性,但是在调用的时候如果没有进行手动注册其实是不能使用的(在BeanFactory中其实并没有实现后处理器的自动注册,而ApplicationContext中却添加了自动注册功能)
】
- 对于BeanPostProcessor的处理与BeanFactoryPostProcessor的处理极为相似
在registerBeanPostProcessors方法的实现中其实已经确保了beanPostProcessor的唯一性
- 【
- 7)通过initMessageSource()为上下文初始化Message源或者叫不同语言的消息体,也就是进行国际化处理
- 假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:
英文的操作系统返回英文界面,而中文的操作系统则返回中文界面
——这便是典型的i18n国际化问题
,此时我们就要为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。“国际化信息”也称为“本地化信息”,一般需要两个条件才可以确定一个特定类型的本地化信息,它们分别是“语言类型”和“国家/地区的类型”。Java通过java.util.Locale类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象。java.util.Locale是表示语言和国家/地区信息的本地化类,它是创建国际化应用的基础。Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类 在initMessageSource中的方法主要功能是提取配置中定义的messageSource,并将其记录在Spring的容器中【通过读取并将自定义资源文件配置记录在容器中,那么就可以在获取资源文件的时候直接使用了】,也就是AbstractApplicationContext中。如果用户未设置资源文件的话,Spring中也提供了默认的配置DelegatingMessageSource
。
- 假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:
- 8)通过initApplicationEventMulticaster()初始化应用消息广播器【
广播器是用于存放监听器并在合适的时候调用监听器
】,并放入“applicationEventMulticaster”bean中。- Spring的事件监听使用例子这里贴上郝老师团队的Spring源码深度解析中的一个例子,供日后学习使用:
- initApplicationEventMulticaster的方法中区分了两种情况:【当产生Spring事件的时候会默认使用SimpleApplicationEventMulticaster的multicastEvent来广播事件,遍历所有监听器,并使用监听器中的onApplicationEvent方法来进行监听器的处理。而对于每个监听器来说其实都可以获取到产生的事件,但是是否进行处理则由事件监听器来决定。】
- 如果用户自定义了事件广播器,那么使用用户自定义的事件广播器
- 如果用户没有自定义事件广播器,那么使用默认的ApplicationEventMulticaster
- Spring注册监听器时其实就是通过硬编码方式注册的监听器处理或者配置文件注册的监听器处理
- Spring的事件监听使用例子这里贴上郝老师团队的Spring源码深度解析中的一个例子,供日后学习使用:
- 9)通过onRefresh()留给子类来初始化其他的bean
- 10)通过registerListeners()方法在所有注册的bean中查找listener bean,注册到消息广播器中。
- 11)通过finishBeanFactoryInitialization(beanFactory)初始化剩下的单实例,也就是
所有的非惰性的singleton beans,到此为止,Spring 会在这个阶段完成所有的 singleton beans 的实例化。
。执行到finishBeanFactoryInitialization(beanFactory)这一步之后,应该说 BeanFactory 已经创建完成,并且所有的实现了 BeanFactoryPostProcessor 接口的 Bean 都已经初始化并且其中的 postProcessBeanFactory(factory) 方法已经得到回调执行了。而且 Spring 已经“手动”注册了一些特殊的 Bean,如 environment、systemProperties 等,所以不就刚好剩下的就是初始化 singleton beans 了嘛。
。- 初始化的动作是包装在 beanFactory.getBean(…) 中的:
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class))
;
- 初始化的动作是包装在 beanFactory.getBean(…) 中的:
- 在finishBeanFactoryInitialization(beanFactory)方法中完成BeanFactory的初始化工作,其中包括ConversionService的设置、配置冻结以及非延迟加载的bean的初始化工作。
- ConversionService的设置其实就是当咱们碰到使用自定义类型转换器从String转换为Date的方式时,Spring说你这个方式不行,看我的,Spring中还提供了另一种转换方式:使用Converter转换器,咱们先定义一个转换器:public class DemoByHuDateConverter implements Converter<String, date>{…},然后在在配置文件中注册咱们自己写的转换器,最后就可以直接调用咱们的转换器了。
- ConversionService最有用的场景就是
用来将前端传过来的参数和后端的 controller 方法上的参数进行绑定的时候
。像前端传过来的字符串、整数要转换为后端的 String、Integer 很容易,但是如果 controller 方法需要的是一个枚举值,或者是 Date 这些非基础类型(含基础类型包装类)值的时候,我们就可以考虑采用 ConversionService 来进行转换。
- 实现这种转换很简单的方式就是实现 Converter 接口。
只要注册这个 Bean 就可以了。这样,前端往后端传的时间描述字符串就很容易绑定成 Date 类型了,不需要其他任何操作
。public class StringToDateConverter implements Converter<String, Date> { @Override public Date convert(String source) { try { return DateUtils.parseDate(source, "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "HH:mm:ss", "HH:mm"); } catch (ParseException e) { return null; } } }
- ConversionService最有用的场景就是
- 配置冻结:冻结所有的bean定义,说明注册的bean定义将不被修改或进行任何进一步的处理。
- 初始化非延迟加载:
ApplicationContext实现的默认行为就是在启动时将所有单例bean提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的单例bean
。通常情况下这是一件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。而这个实例化的过程就是在finishBeanFactoryInitialization中完成的。- 也就是finishBeanFactoryInitialization里面的这一句
beanFactory.preInstantiateSingletons()
;表示开始初始化了。而很重要的一点是finishBeanFactoryInitialization里面调用的beanFactory.preInstantiateSingletons()方法是在DefaultListableBeanFactory类中
【这个货是个手很长人脉人广的BeanFactory哦】@Override public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } // this.beanDefinitionNames 保存了所有的 beanNames List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames); // 下面这个循环,触发所有的非懒加载的 singleton beans 的初始化操作 for (String beanName : beanNames) { // 合并父 Bean 中的配置,注意 <bean id="" class="" parent="" /> 中的 parent RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 非抽象、非懒加载的 singletons。如果配置了 'abstract = true',那是不需要初始化的 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 处理 FactoryBean if (isFactoryBean(beanName)) { // FactoryBean 的话,在 beanName 前面加上 ‘&’ 符号。再调用 getBean,getBean 方法别急 final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); // 判断当前 FactoryBean 是否是 SmartFactoryBean 的实现, boolean isEagerInit; ... if (isEagerInit) { getBean(beanName); } } else { // 对于普通的 Bean,只要调用 getBean(beanName) 这个方法就可以进行初始化了 getBean(beanName); } } } // 到这里说明所有的非懒加载的 singleton beans 已经完成了初始化 // 如果我们定义的 bean 是实现了 SmartInitializingSingleton 接口的,那么在这里得到回调, for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { smartSingleton.afterSingletonsInstantiated(); return null; } }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
- preInstantiateSingletons比较重要的点就是进入到 AbstractBeanFactory 中的getBean(beanName) 方法了,这个AbstractBeanFactory 中的getBean(beanName) 方法我们经常用来从 BeanFactory 中获取一个 Bean,而初始化的过程也封装到了这个getBean(beanName) 方法里。但是getBean 方法我们经常是用来从容器中获取 Bean 用的,已经初始化过了就从容器中直接返回,否则就先初始化再返回
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { ... @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ... @SuppressWarnings("unchecked") protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // 获取一个 “正统的” beanName,处理两种情况,一个是前面说的 FactoryBean(前面带 ‘&’), 一个是别名问题,因为这个方法是 getBean,获取 Bean 用的,所以要是传一个别名进来是完全可以的 String beanName = transformedBeanName(name); //这个是返回值 Object beanInstance; // Eagerly check singleton cache for manually registered singletons.检查下是不是已经创建过了 Object sharedInstance = getSingleton(beanName); // args 传参其实是 null 的,但是如果 args 不为空的时候,那么意味着调用方不是希望获取 Bean,而是创建 Bean if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } //如果是普通 Bean 的话,直接返回 sharedInstance,如果是 FactoryBean 的话,返回它创建的那个实例对象 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { //创建过了此 beanName 的 prototype 类型的 bean,那么抛异常,但一般抛异常往往是因为陷入了循环引用 throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory.检查一下这个 BeanDefinition 在容器中是否存在 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent.如果当前容器不存在这个 BeanDefinition,试试父容器中有没有 String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory abf) { return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args.返回父容器的查询结果 return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } if (!typeCheckOnly) { //typeCheckOnly 为 false,将当前 beanName 放入一个 alreadyCreated 的 Set 集合中。 markBeanAsCreated(beanName); } StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate") .tag("beanName", name); try { if (requiredType != null) { beanCreation.tag("beanType", requiredType::toString); } // 到这里的话,要准备创建 Bean 了,对于 singleton 的 Bean 来说,容器中还没创建过此 Bean;对于 prototype 的 Bean 来说,本来就是要创建一个新的 Bean。 RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on.先初始化依赖的所有 Bean。这里的依赖指的是 depends-on 中定义的依赖 String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { //检查是不是有循环依赖,这里的循环依赖和我们前面说的循环依赖又不一样,这里肯定是不允许出现的,不然要乱套了 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } //注册一下依赖关系 registerDependentBean(dep, beanName); try { //先初始化被依赖项 getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance.如果是 singleton scope 的,创建 singleton 的实例 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { //执行创建 Bean return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } //如果是 prototype scope 的,创建 prototype 的实例 else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); //执行创建 Bean prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } //如果不是 singleton 和 prototype 的话,需要委托给相应的实现类来处理 else { String scopeName = mbd.getScope(); if (!StringUtils.hasLength(scopeName)) { throw new IllegalStateException("No scope name defined for bean '" + beanName + "'"); } Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { //执行创建 Bean return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new ScopeNotActiveException(beanName, scopeName, ex); } } } catch (BeansException ex) { beanCreation.tag("exception", ex.getClass().toString()); beanCreation.tag("message", String.valueOf(ex.getMessage())); cleanupAfterBeanCreationFailure(beanName); throw ex; } finally { beanCreation.end(); } } return adaptBeanInstance(name, beanInstance, requiredType); } }
- 然后呢,AbstractBeanFactory 中的getBean(beanName) 方法中比较重要的点是createBean 方法:
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException
;//第三个参数 args 数组代表创建实例需要的参数,不就是给构造方法用的参数,或者是工厂 Bean 的参数嘛。在我们的初始化阶段,args 是 null。
- 也就是finishBeanFactoryInitialization里面的这一句
- ConversionService的设置其实就是当咱们碰到使用自定义类型转换器从String转换为Date的方式时,Spring说你这个方式不行,看我的,Spring中还提供了另一种转换方式:使用Converter转换器,咱们先定义一个转换器:public class DemoByHuDateConverter implements Converter<String, date>{…},然后在在配置文件中注册咱们自己写的转换器,最后就可以直接调用咱们的转换器了。
- 12)通过finishRefresh()完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出Context RefreshEvent通知别人
- 在Spring中还提供了Lifecycle接口,Lifecycle中包含start/stop方法,实现此接口后Spring会保证在启动的时候调用其start方法开始生命周期,并在Spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,在启动后一直运行(如对MQ进行轮询等)。而ApplicationContext的初始化最后正是保证了这一功能的实现。
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { ... @SuppressWarnings("deprecation") protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning). clearResourceCaches(); // Initialize lifecycle processor for this context. //当ApplicationContext启动或停止时,它会通过LifecycleProcessor来与所有声明的bean的周期做状态更新,而在LifecycleProcessor的使用前首先需要初始化。 initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. //启动所有实现了Lifecycle接口的bean。 getLifecycleProcessor().onRefresh(); // Publish the final event. //当完成ApplicationContext初始化的时候,要通过Spring中的事件发布机制来发出ContextRefreshedEvent事件,以保证对应的监听器可以做进一步的逻辑处理。 publishEvent(new ContextRefreshedEvent(this)); } ... }
- 在Spring中还提供了Lifecycle接口,Lifecycle中包含start/stop方法,实现此接口后Spring会保证在启动的时候调用其start方法开始生命周期,并在Spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,在启动后一直运行(如对MQ进行轮询等)。而ApplicationContext的初始化最后正是保证了这一功能的实现。
- 1)通过prepareRefresh()方法进行初始化前的准备工作,在Spring启动的时候提前对必须的变量进行存在性验证。例如
-
巨人的肩膀:
(很多很多好的文章,特此感谢google以及B站各位前辈)
Spring源码深度解析
javadoop(文章写的很好的前辈)