Spring源码分析 二
基于Spring 5.1.5
1.initWebApplicationContext源码分析
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...
//cwac.isActive()为什么是false呢?
//初始化的时候容器还未刷新,容器的刷新是在第三步进行
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = this.loadParentContext(servletContext);
cwac.setParent(parent);
}
this.configureAndRefreshWebApplicationContext(cwac, servletContext);
}
...
}
2.cwac.isActive()源码分析
通过看下方代码可以看出只有当value==0时,cwac.isActive()才为false
刷新之后value的值会置为1,讲解在后边
public boolean isActive() {
return this.active.get();
}
public final boolean get() {
return value != 0;
}
3.configureAndRefreshWebApplicationContext源码分析
容器的configureAndRefreshWebApplicationContext:配置并且刷新,这一步是整个spring ioc的核心
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
String configLocationParam;
//返回对象整体标识的字符串表示形式。
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
configLocationParam = sc.getInitParameter("contextId");
if (configLocationParam != null) {
wac.setId(configLocationParam);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
//它将servletContext这个上下文放到了wac中,
//也就是说,在Spring容器中,有了servlet的上下文参数,那么我们可以在spring后续的容器操作中,使用servlet的配置等信息了。
wac.setServletContext(sc);
//读取web.xml中Spring配置文件路径
configLocationParam = sc.getInitParameter("contextConfigLocation");
if (configLocationParam != null) {
//contextConfigLocation有可能有多个,通过StringTokenizer将给定字符串标记为字符串数组。
//字符串数组放到了AbstractRefreshableConfigApplicationContext的一个成员数组configLocations中,
//这里先mark一下,因为这里它放进去,是为了后续取出来去加载bean用的,所以记牢
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
}
//这一步没看懂,定制什么
this.customizeContext(sc, wac);
//这个方法就要开始Spring容器的刷新了
wac.refresh();
}
4.refresh()源码分析
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
//preRefresh预刷新,其实就是把记录一下开始时间,打印一下日志,然后把servletConfig和servletContext放到spring容器的propertySources容器里面.
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 var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
prepareRefresh()源码分析:
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
//修改active状态为true,可以认为它是容器的激活状态,既将第2步的value值置为1
this.active.set(true);
if (this.logger.isDebugEnabled()) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Refreshing " + this);
} else {
this.logger.debug("Refreshing " + this.getDisplayName());
}
}
//初始化propertySources这个容器
//初始化propertySources,注意了,这个propertySources不是一堆propertySource啊,而是一个容器
//spring里有两个概念,一个是propertySource,一个是propertySources,后者不是简单的表示一堆前者,而是表示一个存放前者的容器。
//下方的源码分析中说明
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();
}
PropertySource源码分析
从泛型和构造我们可以大致了解,propertySource,是一个用来存放property的keyValue的实体,构造表示,我拿任意类型的对象进来,都可以设置一个key,存在成员name上,然后把对象存放在成员source上,是不是很像一个Map.Entry<String,Object>对象呢。
public abstract class PropertySource<T> {
protected final Log logger;
protected final String name;
protected final T source;
public PropertySource(String name, T source) {
this.logger = LogFactory.getLog(this.getClass());
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;
}
}
PropertySources源码分析
可以看到,他是propertySource的一个容器接口,具备迭代能力,有2个容器级别的方法,contains和get,这都是集合中常见的概念,它只有一个实现类,MutablePropertySources
public interface PropertySources extends Iterable<PropertySource<?>> {
default Stream<PropertySource<?>> stream() {
return StreamSupport.stream(this.spliterator(), false);
}
boolean contains(String var1);
@Nullable
PropertySource<?> get(String var1);
}
MutablePropertySources源码分析
它有一个propertySourceList,是用链表List来实现的,用于存放所有的propertySource,并且MutablePropertySources实现了contanins,get等集合方法,还有addFirst,addLast等添加对象的方法。在我们还没看到propertyResolver之前,先可以认为propertySource的检索都由容器自己完成,后续会看到一个全新的工具resolver,专门用于propertySources的检索,解析等相关工作
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList;
...
再回到initPropertySources方法中
我们可以看到,initServletProperSources,顾名思义,就是初始化propertySources这个容器,那我们应该可以想到,它应该是往容器里塞点东西, 再看后面2个参数,一个是servletContext,一个servletConfig,那么我们知道了,它这个初始化,其实是想把servletContext,一个servletConfig这两个对象给存到容器的propertySources容器里去
protected void initPropertySources() {
ConfigurableEnvironment env = this.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(this.servletContext, this.servletConfig);
}
}
initPropertySources中的initServletPropertySources分析
我们在之前容器里已经有servletContext了,那么现在servletContext就被propertySources接管了
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(this.getPropertySources(), servletContext, servletConfig);
}
public static void initServletPropertySources(MutablePropertySources sources, @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
Assert.notNull(sources, "'propertySources' must not be null");
String name = "servletContextInitParams";
if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletContextPropertySource(name, servletContext));
}
name = "servletConfigInitParams";
if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
}
}