前言
今天继续上一篇文章,上篇文章中只要将了 我们SpringMVC 中的ContextLoaderListener 是怎么被加入监听 然后tomact又是怎么去加载web.config配置的 最后怎么去调用ContextLoaderListener#contextInitialized方法的 不清楚的小伙伴 可以回到上篇文章 去看下
ContextLoaderListener
我们从前文中分析得到 程序启动的时候 是调用了contextInitialized的方法
那我就从这个方法入手 看看到底执行了什么
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
从代码中 我们可以看出 这个方法时 重写了父类的方法 initWebApplicationContext方法 我们跟进去 可以看到 是父类ContentLoader的方法 从名字我们也可以看到 就是一个初始化webApplicationContenxt的
ServletContextEvent 我们从上篇文章中 也可以看出 其实就是包装了下ServletContext,ServletContextEvent.getServletContext()方法 最终就是返回的ServletContext对象,如果对ServletContext不清楚的 也可以去上篇文章中去看下 他是在ContentConfig中 处理上下文的方法lifecycleEvent中去创建的,每个web应用 只会分配一个ServletContext 可以获取应用的上下文
好的,继续回到initWebApplicationContext方法中
initWebApplicationContex
下面我截取一些核心的代码
/**
* 这边的英文注释 写的还是很清楚的 自己读一遍应该能看懂 我就不翻译了 怕词不答意
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
/**
* 这个是判断了下context是否为null 如果使用的构造函数 直接传入一个webApplicationContext 我们就不用创建了
* 当然我们这边我们初始化的时候 并没有传入 所以这边context是null 所以要走createWebApplicationContext流程
*/
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
// 这边做的就是 如果上下文没有刷新 就进行配置和刷新
// ConfigurableWebApplicationContext 这边的默认配置的实现是 XmlWebApplicationContext 这个可以在 ContextLoader.properties 的配置里面可以去 找到
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// 这边判断了下 当前容器 没有父容器 就创建父容器 并且设置
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
<!--这二就是设置了当前webApplicationContext 到ServletConetext的属性中-->
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
<!--获取当前的类加载器-->
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;//这边 为什么要这个判断 个人感觉是因为 context在初始化时候 可以又传入一个webApplicationConetext 而这个类可能是又Servlet容器 加载的 这样就可能会导致类加载器是不一样的 所以不能给currentContext赋值
}
else if (ccl != null) {
<!--这边是放到一个map 集合里面 存放着 对应类加载器下的web容器-->
currentContextPerThread.put(ccl, this.context);
}
return this.context;
}
initWebApplicationContex 方法主要做的就是创建root web容器 并对其进行刷新 读取的配置 从注释中也可以看到 就是context-para里面param-name:contextConfigLocation的value
这个就能看出 我们web.config里面的配置了 就是为了设置root webApplicatonContext的,
createWebApplicationContext
先来下代码
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
<!--这边就是获取了contextClass-->
Class<?> contextClass = determineContextClass(sc);
<!--做了下判断 是否是继承了ConfigurableWebApplicationContext的 代码写的很是很严谨的 哈哈-->
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);//这个就是通过反射创建一个WebApplicationContext对象
}
protected Class<?> determineContextClass(ServletContext servletContext) {
<!--这个配置就是读取我们web.config里面的contextpara 是否配置了contextClass 当然了 正常的情况下我们都没有配置 所有 逻辑一般走的都是下面的部分-->
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
<!--这个就是获取默认的配置 这个默认配置的是XmlWebApplicationContext 配置是的ContextLoader.properties 中-->
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
determineContextClass 方法就是获取我们的conetext类 一般都是读取的默认配置里的XmlWebApplicationContext,
总结
这篇文章很短 主要讲了的是怎么创建WebApplicationConetext的 当看到一个configureAndRefreshWebApplicationContext#refresh 就能串联起来 我们Spring 中IOC的初始化流程 这个找时间 聊一波 这个有点儿长。。。
好的 后面一篇文章 我们主要聊下 DispatcherServlet