在(三)里我们已经进入了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的读取解析的很重要的工作。