Spring MVC源码——Root WebApplicationContext

Spring MVC源码——Root WebApplicationContext

  打算开始读一些框架的源码,先拿 Spring MVC 练练手,欢迎点击这里访问我的源码注释, SpringMVC官方文档一开始就给出了这样的两段示例:

WebApplicationInitializer示例:

public class MyWebApplicationInitializer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletCxt) {// Load Spring web application configurationAnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();ac.register(AppConfig.class);ac.refresh();// Create and register the DispatcherServletDispatcherServlet servlet = new DispatcherServlet(ac);ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);registration.setLoadOnStartup(1);registration.addMapping("/app/*");}
}

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 中的实例来看一下 Spring MVC 初始化过程.

上下文层次结构

  Spring MVC 的上下文有如下这样的层级:

  

  图中的 Servlet WebApplicationContext 是与 DispatcherServlet 绑定的上下文, 其中还有 controllers、ViewResolver、HandlerMapping 等组件.

Root WebApplicationContext 不是必须的上下文, 在需要时,可以用来在多个 DispatcherServlet 间共享一些 bean.

Root WebApplicationContext 初始化和销毁

  ContextLoaderListener

  web.xml 中配置的 ContextLoaderListener 用于启动和终止 Spring 的 root WebApplicationContext.

  

   ContextLoaderListener

 

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {public ContextLoaderListener() {}public ContextLoaderListener(WebApplicationContext context) {super(context);}/*** Initialize the root web application context.* Servlet 上下文初始化,调用父类的方法初始化 WebApplicationContext*/@Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext());}/*** Close the root web application context.* Servlet 上下文被销毁,调用父类的方法销毁 WebApplicationContext*/@Overridepublic void contextDestroyed(ServletContextEvent event) {closeWebApplicationContext(event.getServletContext());// 销毁 ServletContext 上实现了 DisposableBean 的属性并移除他们
        ContextCleanupListener.cleanupAttributes(event.getServletContext());}
}

ContextLoaderListener 直接调用了父类 ContextLoader 的方法来初始化和销毁上下文.

ContextLoaderListener 在创建上下文时,会尝试读取 contextClass context-param 来指定上下文的类型,被指定的类需要实现 ConfigurableWebApplicationContext 接口. 如果没有获取到,默认会使用 WebApplicationContext.

初始化上下文时,会尝试读取 contextConfigLocation context-param, 作为 xml 文件的路径.

  初始化上下文

initWebApplicationContext() 方法如下:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {// 检查有没有绑定上下文if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " +"check whether you have multiple ContextLoader* definitions in your web.xml!");}servletContext.log("Initializing Spring root WebApplicationContext");Log logger = LogFactory.getLog(ContextLoader.class);if (logger.isInfoEnabled()) {logger.info("Root WebApplicationContext: initialization started");}// 初始化开始时间long startTime = System.currentTimeMillis();try {// Store context in local instance variable, to guarantee that// it is available on ServletContext shutdown.// 保存上下文到本地实例变量中,保证上细纹能在 ServletContext 关闭时访问到if (this.context == null) {// 创建上下文this.context = createWebApplicationContext(servletContext);}if (this.context instanceof ConfigurableWebApplicationContext) {// 如果上下文实现了 ConfigurableWebApplicationContextConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;if (!cwac.isActive()) {// 上下文还没有刷新,设置 父上下文(如果能找到),并且刷新// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {// The context instance was injected without an explicit parent ->// determine parent for root web application context, if any.ApplicationContext parent = loadParentContext(servletContext);cwac.setParent(parent);}// 设置和刷新上下文
                configureAndRefreshWebApplicationContext(cwac, servletContext);}}// 将上下文绑定到 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) {// 如果有线程上下文类加载器,而且不是 ContextLoader 本身的类加载器,放入到 currentContextPerThread 中。这是一个 static 的域currentContextPerThread.put(ccl, this.context);}if (logger.isInfoEnabled()) {long elapsedTime = System.currentTimeMillis() - startTime;logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");}return this.context;}catch (RuntimeException | Error ex) {// 发生异常, 把异常绑定到上下文对应的属性上,之后不会再进行初始化logger.error("Context initialization failed", ex);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);throw ex;}
}

