创建容器我们在(二)里仔细的学习过了,现在再看容器的configureAndRefreshWebApplicationContext,配置并且刷新,这一步是整个spring ioc的核心,点进去看
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getServletContextName()));
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
}
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(sc);
wac.setParent(parent);
wac.setServletContext(sc);
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (initParameter != null) {
wac.setConfigLocation(initParameter);
}
customizeContext(sc, wac);
wac.refresh();
}
我们可以看到,首先spring会为容器创建一个id,这个id花了一大段的代码,我们可以看到,id的来源可以是我们在web.xml中配置的,也可以是自动生成的,如果自动生成,则跟servlet版本有关,具体的逻辑有兴趣的可以仔细看看。
接下来,加载父容器,通常情况下我们只有一个applicationContext.xml时这个父容器的引用是null.
wac.setServletContext(sc);
这一步,它将servletContext这个上下文放到了wac中,也就是说,在容器中,有了servlet的上下文参数,那么我们可以在spring后续的容器操作中,使用servlet的配置等信息了。
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
这里,去取web.xml里的配置信息了,就是我们在web.xml里配置的配置文件路径,我们看看
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
于是我们去web.xml里找contextConfigLocation,可以看到
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:spring/applicationcontext.xml,
classpath*:spring/spring-*.xml,
classpath*:spring/consumer/spring-*.xml,
classpath*:spring/provider/spring-*.xml
</param-value>
</context-param>
我这里是配置了多个文件,整个是一个字符串,spring会获取他们并且set他们
if (initParameter != null) {
wac.setConfigLocation(initParameter);
}
我们看看
wac.setConfigLocation(initParameter);
他的实现在哪里
首先,我们的容器是默认的xmlWebApplicationContext
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
然后
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
可以看到,他的爷爷类
AbstractRefreshableConfigApplicationContext
是一个抽象的,可配置的抽象容器
我们在看的过程中应该去思考,我们需要找到一些方法应该被spring放在什么地方,既然是配置信息,应该是放在可配置的容器中,而我们现在要找的是他的实现方法,所以应该是一个抽象类中,于是我们进到
AbstractRefreshableConfigApplicationContext中,ctrl+o,输入
setConfigLocation
接下来就能看到它怎么处理我们配置的那几个配置文件了
public void setConfigLocations(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;
}
}
可以看到它把配置放到了AbstractRefreshableConfigApplicationContext的一个成员数组configLocations中,这里先mark一下,因为这里它放进去,是为了后续取出来去加载bean用的,所以记牢
我们接着看容器的刷新
customizeContext(sc, wac);
这个方法,又可以顾名思义了,customize,自定义的。定制的。而且我百度上查过好像有人说早期版本的spring这个方法是空实现,那么我觉得应该这个地方应该是埋了个点,为了后续版本spring加入一些其他功能而打下的伏笔,再看这个方法的注释。
Customize the ConfigurableWebApplicationContext created by this ContextLoader after config locations have been supplied to the context but before the context is refreshed.
The default implementation determines what (if any) context initializer classes have been specified through context init parameters and invokes each with the given web application context.
Any ApplicationContextInitializers implementing Ordered or marked with @Order will be sorted appropriately.
在为容器设置了配置文件之后,并且在容器真正刷新之前,由contextLoader为容器进行一些定制操作
定制操作的默认的实现是去决定容器的初始化类,并且这些类是通过web.xml里的配置参数来决定的,它的执行依赖本容器。
任何初始化都实现Ordered或使用了@Order注解来排序
这里我没有太读懂排序是怎么排,排什么,断点到这里我的环境都是直接返回,看懂这块的朋友可以留言
好了,定制操作之后就是refresh了,
wac.refresh();
这个方法完成了spring 容器的刷新,我们点进去看下
可以看到,由很多步骤完成,每一步都值得我们细细的去看
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;
}
}
}
我们在接下来的(三),(四),(五)里,每一节就只学习一个步骤,一个方法,细细的看,慢慢的想