SpringMVC的父子容器你都了解了吗?
yuyutoo 2024-10-26 16:07 8 浏览 0 评论
环境:Spring5.3.23
配置文件
如果我们不使用基于注解的方式,那么在Spring Web项目一般我们都会在web.xml文件中做如下的配置:
web.xml
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
父容器初始化
通过在web.xml中还会配置一个监听器,而这个监听器的作用就是初始化父容器的
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
上面配置的监听程序是用来初始化父容器。
public class ContextLoader {
private WebApplicationContext context;
private static final Properties defaultStrategies;
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
static {
try {
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
// 从类路径下查找ContextLoader.properties文件
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
// 加载配置文件;默认情况下会从spring-web-xxx.jar包下的org.springframework.web.context包下有该文件
// 文件内容:org.springframework.web.context.WebApplicationContext=\
// org.springframework.web.context.support.XmlWebApplicationContext
// 也就是默认情况下实例化的是XmlWebApplicationContext容器对象
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
}
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 获取容器ApplicationContext具体的类的Class对象
Class<?> contextClass = determineContextClass(sc);
// 实例化XmlWebApplicationContext对象,该对象就是基于xml的配置的解析类
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
protected Class<?> determineContextClass(ServletContext servletContext) {
public static final String CONTEXT_CLASS_PARAM = "contextClass";
// 获取在web.xml中配置的context-param参数名称为contextClass;这里其实就是配置你要使用哪个ApplicationContext容器对象
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
// 如果配置了使用你配置的ApplicationContext容器的具体对象
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
} else {
// 如果没有在web.xml配置文件中配置contextClass参数,则通过下面的方式获取
// 这里获取的就是上面static代码段中加载的配置
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
}
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac,ServletContext sc) {
wac.setServletContext(sc);
// 获取在web.xml中配置的上下文参数(context-param), 名称为contextConfigLocation的参数值
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
// 如果配置了设置配置文件路径
wac.setConfigLocation(configLocationParam);
}
// 执行刷新,也就是解析处理上面配置的spring配置文件
// 如果没有配置上面的contextConfigLocation参数,那么会读取默认的applicationContext.xml配置文件
// 查看XmlWebApplicationContext类
wac.refresh();
}
}
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
try {
if (this.context == null) {
// 创建容器,在父类中创建出XmlWebApplicationContext对象
this.context = createWebApplicationContext(servletContext);
}
// mlWebApplicationContext实现了ConfigurableWebApplicationContext接口
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
// 确定此应用程序上下文是否处于活动状态,即它是否已刷新至少一次且尚未关闭。
if (!cwac.isActive()) {
// ...
// 刷新上下文
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 将实例化的ApplicationContext保存到ServletContext对象中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
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;
}
}
}
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
} else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
// 调用父类AbstractRefreshableWebApplicationContext方法
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
}
// XmlWebApplicationContext在执行refresh的时候有这么一步
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
public void refresh() {
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
}
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
public String[] getConfigLocations() {
return super.getConfigLocations();
}
}
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
implements BeanNameAware, InitializingBean {
protected String[] getConfigLocations() {
// 如果没有配置,则调用默认的方法调用子类XmlWebApplicationContext重写的方法
return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}
}
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
protected final void refreshBeanFactory() throws BeansException {
// 调用子类XmlWebApplicationContext重写的方法
loadBeanDefinitions(beanFactory);
}
}
总结:
- 从配置文件中获取contextClass参数
如果没有则读取spring-web-xxx.jar中org.springframework.web.context包下的ContextLoader.properties文件
目的就是容器实例化具体ApplicationContext那个子类对象。 - 刷新初始化ApplicationContext对象
这里具体的类是XmlWebApplicationContext对象。
1. 首先是设置要读取的配置文件
读取web.xml中配置的<context-param>参数contextConfigLocation,如果没有设置该 参数,则使用默认的/WEB-INF/applicationContext.xml。
2. 调用refresh初始化spring容器 - 保存spring容器对象
实例化后会将该容器对象保存到ServletContext中,以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为name存入。
子容器初始化
子容器的初始化就是DispatcherServlet配置的<init-param>参数
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
public final void init() throws ServletException {
// 这里代码的作用就是用来读取DispatcherServlet配置的contextConfigLocation参数,然后设置到
// FrameworkServlet中的contextConfigLocation属性中
// 这里就是通过BeanWrapper来完成此操作
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
}
// 到这里就设置完了配置在DispatcherServlet中的contextConfigLocation参数
// 初始化Servlet Bean
initServletBean();
}
}
FrameworkServlet
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
// 该值在父类的init方法中已经通过BeanWrapper设置搞定了,当如如果没有配置会有默认机制的,下面会看到
private String contextConfigLocation;
protected final void initServletBean() throws ServletException {
this.webApplicationContext = initWebApplicationContext();
}
protected WebApplicationContext initWebApplicationContext() {
// 从ServletContext中读取在上面(初始化父容器)设置的WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
// 容器对象
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// ...
if (wac == null) {
// 查找容器
// 这里默认还是返回null,这里就是看你在配置DispatcherServlet的时候有没有配置contextAttribute属性
// 如果你设置了该属性,会从ServletContext中读取配置的contextAttribute属性对应的值(该值必须是WebApplicationContext)
// 如果不是则抛出异常
wac = findWebApplicationContext();
}
if (wac == null) {
// 到这还没有容器对象,则创建容器对象,同时这里的rootContext会作为父容器
wac = createWebApplicationContext(rootContext);
}
return wac;
}
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 这里就是获取要实例化的默认ApplictionContext对象,XmlWebApplicationContext
Class<?> contextClass = getContextClass();
// 实例化
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
// 设置父容器
wac.setParent(parent);
// 获取要读取加载的配置文件,如果你没有则就是null
String configLocation = getContextConfigLocation();
// 如果你没有配置,不进入
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
// 刷新上下文
configureAndRefreshWebApplicationContext(wac);
return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
// 这里就非常关键了,记住你这里有了namespace的值
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
// 这里refresh的过程就和上面初始化父容器的流程一样了,会查找使用的那些xml配置文件
wac.refresh();
}
public String getNamespace() {
// 根据你配置的<servlet-name>xxx</servlet-name>名称拼接
// 默认就返回:xxx-servlet(xxx就是你配置的servlet名称)
return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}
public String getContextConfigLocation() {
return this.contextConfigLocation;
}
}
// 如果你没有配置contextConfigLocation,那么就找默认的配置xml文件
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
protected String[] getDefaultConfigLocations() {
// 上面看到了我们的getNamespace设置了值,则进入
if (getNamespace() != null) {
// 这里就拼接成:/WEB-INF/xxx-servlet.xml(也就是,如果你没有为DispatcherServlet配置contextConfigLocation属性)
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
} else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
}
总结:
- 读取Servlet配置参数
读取配置DispatcherServlet时配置的参数,通过BeanWrapper设置当前Servlet对应的属性中。 - 实例化Spring容器对象
1. 实例化Spring容器对象
2. 设置容器对象的配置文件
如果你为Servlet配置了contextConfigLocation,则使用该参数对应的值作为子容器解 析的xml配置文件
3. 设置名称空间namespace
根据你配置的servlet名称 + '-servlet'作为名称空间 - 刷新Spring容器
调用refresh方法;如果你没有配置contextConfigLocation,则会查找默认的配置文件,而这个默认配置在XmlWebApplicationContext已经重写了,会判断当前的namespace是否为空,不为空则返回/WEB-INF/xxx-servlet.xml (xxx: 取值根据你配置的servlet-name)。
完毕!!!
SpringBoot对Spring MVC都做了哪些事?(一)
SpringBoot对Spring MVC都做了哪些事?(二)
SpringBoot对Spring MVC都做了哪些事?(三)
SpringBoot对Spring MVC都做了哪些事?(四)
Spring中的@Configuration注解你真的了解吗?
Spring MVC 异常处理方式
Spring中字段格式化的使用详解
Spring MVC 异步请求方式
Spring容器这些扩展点你都清楚了吗?
Spring 自定义Advisor以编程的方式实现AOP
SpringBoot WebFlux整合Spring Security进行权限认证
SpringBoot项目中应用Spring Batch批处理框架,处理大数据新方案
Spring Security权限控制系列(七)
相关推荐
- Linux内核网络设备驱动
-
1.接收数据包过程概述介绍数据包收包过程,有助于我们了解Linux内核网络设备在数据收包过程中的位置,下面从宏观的角度介绍数据包从被网卡接收到进入socket接收队列的整个过程:加载网卡驱动,初...
- 「技术」一文带你掌握Linux字符设备架构
-
一、Linux设备分类Linux系统为了管理方便,将设备分成三种基本类型:...
- 「技术干货」一文搞懂Linux内核调试方法(二)
-
上篇回顾:一文Linux内核调试方法(一)...
- Pytorch学习Day 5: 神经网络基础(nn.Module)学习课程
-
学习目标理解PyTorch中nn.Module类的作用和核心功能。掌握如何使用nn.Linear构建简单的单层神经网络。学会访问和打印神经网络的参数。通过代码实践加深对PyTorch神...
- 内存问题探微
-
这篇文章是我在公司TechDay上分享的内容的文字实录版,本来不想写这么一篇冗长的文章,因为有不少的同学问是否能写一篇相关的文字版,本来没有的也就有了。说起来这是我第二次在TechDay上做的...
- 阿里架构师的5年经验总结:盘点数据仓库常用的4大数据工具平台
-
数据仓库是解决方案,真正落地的时候,还要依托于工具平台。...
- 软考系统架构师2021_备考说明---软考高级之系统架构师_备考笔记
-
第一轮一个半月.这些是考试内容.这个通过率全国,只有百分之10左右..挺难的....
- 从 Java 程序员到架构师:技术进阶与能力跃迁的完整路径(深度版)
-
#程序员如何进阶为架构师?#从Java程序员到架构师:技术进阶与能力跃迁的完整路径(深度版)...
- 大厂架构师被A1逼疯:我20年经验不如A1五分钟生成的方案
-
程序员末日?AI竟包办八成代码!前端真的凉了?...
- 立志成为架构师的你请收下——架构设计的三种思维
-
软件架构的几个误区1.架构的目标即灵活性灵活性越好的架构越能适应未来变化的需要,但不是架构设计的目标,一味追求容易陷入另外一个坑,造成性能的损失和资源的浪费。2.一套成熟的开源框架就是架构框...
- 系统架构师之——软件开发方法
-
不管你是开发人员,还是互联网行业人员,基本上经常看到各种各样的软件相关的图,如什么架构图什么设计图什么模式图甘特图等。很多时候总是傻傻分不清。对此,我们很有必要对系统开发基础知识有认知。对于一名程序员...
- 系统分析师和系统架构师的区别是什么?
-
软考高级包括系统分析师、信息系统项目管理师、网络规划设计师、系统架构设计师和系统规划与管理师,其中,系统分析师和系统架构师这两个科目是大家很容易搞混淆的,因为都属于软考,所以他们的报考时间、报考条件都...
- 软件开发 “四高”的详细分析——高扩展
-
高并发架构的黄金法则:用“分治异步”玩转每秒10万+请求弹性架构:像搭乐高一样扩展你的系统杰夫·贝索斯曾说:“架构不是设计出来的,而是演化出来的。”高并发系统的核心在于...
- 复习七天通过软考高级系统架构师
-
前言软考复习的方式可以分为两种:报班和自学。首先晒一下成绩,开心的一批,虽然考的不是很好!!每科满分75,需要同时都>=45分才算合格。...
- 软件开发 “四高”的详细分析,即高并发、高性能、高扩展、高可用
-
高并发(HighConcurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。高并发相关常用的一些指标有响应时间(ResponseT...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)