initWebApplicationContext() 方法调用了 createWebApplicationContext() 方法来创建上下文;调用了 configureAndRefreshWebApplicationContext() 来对实现了 ConfigurableWebApplicationContext 接口的上下文做初始化.

createWebApplicationContext() 会调用 determineContextClass() 来获取上下文类型的 Class 对象.

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {// 获取上下文类型Class<?> contextClass = determineContextClass(sc);// 检查是否实现了 ConfigurableWebApplicationContextif (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Custom context class [" + contextClass.getName() +"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");}// 实例化return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}protected Class<?> determineContextClass(ServletContext servletContext) {// 获取 serveltContext 的 '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 {// 去默认策略里获取默认的上下文类型名称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);}}
}

configureAndRefreshWebApplicationContext() 方法.

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {// 用可以获取到的信息,获取一个更有意义的上下文if (ObjectUtils.identityToString(wac).equals(wac.getId())) {// The application context id is still set to its original default value// -> assign a more useful id based on available information// 获取 ServletContext 的 'contextId' 初始化参数。String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);if (idParam != null) {wac.setId(idParam);}else {// Generate default id...// 生成默认 idwac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(sc.getContextPath()));}}// 设置 servletContext 属性
    wac.setServletContext(sc);// 设置配置文件路径String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);}// The wac environment's #initPropertySources will be called in any case when the context// is refreshed; do it eagerly here to ensure servlet property sources are in place for// use in any post-processing or initialization that occurs below prior to #refresh// 初始化属性源, 确保 servlet 属性源到位并能够在任何 refresh 之前的后期处理和初始化中使用ConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(sc, null);}// 在设置了配置文件之后上下文刷新之前,自定义上下文
    customizeContext(sc, wac);wac.refresh();
}protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {// 根据 ServletContext 的 'contextInitializerClasses' 和 'globalInitializerClasses' 初始化参数 加载 ApplicationContextInitializer 的 classList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =determineContextInitializerClasses(sc);for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {// 获取范型参数类型Class<?> initializerContextClass =GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);// 检查 Initializer 是否适用于当前上下文对象if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {throw new ApplicationContextException(String.format("Could not apply context initializer [%s] since its generic parameter [%s] " +"is not assignable from the type of application context used by this " +"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),wac.getClass().getName()));}// 创建 Initializer 实例,并添加到 contextInitializersthis.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));}// 根据 org.springframework.core.Ordered 和 org.springframework.core.annotation.Order 排序,如果没有实现或注解,会被排到最后AnnotationAwareOrderComparator.sort(this.contextInitializers);// 执行每个 initializer 的 initialize() 方法for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {initializer.initialize(wac);}
}

  销毁上下文

closeWebApplicationContext() 方法如下:

public void closeWebApplicationContext(ServletContext servletContext) {servletContext.log("Closing Spring root WebApplicationContext");try {// 如果 context 是 ConfigurableWebApplicationContext 调用 close() 方法if (this.context instanceof ConfigurableWebApplicationContext) {((ConfigurableWebApplicationContext) this.context).close();}}finally {ClassLoader ccl = Thread.currentThread().getContextClassLoader();if (ccl == ContextLoader.class.getClassLoader()) {currentContext = null;}else if (ccl != null) {currentContextPerThread.remove(ccl);}// 移除 servletContext 中的 context 属性
        servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);}
}

Servlet 3.0+ 中初始化

Servlet 3.0+ 中可以通过 ServletContext 的 addlistener() 方法来添加监听器.因此可以先把 Spring 容器先创建好,再传给 ContextLoaderListener 的构造器.这里就不自己写例子了,选了单元测试中的 ContextLoaderTests.testContextLoaderListenerWithDefaultContext() 方法:

