在(三)里我们已经进入了AbstractApplicationContext的刷新方法,refresh,代码如下
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
它由很多个步骤组成,我们在(四)中就研究
prepareRefresh
这个步骤,看看刷新的第一步做了什么
顾名思义,刷新前的预处理,处理了什么,我们点进去看一下
protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); synchronized (this.activeMonitor) { this.active = true; } if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Initialize any placeholder property sources in the context environment initPropertySources(); // Validate that all properties marked as required are resolvable // see ConfigurablePropertyResolver#setRequiredProperties this.environment.validateRequiredProperties(); }
可以看到,这个方法里它产生了容器的开始时间,接着改了active状态,这里稍微要注意一下
synchronized (this.activeMonitor) { this.active = true; }
他在改状态的时候用activeMonitor加了锁,并且改了active的状态为true,作者这么写的用意一定是为了让active在后续的某个操作或某个判断中去使用,既然active = true是在容器启动的开始时改的,那么后续肯定是在一些需要判断容器是否启动了来的地方使用,我们可以认为它是容器的激活状态,后续在任何修改容器的激活状态的地方,也一定会用activeMonitor来加锁。
再往下,打印一行refresh的日志
if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); }
继续看
// Initialize any placeholder property sources in the context environment initPropertySources();
初始化propertySources,主意哈,这个propertySources不是一堆propertySource啊,而是一个容器
spring里有两个概念,一个是propertySource,一个是propertySources,后者不是简单的表示一堆前者,而是表示一个存放前者的容器。我之前也在强调,spring里有很多名词,我们都需要去熟悉它们,知道它们是干什么的。 这里稍微偏移了扩展一下,我们看看propertySource和PropertySources的源码
在core包下,我们找到propertySource
public abstract class PropertySource<T> { protected final Log logger = LogFactory.getLog(this.getClass()); protected final String name; protected final T source; /** * Create a new {@code PropertySource} with the given name and source object. */ public PropertySource(String name, T source) { Assert.hasText(name, "Property source name must contain at least one character"); Assert.notNull(source, "Property source must not be null"); this.name = name; this.source = source; }
从泛型和构造我们可以大致了解,propertySource,是一个用来存放property的keyValue的实体,构造表示,我拿任意类型的对象进来,都可以设置一个key,存在成员name上,然后把对象存放在成员source上,是不是很像一个Map.Entry<String,Object>对象呢。
这里稍微需要了解下他有2个静态内部类StubPropertySource和ComparisonPropertySource
StubPropertySource是Property的子类,用于充当存根,它的注释中提到实际的PropertySource的初始化工作,不能在容器创建之前进行,所以用存根来充当一个占位符,他用来占用一个预期的propertySource位置,等容器refresh的时候用来进行替换,替换后成一个真正的值
如果这段话不好理解,可以先稍微知道有这么个东西,它是一个变量,用来占着你的property的位置,等刷新的时候再进行填充。
ComparisonPropertySource是存根的子类,仅仅用于集合的操作。注释中也是这么说,我们在propertySource的named方法上按f2
Return a PropertySource implementation intended for collection comparison purposes only. Primarily for internal use, but given a collection of PropertySource objects, may be used as follows: List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>(); sources.add(new MapPropertySource("sourceA", mapA)); sources.add(new MapPropertySource("sourceB", mapB)); assert sources.contains(PropertySource.named("sourceA")); assert sources.contains(PropertySource.named("sourceB")); assert !sources.contains(PropertySource.named("sourceC"));
这里的集合比较不是简单的比较大小的意思,而 是一系列集合可比较的操作,如contains,equals等。
看完propertySource,我们再看propertySources
public interface PropertySources extends Iterable<PropertySource<?>> { /** * Return whether a property source with the given name is contained. * @param name the {@linkplain PropertySource#getName() name of the property source} to find */ boolean contains(String name); /** * Return the property source with the given name, {@code null} if not found. * @param name the {@linkplain PropertySource#getName() name of the property source} to find */ PropertySource<?> get(String name); }
可以看到,他是propertySource的一个容器接口,具备迭代能力,有2个容器级别的方法,contains和get,这都是集合中常见的概念,它只有一个实现类,MutablePropertySources
再看看MutablePropertySources
public class MutablePropertySources implements PropertySources { static final String NON_EXISTENT_PROPERTY_SOURCE_MESSAGE = "PropertySource named [%s] does not exist"; static final String ILLEGAL_RELATIVE_ADDITION_MESSAGE = "PropertySource named [%s] cannot be added relative to itself"; private final Log logger; private final LinkedList<PropertySource<?>> propertySourceList = new LinkedList<PropertySource<?>>();
他有一个propertySourceList,是用链表List来实现的,用于存放所有的propertySource,并且MutablePropertySources实现了contanins,get等集合方法,还有addFirst,addLast等添加对象的方法。在我们还没看到propertyResolver之前,先可以认为propertySource的检索都由容器自己完成,后续会看到一个全新的工具resolver,专门用于propertySources的检索,解析等相关工作
扯远了,回到容器刷新的第一步,initPropertySources方法来
@Override protected void initPropertySources() { super.initPropertySources(); WebApplicationContextUtils.initServletPropertySources( this.getEnvironment().getPropertySources(), this.servletContext, this.servletConfig); }
super的init是空实现,具体的初始化工作,直接由第2行开始
我们可以看到,initServletProperSources,顾名思义,就是初始化propertySources这个容器,那我们应该可以想到,它应该是往容器里塞点东西, 再看后面2个参数,一个是servletContext,一个servletConfig,那么我们知道了,它这个初始化,其实是想把servletContext,一个servletConfig这两个对象给存到容器的propertySources容器里去,我们点进去看看是不是和我们想得一样
public static void initServletPropertySources( MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) { Assert.notNull(propertySources, "propertySources must not be null"); if(servletContext != null && propertySources.contains(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) && propertySources.get(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) { propertySources.replace(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, new ServletContextPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext)); } if(servletConfig != null && propertySources.contains(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) && propertySources.get(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) { propertySources.replace(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, new ServletConfigPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig)); } }
果然是这样,key是常量,value是一个PropertySource,
我们看下replace的实现,实现当然在propertySources的唯一一个实现类中,MutablePropertySources
/** * Replace the property source with the given name with the given property source object. * @param name the name of the property source to find and replace * @param propertySource the replacement property source * @throws IllegalArgumentException if no property source with the given name is present * @see #contains */ public void replace(String name, PropertySource<?> propertySource) { logger.debug(String.format("Replacing [%s] PropertySource with [%s]", name, propertySource.getName())); int index = assertPresentAndGetIndex(name); this.propertySourceList.set(index, propertySource); }
可以看到,他先用name查出这个propertySource的下标,然后进行替换
由于我们在之前容器里已经有servletContext了,那么现在servletContext就被propertySources接管了,它用replace方法,将servletContext存放了进来,key是一个常量,value是一个ServetPropertySource对象,ServetPropertySource就类似一个Entry.
读到这里我明白了,原来preRefresh预刷新,其实就是把记录一下开始时间,打印一下日志,然后把servletConfig和servletContext放到spring容器的propertySources容器里面.
好了,下一节我们一起去读refresh的第2步,
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
这一步里,spring开始使用Reader,Resource,Document,Registry,去解析配置文件,生成BeanDefinition,并且在注册器里进行注册,可以说,这一步,完成bean的读取解析的很重要的工作。