前面的几篇文章我们一直围绕着 BeanFactory 分析容器的初始化和依赖注入过程,本篇我们将从 ApplicationContext 触发探究容器的高级形式。ApplicationContext 相对于 BeanFactory 扩展了许多实用功能,方便开发者的使用。二者的结构设计我们在前面的文章中已经介绍过,本篇将详细分析基于 ApplicationContext 的容器初始化和注入过程。
关于 ApplicationContext 的使用方式,广大开发者应该是信手拈来,这里还是简单的举例一下:
1 2 ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-core.xml" ); MyBean myBean = (MyBean) context.getBean("myBean" );
相对于 BeanFactory 来说,ApplicationContext 在使用方式上没有太大的区别,也是分成两步走:第一步加载配置;第二步执行 bean 实例的创建和初始化过程。其中,第二步与之前我们分析 BeanFactory 时的 BeanFactory#getBean
方法复用的是一套逻辑。
由前面文章介绍的继承关系我们知道,ApplicationContext 不是一个新的策略实现类,而是从 BeanFactory 扩展而来,并且将主要的精力都放在了对配置文件加载和解析层面。这样的实现也是很容易理解的,毕竟第一步是和开发者息息相关的,是开发者能够直接配置的东西,这一块的优化能够直观反映在框架的使用上,而第二步主要是框架内部的运作流程。所以接下来我们主要探究第一步的实现过程,而第二步则可以参考上一篇专门分析 bean 实例创建与初始化过程的文章。
第一步的逻辑暴露给开发者的接口位于 ClassPathXmlApplicationContext 类的构造方法中,我们通过 new ClassPathXmlApplicationContext("classpath:spring-core.xml")
触发高级容器加载和解析配置的逻辑,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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); this .setConfigLocations(configLocations); if (refresh) { refresh(); } }
Spring 定义了 AbstractRefreshableConfigApplicationContext#configLocation
数组用来记录传递的配置文件路径。因为允许传递多个配置文件,考虑配置文件的组织形式不一定是容器能够理解的方式,所以还需要执行一些解析的工作:
1 2 3 4 5 6 7 8 9 10 11 12 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 ; } }
我们传递的配置文件路径可能存在一些占位符,所以容器需要对这些占位符进行解析,使用真实指代的值进行替换。
接下来,Spring 会调用 AbstractApplicationContext#refresh
方法初始化 IoC 容器,该方法是 ApplicationContext 的核心,概括了高级容器的整体初始化过程,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public void refresh () throws BeansException, IllegalStateException { synchronized (this .startupShutdownMonitor) { this .prepareRefresh(); ConfigurableListableBeanFactory beanFactory = this .obtainFreshBeanFactory(); this .prepareBeanFactory(beanFactory); try { this .postProcessBeanFactory(beanFactory); this .invokeBeanFactoryPostProcessors(beanFactory); this .registerBeanPostProcessors(beanFactory); this .initMessageSource(); this .initApplicationEventMulticaster(); this .onRefresh(); this .registerListeners(); this .finishBeanFactoryInitialization(beanFactory); this .finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex); } this .destroyBeans(); this .cancelRefresh(ex); throw ex; } finally { this .resetCommonCaches(); } } }
高级容器的初始化整体流程可以概括为:
初始化上下文环境;
初始化 BeanFactory,加载并解析配置;
增强 BeanFactory,附加标准上下文特征;
后置处理 BeanFactory,这一过程交由子类实现,以提升容器的可扩展性;
后置处理 BeanFactory,应用 BeanFactoryPostProcessor 处理器;
注册 BeanPostProcessor 处理器;
初始化国际化资源;
注册事件通知广播器;
调用模板方法 AbstractApplicationContext#onRefresh
,可以通过覆盖实现该方法扩展 refresh 流程;
向事件通知广播器中注册事件监听器;
实例化非延迟加载的 singleton 类对象;
完成刷新过程,发布事件通知。
其中步骤 1 和 2 已经完成了简单容器中解析配置文件、以 BeanDefinition 对象承载配置,并注册到容器的全部过程,从第 3 步开始进入属于高级容器的扩展实现。下面对上述步骤中的关键点展开分析。
初始化上下文环境
初始化上下文作为整个流程的第一步,包含了重置上下文状态、解析属性占位符,以及验证必要属性是否缺失等工作,由 AbstractApplicationContext#prepareRefresh
方法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 protected void prepareRefresh () { this .startupDate = System.currentTimeMillis(); this .closed.set(false ); this .active.set(true ); this .initPropertySources(); this .getEnvironment().validateRequiredProperties(); if (this .earlyApplicationListeners == null ) { this .earlyApplicationListeners = new LinkedHashSet<>(this .applicationListeners); } else { this .applicationListeners.clear(); this .applicationListeners.addAll(this .earlyApplicationListeners); } this .earlyApplicationEvents = new LinkedHashSet<>(); }
对于一些必要的属性,如果缺失会影响系统的正常执行逻辑,对于这类属性可以调用 ConfigurablePropertyResolver#setRequiredProperties
方法将其设置为 required,这样在初始化上下文时就会校验其是否存在,如果不存在则会提前抛出异常。
创建并初始化 BeanFactory
ApplicationContext 是基于 BeanFactory 的扩展实现,复用了 BeanFactory 加载并解析配置文件的过程。所以在这一步,ApplicationContext 就已经完成了加载静态配置,并解析成为 BeanDefinition 对象注册到 IoC 容器的过程。这里的 BeanFactory 具体实现是 DefaultListableBeanFactory 类,前面曾强调过该类在 BeanFactory 的继承体系中占有着相当重要的地位,是 IoC 容器完整功能的一个基本实现。
基于 DefaultListableBeanFactory 创建并初始化 BeanFactory 的实现位于 AbstractApplicationContext#obtainFreshBeanFactory
方法中,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 protected ConfigurableListableBeanFactory obtainFreshBeanFactory () { this .refreshBeanFactory(); return this .getBeanFactory(); } protected final void refreshBeanFactory () throws BeansException { if (this .hasBeanFactory()) { this .destroyBeans(); this .closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = this .createBeanFactory(); beanFactory.setSerializationId(this .getId()); this .customizeBeanFactory(beanFactory); this .loadBeanDefinitions(beanFactory); synchronized (this .beanFactoryMonitor) { this .beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + this .getDisplayName(), ex); } }
上述方法首先会判断是否已经有创建的 BeanFactory 实例存在,如果存在则说明上下文之前已经被 refresh 过,且没有正常关闭对应的 BeanFactory,所以再次被 refresh 之前需要执行关闭操作。接着,调用 AbstractXmlApplicationContext#loadBeanDefinitions
方法加载并解析配置。
如果看过本系列之前的文章,你一定会理解本方法的作用,并体会到其过程的复杂程度。该方法包含了获取配置文件 Document 对象、执行默认标签和自定义标签的解析,并最终将配置封装到 BeanDefinition 对象中返回的逻辑。经过这一系列步骤,XML 中静态的配置就会转变成内存中的数据结构 BeanDefinition 对象注册到 IoC 容器中。
为 BeanFactory 附加标准上下文特征
上面两个步骤已经完成了简单容器中加载并解析配置的功能,以此为分界线将开始对简单容器进行功能增强处理。增强的第一步就是调用 AbstractApplicationContext#prepareBeanFactory
方法为 BeanFactory 设置一些上下文所应该具备的标准特性,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 protected void prepareBeanFactory (ConfigurableListableBeanFactory beanFactory) { beanFactory.setBeanClassLoader(this .getClassLoader()); beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this , this .getEnvironment())); beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this )); beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this ); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this ); beanFactory.registerResolvableDependency(ApplicationContext.class, this ); beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this )); if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, this .getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, this .getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, this .getEnvironment().getSystemEnvironment()); } }
上述实现为 BeanFactory 增加了如下上下文特性:
增加了对 Spring SpEL 表达式的支持。
添加属性编辑器,以实现对某些类型属性的统一处理。
自动装配 Aware 类,注入相应的资源。
自动探测 ApplicationListener 监听器类。
增加了对 AspectJ 的支持。
注册系统环境变量相关的 bean 实例,用于获取系统环境变量。
Spring Expression Language,即 SpEL 表达式是在 3.0 版本引入的新特性,允许我们在配置时候以类似 EL 表达式的方式引用上下文中的变量,未接触过的同学可以自己去体验一下。
执行属性注入时,如果希望对某一类型的属性执行一些处理,可以通过自定义属性编辑器 PropertyEditor 实现。典型的应用场景就是对时间类型属性的转换,假设某个 bean 实例存在 LocalDate 类型的属性,这个时候我们直接以字符串配置进行注入是会出错的,解决的方法就是通过自定义属性编辑器实现类型的转换,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class LocalDatePropertyEditor extends PropertyEditorSupport implements InitializingBean { private String format = "yyyy-MM-dd" ; private DateTimeFormatter formatter; @Override public void afterPropertiesSet () throws Exception { formatter = DateTimeFormatter.ofPattern(format); } @Override public void setAsText (String text) throws IllegalArgumentException { if (StringUtils.hasText(text)) { this .setValue(LocalDate.parse(text, formatter)); } throw new IllegalArgumentException("illegal property: " + text); } public void setFormat (String format) { this .format = format; this .formatter = DateTimeFormatter.ofPattern(format); } }
1 2 3 4 5 6 7 8 9 10 11 12 <bean class ="org.springframework.beans.factory.config.CustomEditorConfigurer" > <property name ="customEditors" > <map > <entry key ="java.time.LocalDate" > <bean class ="org.zhenchao.spring.ioc.LocalDatePropertyEditor" > <property name ="format" value ="yyyy-MM-dd" /> </bean > </entry > </map > </property > </bean >
通过自定义属性编辑器 LocalDatePropertyEditor,基于时间格式化工具对指定格式的字符串日期进行转换和注入。上述转换只能在以 ApplicationContext 方式加载 bean 实例的前提下才生效,如果使用的是 BeanFactory 则还是会抛出异常,毕竟这属于高级容器中增强的功能。
当定义的 bean 实现了 Aware 接口时,这些 bean 可以比一般的 bean 多拿到一些资源。ApplicationContext 对于 BeanFactory 的扩展增加了对一些 Aware 类自动装配支持。ApplicationContextAwareProcessor 实现了 BeanPostProcessor 接口,并主要覆盖实现了 ApplicationContextAwareProcessor#postProcessBeforeInitialization
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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)) { return bean; } AccessControlContext acc = null ; if (System.getSecurityManager() != null ) { acc = this .applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null ) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { this .invokeAwareInterfaces(bean); return null ; }, acc); } else { this .invokeAwareInterfaces(bean); } return bean; } 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 ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this .applicationContext); } }
对于实现了指定 Aware 接口的 bean 类,上述方法会将相应的资源给到该 bean 实例。以 ApplicationContextAware 接口为例,实现了该接口的 bean 都持有 ApplicationContext 实例,而上述方法也正是通过调用 bean 的 setter 方法将 ApplicationContext 实例注入到 bean 实例中。
自动探测 ApplicationListener 监听器
自 4.3.4 版本起,Spring 增加了对实现了 ApplicationListener 接口监听器的探测。这一机制由 ApplicationListenerDetector 类实现,核心逻辑为 `` 方法中:。ApplicationListenerDetector 实现了 MergedBeanDefinitionPostProcessor 和 DestructionAwareBeanPostProcessor 后置处理接口,并相应实现了这些后置处理器定义的模板方法,其中核心的方法 postProcessAfterInitialization 逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public Object postProcessAfterInitialization (Object bean, String beanName) { if (bean instanceof ApplicationListener) { Boolean flag = this .singletonNames.get(beanName); if (Boolean.TRUE.equals(flag)) { this .applicationContext.addApplicationListener((ApplicationListener<?>) bean); } else if (Boolean.FALSE.equals(flag)) { if (logger.isWarnEnabled() && !this .applicationContext.containsBean(beanName)) { logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " + "but is not reachable for event multicasting by its containing ApplicationContext " + "because it does not have singleton scope. Only top-level listener beans are allowed to be of non-singleton scope." ); } this .singletonNames.remove(beanName); } } return bean; }
该方法针对实现了 ApplicationListener 接口的单例对象,统一注册到监听器集合中监听事件。方法中的 ApplicationListenerDetector#singletonNames
变量则是在 ApplicationListenerDetector#postProcessMergedBeanDefinition
方法中完成构建:
1 2 3 4 5 public void postProcessMergedBeanDefinition (RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { if (ApplicationListener.class.isAssignableFrom(beanType)) { this .singletonNames.put(beanName, beanDefinition.isSingleton()); } }
因此,我们可以知道该变量中记录了 beanName 与实现了 ApplicationListener 接口的 singleton 实例之间的映射关系。
应用 BeanFactoryPostProcessor 处理器
BeanFactoryPostProcessor 处理器用于对 BeanFactory 实例进行后置处理,这和 BeanPostProcessor 以 bean 实例作为处理对象有着本质的区别。所以,执行到这里就已经开始应用这些已注册的 BeanFactoryPostProcessor 处理器对前面已经准备好的 BeanFactory 对象进行处理,但是需要清楚的一点是此时还没有开始创建 bean 实例。应用 BeanFactoryPostProcessor 处理器的过程位于 AbstractApplicationContext#invokeBeanFactoryPostProcessors
方法中:
1 2 3 4 5 6 7 8 9 10 11 12 protected void invokeBeanFactoryPostProcessors (ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this .getBeanFactoryPostProcessors()); if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
注意,这里调用 AbstractApplicationContext#getBeanFactoryPostProcessors
方法获取的 BeanFactoryPostProcessor 全部是通过编码注册而非配置的。通过调用如下方法可以以编码的方式注册 BeanFactoryPostProcessor 处理器:
1 2 3 4 public void addBeanFactoryPostProcessor (BeanFactoryPostProcessor postProcessor) { Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null" ); this .beanFactoryPostProcessors.add(postProcessor); }
笔者最开始看这段代码时,潜意识认为所有的 BeanFactoryPostProcessor 都是记录在 AbstractApplicationContext#beanFactoryPostProcessors
属性中的,如果以配置的方式使用过 BeanFactoryPostProcessor 的同学都知道该处理器的配置仅仅需要在配置文件中配置一个 <bean />
标签,而不需要在其它任何地方去注册或引用这个 bean 实例。如下:
1 <bean id ="myBeanFactoryPostProcessor" class ="org.zhenchao.processor.MyBeanFactoryPostProcessor" />
所以,我就去代码中寻找 Spring 是如何把上述配置给添加到 AbstractApplicationContext#beanFactoryPostProcessors
属性中的,即在哪里调用了 AbstractApplicationContext#addBeanFactoryPostProcessor
方法,结果当然是一无所获,因为出发点就是错误的。由下面的实现你将会看到这里传递的仅仅是通过编码注册的 BeanFactoryPostProcessor 处理器,而基于配置注册的 BeanFactoryPostProcessor 处理器则通过其它方式获取。
相应的实现位于 PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
方法中,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 public static void invokeBeanFactoryPostProcessors ( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } } List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true , false ); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true , false ); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); boolean reiterate = true ; while (reiterate) { reiterate = false ; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true , false ); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true ; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); } invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); } else { invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true , false ); List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { } else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } } sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); beanFactory.clearMetadataCache(); }
可以看到针对编码注册和以配置方式注册的 BeanFactoryPostProcessor 处理器实例,Spring 的获取方式是不一样的。前者都是注册到之前提到的 AbstractApplicationContext#beanFactoryPostProcessors
属性中,而后者都是通过 BeanFactory#getBean
方法获取到,所以是由 IoC 容器管理的。
上述方法的执行流程可以分为处理 BeanDefinitionRegistryPostProcessor 和处理 BeanFactoryPostProcessor 两部分。BeanDefinitionRegistryPostProcessor 扩展自 BeanFactoryPostProcessor,继承关系如下:
1 2 3 4 5 6 7 public interface BeanFactoryPostProcessor { void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException ; } public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException ; }
整个方法的前半部分都是在应用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
方法对实现了 BeanDefinitionRegistry 接口的 BeanFactory 实例进行后置处理,而后半部分则是在应用 BeanFactoryPostProcessor#postProcessBeanFactory
方法对所有类型的 BeanFactory 实例进行后置处理。考虑配置 BeanFactoryPostProcessor 顺序的不确定性,Spring 支持对其设置优先级。由实现也可以看出,Spring 会依次应用 PriorityOrdered、Ordered,以及剩余类型的处理器,并针对各类型按照比较器进行排序处理。
注册 BeanPostProcessor 处理器
不同于上一步介绍的对 BeanFactoryPostProcessor 的处理过程,对于 BeanPostProcessor 而言,这一步仅仅是注册而非应用。因为 BeanPostProcessor 处理器作用于 bean 实例之上,而当前还没有开始创建 bean 实例。之前讲解 BeanFactory#getBean
过程的文章中我们已经分析过,对于 BeanPostProcessor 的应用是发生在 bean 实例的创建和初始化过程中,具体来说是围绕 bean 初始化过程的前后:
1 2 3 4 5 6 7 8 9 10 11 public interface BeanPostProcessor { default Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { return bean; } default Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { return bean; } }
注册 BeanPostProcessor 处理器的过程本质上是将配置的 BeanPostProcessor 实例记录到 AbstractBeanFactory#beanPostProcessors
属性中的过程。在简单容器中需要通过调用 AbstractBeanFactory#addBeanPostProcessor
方法手动注册我们实现的 BeanPostProcessor 处理器,而在高级容器中则支持自动扫描注册。相应的实现位于 AbstractApplicationContext#registerBeanPostProcessors
方法中,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 protected void registerBeanPostProcessors (ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this ); } public static void registerBeanPostProcessors ( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true , false ); int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<BeanPostProcessor> internalPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); priorityOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } } sortPostProcessors(priorityOrderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); for (String ppName : orderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); orderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } sortPostProcessors(orderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, orderedPostProcessors); List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); for (String ppName : nonOrderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); nonOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); sortPostProcessors(internalPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, internalPostProcessors); beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext)); }
上述实现与处理 BeanFactoryPostProcessor 处理的过程大同小异,同样按优先级进行注册。
初始化国际化资源
笔者之前所负责的几个项目都需要考虑国际化支持,有的直接基于 JDK 原生的 ResourceBundle,有的则基于 Spring 提供的 MessageSource。在具体分析 MessageSource 的初始化过程之前,我们先来了解一下 Spring 国际化支持的设计与简单使用。
Spring MessageSource 支持本质上也是对 ResourceBundle 的封装。MessageSource 接口的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public interface MessageSource { String getMessage (String code, Object[] args, String defaultMessage, Locale locale) ; String getMessage (String code, Object[] args, Locale locale) throws NoSuchMessageException ; String getMessage (MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException ; }
Spring MessageSource 相关类的继承关系如下图所示:
其中 HierarchicalMessageSource 的设计为 MessageSource 提供了层次支持,建立了父子层级结构。ResourceBundleMessageSource 和 ReloadableResourceBundleMessageSource 是我们常用的两个类,均可以看做是对 JDK 原生国际化支持的封装,后者相对于前者提供了定时更新资源文件的支持,避免资源更新时重启系统。StaticMessageSource 用于支持编码式资源注册,DelegatingMessageSource 则可以看作是 MessageSource 的一个代理,必要时对 MessageSource 进行封装。
下面通过示例演示一下 MessageSource 的简单使用。首先我们定义好国际化资源文件:
1 spring=Spring framework is a good design, the latest version is {0}
1 spring=Spring 框架設計精良,當前最新版本是 {0}
resource_zh_CN.properties
1 spring=Spring 框架设计精良,当前最新版本是 {0}
需要注意的是,如果资源文件包含非 ASCII 字符,则需要将文本内容转换成 Unicode 编码,JDK 自带的 native2ascii 工具可以达到目的,操作如下:
1 native2ascii -encoding utf-8 resource_zh.properties resource_zh_tmp.properties
然后我们在配置文件中进行如下配置:
1 2 3 4 5 6 7 8 <bean id ="messageResource" class ="org.springframework.context.support.ResourceBundleMessageSource" > <property name ="basenames" > <list > <value > i18n/resource</value > </list > </property > </bean >
调用方式如下:
1 2 3 4 5 ApplicationContext context = new ClassPathXmlApplicationContext("spring-core.xml" ); Object[] params = {"5.2.6.RELEASE" }; System.out.println(context.getMessage("spring" , params, Locale.ENGLISH)); System.out.println(context.getMessage("spring" , params, Locale.TRADITIONAL_CHINESE)); System.out.println(context.getMessage("spring" , params, Locale.SIMPLIFIED_CHINESE));
因为 ApplicationContext 同样实现了 MessageSource 接口,所以可以直接调用 ApplicationContext#getMessage
方法,但是这样调用的前提是配置中的 ID 必须设置为 messageResource
。上述示例如下:
1 2 3 Spring framework is a good design, the latest version is 5.2.6.RELEASE Spring 框架設計精良,當前最新版本是 5.2.6.RELEASE Spring 框架设计精良,当前最新版本是 5.2.6.RELEASE
下面分析高级容器对于 MessageSource 的初始化过程,由 AbstractApplicationContext#initMessageSource
方法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 protected void initMessageSource () { ConfigurableListableBeanFactory beanFactory = this .getBeanFactory(); if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { this .messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); if (this .parent != null && this .messageSource instanceof HierarchicalMessageSource) { HierarchicalMessageSource hms = (HierarchicalMessageSource) this .messageSource; if (hms.getParentMessageSource() == null ) { hms.setParentMessageSource(this .getInternalParentMessageSource()); } } } else { DelegatingMessageSource dms = new DelegatingMessageSource(); dms.setParentMessageSource(this .getInternalParentMessageSource()); this .messageSource = dms; beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this .messageSource); } }
前面的示例在配置时推荐将 ID 配置为 messageSource
是有原因的,通过阅读上述实现一目了然。代码中硬编码要求我们配置 messageSource
作为 MessageSource 实例的 ID,否则不执行初始化,而是创建一个默认的代理,(如果存在的话)委托给父上下文中的 MessageSource 实例。如果通过调用 ApplicationContext#getMessage
方法尝试获取对应的资源则会出现异常,此时就需要我们手动指定 ApplicationContext#getBean
时的名称。然而,Spring 并不推荐这样做,既然框架以约定的方式提供了相应的实现,还是推荐以 messageSource
作为 ID 进行配置为好,毕竟约定优于配置。
注册事件通知广播器
事件广播和监听机制是典型的观察者模式实现,而 ApplicationEventMulticaster 则充当观察者模式中主题角色。如果在 Spring 中希望监听事件广播器广播的事件,需要定义一个实现了 ApplicationListener 接口的监听器。Spring 支持通过编码注册和自动扫描注册两种方式注册 ApplicationListener 监听器,这个我们稍后会细说,首先来看一下广播器的初始化过程,实现位于 AbstractApplicationContext#initApplicationEventMulticaster
方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 protected void initApplicationEventMulticaster () { ConfigurableListableBeanFactory beanFactory = this .getBeanFactory(); if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this .applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); } else { this .applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this .applicationEventMulticaster); } }
如果我们以约定的方式配置了自定义的事件广播器,则上述过程会初始化该广播器实例;否则容器会创建并注册一个默认的 SimpleApplicationEventMulticaster 广播器。下面以 SimpleApplicationEventMulticaster 广播器为例分析广播事件通知的过程,主要来看对于 ApplicationEventMulticaster#multicastEvent
方法的实现,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public void multicastEvent (final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : this .resolveDefaultEventType(event)); Executor executor = this .getTaskExecutor(); for (ApplicationListener<?> listener : this .getApplicationListeners(event, type)) { if (executor != null ) { executor.execute(() -> this .invokeListener(listener, event)); } else { this .invokeListener(listener, event); } } } protected void invokeListener (ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = this .getErrorHandler(); if (errorHandler != null ) { try { this .doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { this .doInvokeListener(listener, event); } } private void doInvokeListener (ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || this .matchesClassCastMessage(msg, event.getClass())) { Log logger = LogFactory.getLog(this .getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
典型的回调观察者监听方法的逻辑,如果对于观察者模式了解的话,这里的逻辑会比较好理解。
注册事件监听器
既然有被观察者,就应该有对应的观察者,事件监听器 ApplicationListener 充当观察者角色。我们需要通过注册 ApplicationListener 来监听事件通知,针对 ApplicationListener 的注册,可以通过编码的方式进行注册,如果我们将其配置到配置文件中,容器也会自动扫描并注册。注册 ApplicationListener 的过程由 AbstractApplicationContext#registerListeners
方法实现,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 protected void registerListeners () { for (ApplicationListener<?> listener : this .getApplicationListeners()) { this .getApplicationEventMulticaster().addApplicationListener(listener); } String[] listenerBeanNames = this .getBeanNamesForType(ApplicationListener.class, true , false ); for (String listenerBeanName : listenerBeanNames) { this .getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } Set<ApplicationEvent> earlyEventsToProcess = this .earlyApplicationEvents; this .earlyApplicationEvents = null ; if (earlyEventsToProcess != null ) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { this .getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
上一步已经完成了对于事件广播器 ApplicationEventMulticaster 的注册操作,这一步所完成的工作就是将编码和配置注册的 ApplicationListener 注册到广播器中。如果在前面的过程中已经记录了一些需要广播的事件,那么在这一步会触发对于这些事件的广播通知,因为此时已经有事件广播器可用了。
实例化非延迟加载的 singleton 类对象
记得最开始学习 Spring 框架的时候,就看到说 BeanFactory 和 ApplicationContext 有一个很大的区别,即 BeanFactory 在初始化容器时不会实例化 bean 对象,而 ApplicationContext 则会实例化所有非延迟加载的 singleton 类实例。这是因为 ApplicationContext 会在初始化容器时通过调用 AbstractApplicationContext#finishBeanFactoryInitialization
方法对所有非延迟加载的 singleton 类进行实例化操作,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 protected void finishBeanFactoryInitialization (ConfigurableListableBeanFactory beanFactory) { 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)); } if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { @Override public String resolveStringValue (String strVal) { return getEnvironment().resolvePlaceholders(strVal); } }); } String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false , false ); for (String weaverAwareName : weaverAwareNames) { this .getBean(weaverAwareName); } beanFactory.setTempClassLoader(null ); beanFactory.freezeConfiguration(); beanFactory.preInstantiateSingletons(); }
由上述实现可以知道,Spring 在实例化非延迟加载的 singleton 对象之前先校验一些必要的工具实例是否存在,如果没有注册则会创建一个默认的作为代替。然后会冻结所有的 BeanDefinition 定义,毕竟即将开始执行实例化了,后续的更改也不会再生效。完成准备工作之后,容器即开始实例化所有满足条件(非 abstract && 单例 && 非 lazy-init)的 bean 对象。
实例化的过程由 DefaultListableBeanFactory#preInstantiateSingletons
方法实现,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public void preInstantiateSingletons () throws BeansException { List<String> beanNames = new ArrayList<>(this .beanDefinitionNames); for (String beanName : beanNames) { RootBeanDefinition bd = this .getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (this .isFactoryBean(beanName)) { Object bean = this .getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, this .getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { this .getBean(beanName); } } } else { this .getBean(beanName); } } } for (String beanName : beanNames) { Object singletonInstance = this .getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null ) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null ; }, this .getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
Spring 在 4.1 版本增加了 SmartInitializingSingleton 接口,实现了该接口的单例可以感知所有单例实例化完成的事件,而回调接口中声明的 SmartInitializingSingleton#afterSingletonsInstantiated
方法的逻辑由上述方法完成。
完成刷新过程
经过前面的过程,对于 ApplicationContext 而言已经完成了加载并解析配置,以及实例化所有非延迟加载的 singleton 类对象的过程。也就是说,高级容器初始化过程的主要工作已经做完了,最后需要调用 AbstractApplicationContext#finishRefresh
方法完成整个初始化进程,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 protected void finishRefresh () { this .initLifecycleProcessor(); this .getLifecycleProcessor().onRefresh(); this .publishEvent(new ContextRefreshedEvent(this )); LiveBeansView.registerApplicationContext(this ); }
Spring 很早就提供了 Lifecycle 接口,实现了该接口的 bean 可以感知到容器的启动和关闭状态,对应接口的 Lifecycle#start
和 Lifecycle#stop
方法,而方法 Lifecycle#start
的回调则位于此。Lifecycle 中方法的执行需要依赖于 LifecycleProcessor 处理器,我们可以自定义 LifecycleProcessor 实现,否则容器会创建一个默认的 DefaultLifecycleProcessor 对象,然后基于定义的 LifecycleProcessor 处理器调用满足条件的 bean 实例的 Lifecycle#start
方法。在完成了这一操作后,容器的初始化过程也就完成了,此时容器可以将容器刷新完毕事件通知到对应的事件监听器,即所有订阅 ContextRefreshedEvent 事件的监听器。
总结
至此,高级容器的初始化过程我们已经分析完了,不同于 BeanFactory 在该阶段仅仅是将静态配置转换成容器中对应的 BeanDefinition 实例,ApplicationContext 因为需要实例化所有非延迟加载的 singleton 对象,所以大部分的 bean 对象已经以实例的形式注册到容器中,后续我们再调用 ApplicationContext#getBean
方法也仅需要针对非单例的 bean 对象才执行复杂的实例化操作,所以在高级容器中本篇所分析的过程基本可以概括容器的大部分工作。
参考
Spring 源码深度解析