public void testContextLoaderListenerWithDefaultContext() {MockServletContext sc = new MockServletContext("");sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,"/org/springframework/web/context/WEB-INF/applicationContext.xml " +"/org/springframework/web/context/WEB-INF/context-addition.xml");ServletContextListener listener = new ContextLoaderListener();ServletContextEvent event = new ServletContextEvent(sc);listener.contextInitialized(event);String contextAttr = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;WebApplicationContext context = (WebApplicationContext) sc.getAttribute(contextAttr);assertTrue("Correct WebApplicationContext exposed in ServletContext", context instanceof XmlWebApplicationContext);assertTrue(WebApplicationContextUtils.getRequiredWebApplicationContext(sc) instanceof XmlWebApplicationContext);LifecycleBean lb = (LifecycleBean) context.getBean("lifecycle");assertTrue("Has father", context.containsBean("father"));assertTrue("Has rod", context.containsBean("rod"));assertTrue("Has kerry", context.containsBean("kerry"));assertTrue("Not destroyed", !lb.isDestroyed());assertFalse(context.containsBean("beans1.bean1"));assertFalse(context.containsBean("beans1.bean2"));listener.contextDestroyed(event);assertTrue("Destroyed", lb.isDestroyed());assertNull(sc.getAttribute(contextAttr));assertNull(WebApplicationContextUtils.getWebApplicationContext(sc));
}

 

 转自:https://www.cnblogs.com/FJH1994/p/10798028.html

转载于:https://www.cnblogs.com/hujunzheng/p/10819473.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531196.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring MVC源码——Servlet WebApplicationContext

上一篇笔记(Spring MVC源码——Root WebApplicationContext)中记录了下 Root WebApplicationContext 的初始化代码.这一篇来看 Servlet WebApplicationContext 的初始化代码 DispatcherServlet 是另一个需要在 web.xml 中配置的类, Servlet WebApplicationContext 就由它来创建…

Springboot源码——应用程序上下文分析

前两篇(Spring MVC源码——Root WebApplicationContext 和 Spring MVC源码——Servlet WebApplicationContext)讲述了springmvc项目创建上下文的过程&#xff0c;这一篇带大家了解一下springboot项目创建上下文的过程。 SpringApplication引导类 SpringApplication类用于启动或…

基于zookeeper实现分布式配置中心(一)

最近在学习zookeeper&#xff0c;发现zk真的是一个优秀的中间件。在分布式环境下&#xff0c;可以高效解决数据管理问题。在学习的过程中&#xff0c;要深入zk的工作原理&#xff0c;并根据其特性做一些简单的分布式环境下数据管理工具。本文首先对zk的工作原理和相关概念做一下…

基于zookeeper实现分布式配置中心(二)

上一篇&#xff08;基于zookeeper实现分布式配置中心&#xff08;一&#xff09;&#xff09;讲述了zookeeper相关概念和工作原理。接下来根据zookeeper的特性&#xff0c;简单实现一个分布式配置中心。 配置中心的优势 1、各环境配置集中管理。 2、配置更改&#xff0c;实时推…

Redis分布式锁实战

背景 目前开发过程中&#xff0c;按照公司规范&#xff0c;需要依赖框架中的缓存组件。不得不说&#xff0c;做组件的大牛对CRUD操作的封装&#xff0c;连接池、缓存路由、缓存安全性的管控都处理的无可挑剔。但是有一个小问题&#xff0c;该组件没有对分布式锁做实现&#xff…

基于RobotFramework实现自动化测试

Java robotframework seleniumlibrary 使用Robot Framework Maven Plugin&#xff08;http://robotframework.org/MavenPlugin/&#xff09;执行自动化测试chromedriver下载&#xff1a; http://chromedriver.storage.googleapis.com/index.htmlchromedriver和chrome版本对应…

Springboot国际化信息(i18n)解析

国际化信息理解 国际化信息也称为本地化信息 。 Java 通过 java.util.Locale 类来表示本地化对象&#xff0c;它通过 “语言类型” 和 “国家/地区” 来创建一个确定的本地化对象 。举个例子吧&#xff0c;比如在发送一个具体的请求的时候&#xff0c;在header中设置一个键值对…

C语言一看就能上手的干货!你确定你不来看吗?

