Spring-web源码解析之ContextLoader

简介: 基于版本4.1.7.RELEASE ContextLoader :应用root application context初始化的实际执行着,被ContextLoaderListener调用...

基于版本4.1.7.RELEASE

ContextLoader应用root application context初始化的实际执行着,被ContextLoaderListener调用

构造函数:

public ContextLoader() {
}

根据servlet配置中的contextClass和contextConfigLocation来创建web application context,在其子类ContextLoaderListener被申明的时候会调用默认的构造函数。

带参数的构造函数:

public ContextLoader(WebApplicationContext context) {
   this.context = context;
}

context作用同ContextLoaderListener中说明一样,只是ContextLoaderListener调用super(context)的时候会调用到此方法中,然后将context赋值给类属性,查看类属性的申明:

/**
 * The root WebApplicationContext instance that this loader manages.
 */
private WebApplicationContext context;

即可明白通过构造函数设置的是root WebApplicationContext

下面来看在ContextLoaderListener的初始化事件通知中所调用的initWebApplicationContext方法

/**
 * 通过参数servletContext初始化WebApplicationContext,使用构造时提供的WebApplicationContext或者根据contextClass和contextConfigL * ocation(在web.xml定义)创建一个新的WebApplicationContext
 */
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
   //防止重复初始化
   if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
      throw new IllegalStateException;
   }

   try {
      //如果当前的rootWebApplicationContext为空 则创建一个,如果不为空有可能是构造时传进来的
      if (this.context == null) {
         this.context = createWebApplicationContext(servletContext);
      }
      if (this.context instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
	//判断是否active的条件是,至少被refresh了1次并且没有被关闭。refresh可以理解为同步配置数据
         if (!cwac.isActive()) {
            // context没有被refresh
            if (cwac.getParent() == null) {
               // context没有明确的父容器 读取父容器
               ApplicationContext parent = loadParentContext(servletContext);
               cwac.setParent(parent);
            }
	    //配置和刷新context
            configureAndRefreshWebApplicationContext(cwac, servletContext);
         }
      }
      //将context保存到WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
      //这里根据classloader来判断当前线程是否是加载ContextLoader的线程。
      ClassLoader ccl = Thread.currentThread().getContextClassLoader();
      if (ccl == ContextLoader.class.getClassLoader()) {
         currentContext = this.context;
      }
      else if (ccl != null) {
         currentContextPerThread.put(ccl, this.context);
      }
      return this.context;
   }
   catch (RuntimeException ex) {
   }
   catch (Error err) {
   }
}

这里有几个疑问,parent是什么,还有线程和context的对应关系是用来做什么的?

对于parent可以看loadParentContext这个方法:

protected ApplicationContext loadParentContext(ServletContext servletContext) {
   ApplicationContext parentContext = null;
   //获取web.xml配置文件中指定的locatorFactorySelector,如果没有配置这个选项,默认指向“class path*:beanRefContext.xml”
   String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
   //获取web.xml配置文件中指定的parentContentKey,这个key指向一个被加载完成的BeanFactory
   String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);

   if (parentContextKey != null) {
      //获取beanFactory定位器
      BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
      //根据定位器查找到指定的BeanFactory,该beanFactory加载了beanRefContext.xml(默认情况下),在设置配置文件路径完成后调用了自身的refresh,所以已经将配置文件中定义的内容加载完成了,而完成加载的ApplicationContext就是这里要返回的parentContext
      this.parentContextRef = locator.useBeanFactory(parentContextKey);
      parentContext = (ApplicationContext) this.parentContextRef.getFactory();
   }

   return parentContext;
}

这个方法一般是在EJB或者EAR需要共享容器的时候使用,对于一般的WEB型应用,由于不配置locatorFactorySelector,所以这个方法实际上并没有运行。


线程和context的对应关系存放在currentContextPerThread中,该变量的类型是Map<ClassLoader,WebApplicationContext>,至于为什么这么存,

在closeWebApplicationContext中我们可以一窥端倪

public void closeWebApplicationContext(ServletContext servletContext) {
   try {
//关闭当前的Context,关闭过程中会产生关闭事件通知,销毁当前Context关联的bean和beanFactory,回调子类的onClose方法,active设置成false。
      if (this.context instanceof ConfigurableWebApplicationContext) {
         ((ConfigurableWebApplicationContext) this.context).close();
      }
   }
   finally {
      ClassLoader ccl = Thread.currentThread().getContextClassLoader();
//释放ClassLoader和Context对应列表中当前ClassLoader对应的Context
      if (ccl == ContextLoader.class.getClassLoader()) {
         currentContext = null;
      }
      else if (ccl != null) {
         currentContextPerThread.remove(ccl);
      }
      servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
      if (this.parentContextRef != null) {
//释放对parent的引用
         this.parentContextRef.release();
      }
   }
}

currentContextPerThread是一个静态类型的引用,目的就是保存不同的ClassLoader对应的Context以防止在销毁Context时出现错误关闭的情况。



在上面的处理过程中,configureAndRefreshWebApplicationContext做了以下几件事

1 设置ID,把ServletContext中的ID赋值给WebApplicationContext,如果没有,则设置默认值

2 根据contextConfigLocation参数(web.xml)指定的地址,找到spring application的xml文件

3 初始化环境变量

4 根据定制的ApplicationContextInitializer初始化Context,这些定制器必须实现ApplicationContextInitializer  并且在contextInitializerClasses(web.xml)申明出来

5 刷新调用refresh方法

由此我们可以看出,如果要对容器做定制化修改,根据第四步来编写自定义类即可。

那么,走到这里,整个初始化就剩下refresh了,refresh里面做的事情非常多,下一节再讲



目录
相关文章
|
3天前
|
安全 前端开发 Java
10:基于Servlet模拟用户登录功能的实现与解析-Java Web
10:基于Servlet模拟用户登录功能的实现与解析-Java Web
16 3
|
6天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
6天前
|
缓存 Java 开发者
10个点介绍SpringBoot3工作流程与核心组件源码解析
Spring Boot 是Java开发中100%会使用到的框架,开发者不仅要熟练使用,对其中的核心源码也要了解,正所谓知其然知其所以然,V 哥建议小伙伴们在学习的过程中,一定要去研读一下源码,这有助于你在开发中游刃有余。欢迎一起交流学习心得,一起成长。
|
7天前
|
安全 网络协议 Java
Netty核心NioEventLoop源码解析(下)
Netty核心NioEventLoop源码解析(下)
21 0
|
7天前
|
算法 Java 索引
Netty核心NioEventLoop源码解析(上)
Netty核心NioEventLoop源码解析(上)
20 0
|
7天前
|
消息中间件 缓存 前端开发
Netty消息编码及发送源码解析
Netty消息编码及发送源码解析
7 0
|
9天前
|
Java Shell 测试技术
Spring源码搭建教程
Spring源码搭建教程
14 0
|
9天前
|
移动开发 网络协议 Java
Netty解码器源码解析
Netty解码器源码解析
13 0
|
9天前
|
SQL XML Java
Mybatis源码解析
Mybatis源码解析
19 0
|
10天前
|
canal 缓存 关系型数据库
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战

推荐镜像

更多