Spring-web源码解析之ContextLoader

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 基于版本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里面做的事情非常多,下一节再讲



目录
相关文章
|
11天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
40 2
|
28天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
30天前
|
搜索推荐 Java Spring
Spring Filter深度解析
【10月更文挑战第21天】Spring Filter 是 Spring 框架中非常重要的一部分,它为请求处理提供了灵活的控制和扩展机制。通过合理配置和使用 Filter,可以实现各种个性化的功能,提升应用的安全性、可靠性和性能。还可以结合具体的代码示例和实际应用案例,进一步深入探讨 Spring Filter 的具体应用和优化技巧,使对它的理解更加全面和深入。
|
17天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
41 9
|
12天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
18天前
|
JSON JavaScript 前端开发
蓝桥杯web组赛题解析和杯赛技巧
本文作者是一位自学前端两年半的大一学生,在第十五届蓝桥杯Web组比赛中获得省一和国三。文章详细解析了比赛题纲,涵盖HTML、CSS、JavaScript、Echarts和Vue等技术要点,并分享了备赛技巧和比赛经验。作者强调了多写代码和解题思路的重要性,同时提供了省赛和国赛的具体流程及注意事项。希望对参赛者有所帮助。
|
25天前
|
安全 前端开发 Java
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第26天】Web安全是现代软件开发的重要领域,本文深入探讨了XSS和CSRF两种常见攻击的原理及防御策略。针对XSS,介绍了输入验证与转义、使用CSP、WAF、HTTP-only Cookie和代码审查等方法。对于CSRF,提出了启用CSRF保护、设置CSRF Token、使用HTTPS、二次验证和用户教育等措施。通过这些策略,开发者可以构建更安全的Web应用。
64 4
|
24天前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
52 2
|
24天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
41 3
|
1月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
71 0

推荐镜像

更多
下一篇
无影云桌面