本地环境设置 如果您想要设置 C 语言环境&#xff0c;您需要确保电脑上有以下两款可用的软件&#xff0c;文本编辑器和 C 编译器。 文本编辑器 这将用于输入您的程序。文本编辑器包括 Windows Notepad、OS Edit command、Brief、Epsilon、EMACS 和 vim/vi。文本编辑器的名称…

10万码农五年的C语言笔记!你现在知道别人为什么这么优秀了吗?

c语言对许多同学来说确实是一门比较难学的课程&#xff0c;不仅抽象&#xff0c;而且繁琐&#xff0c;但这又是一门不得不学的课程。前两节可能还有兴致听一听&#xff0c;然而&#xff0c;再过几节课就是一脸蒙比。凭空要想出一道题的算法和程序&#xff0c;根本无从下手。 所…

C语言/C++编程学习:C语言环境设置!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

C语言指针原来也可以这么的通俗易懂!

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

C语言过时了?你在做梦?

为什么要使用C语言&#xff1f; 在过去的四十年里&#xff0c;C语言已经成为世界上最流行、最重要的一种编程语言。 C是一种融合了控制特性的现代语言&#xff0c;而我们已发现在计算机科学的理论和实践中&#xff0c;控制特性是很重要的。其设计使得用户可以自然地采用自顶向…

C语言深入理解!助你向大佬迈进!

Dennis Ritchie 过世了&#xff0c;他发明了C语言&#xff0c;一个影响深远并彻底改变世界的计算机语言。一门经历40多年的到今天还长盛不衰的语言&#xff0c;今天很多语言都受到C的影响&#xff0c;C&#xff0c;Java&#xff0c;C#&#xff0c;Perl&#xff0c; PHP&#xf…

【初涉C语言】程序员欢迎来到C语言的世界!

计算机发展史 机器语言所有的代码里面只有0和1优点&#xff1a;直接对硬件产生作用&#xff0c;程序的执行效率非常高缺点&#xff1a;指令又多又难记、可读性差、无可移植性汇编语言符号化的机器语言&#xff0c;用一个符号&#xff08;英文单词、数字&#xff09;来代表一条…

C语言和C++的区别整理详解!

c和c主要区别 根据书中的描述&#xff0c;进行了整理 推荐一个我自己的C/C交流裙815393895 1、 源代码文件的扩展名 摘自1.4.1 C实现源代码文件的扩展名UNIXC、cc、cxx、cGNU CC、cc、cxx、cpp、cDigital Marscpp、cxxBorland CcppWatcomcppMicrosoft Visual Ccpp、cxx、cc…

揭示C语言函数调用的本质解析

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得到…

C语言/C++编程学习:不找C/C++的工作也要学C/C++的原因

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 C和C的区别&#xff1a; C是一个结构化语言&#xff0c;它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程&#xff0c;对输入&#xff08;或环境条件&#xff09;进行运算处理得…

【网络攻防】精通C语言的黑客才是真正的黑客!

精通C语言的黑客才是真正的黑客 黑客界&#xff0c;有两样重要的课程&#xff0c;一是计算机的本质&#xff0c;二是编译原理。相对于汇编等底层语言&#xff0c;它简单&#xff1b;相对于其它高级语言&#xff0c;它更为接近计算机&#xff1b;同样它对黑客的两大课程很有帮助…

我两小时学完指针,你学会数组/指针与函数需要多久?

数组与函数&#xff1a; 这段函数中 函数的参数是数组&#xff0c;注意数组作为函数参数时&#xff0c;数组名和数组元素个数时分别传递的。 指针与函数&#xff1a; 这段函数中的参数是指针变量&#xff0c;传入的是数组的数组名或者首元素的地址&#xff0c;然后用引领操作…

C语言发展历史,C语言特点,C语言利于弊,入门须知三招

C语言是面向过程的&#xff0c;而C&#xff0b;&#xff0b;是面向对象的 这些是C/C能做的 服务器开发工程师、人工智能、云计算工程师、信息安全&#xff08;黑客反黑客&#xff09;、大数据 、数据平台、嵌入式工程师、流媒体服务器、数据控解、图像处理、音频视频开发工程…