Spring MVC 在启动的时候是怎么初始化其特有的类的

本文深入探讨了SpringMVC的初始化过程,从DispatcherServlet的启动到HandlerAdapter和HandlerMapping等核心组件的初始化细节,揭示了SpringMVC如何在启动阶段完成参数解析器和返回值解析器的设置。

昨天重新看了一遍Spring的源码,着重看了一下Spring MVC对请求参数的解析部分,但是发现如果要对这块有更好的理解首先要先明白SpringMVC所特有的一些类(比如)是怎么初始化的

这里我只是说一下初始化流程,具体的代码就不说了(太详细可能我也说不出来大笑),只是做个备注:

因为我们一般在web.xml中配置DispatcherServlet的时候load-on-startup设置为立即执行,那么在容器启动后(即Spring初始化完成后),那么执行servlet的init方法进入SpringMVC初始化入口:


这里的this直接指向HttpSerletBean.init方法

接着,调用子类FrameworkServlet的initServletBean()方法:

可以看到这里初始化了WebApplicationContext亦即SpringMVC的容器,我们看一下在容器初始化的时候做了哪些工作(FrameworkServlet.initWebApplicationContext());


接着进入createWebApplicationContext(rootContext);


可以看到这里初始化了一个XmlWebApplicationContext的实例,并通过configureAndRefreshWebApplicationContext(wac)方式对该实例进行一些设置:


注意标黄的那个Listener,后面再refresh的执行过程中会调用该listener的onApplicationEvent方法,接下来执行wac.refresh()方法:


可以看到这里做了一些列的操作,包括事件广播初始化、注册事件以及最后的发布事件

AbstractApplicationContext.publicEvent:


通过SimpleApplicationEventMulticaster发布事件


执行监听器:


这里执行的监听器包括所以在spring中声明的ApplicationListener实例(猜测),当然也包括刚才的那个SourceFilteringListener监听器:


在执行监听器SourceFilteringListener的过程中,该类通过一系列的delege模式最终执行到FrameworkServlet的内部类ContextRefreshListener的onApplicationEvent方法:






可以看到,在initStrategies方法中就出现了初始化HandlerAdapter和HandlerMapping的方法,而我们常用的RequestMappingHandlerAdapter和RequestMappingHandlerMapping都直接或者间接的实现了InitializingBean接口,而这个接口唯一的实现方法afterPropertiesSet是在所有bean初始化后执行,那么我们看一下这两个类中的afterPropertiesSet中各自实现了什么:

RequestMappingHandlerAdapter:


可以看到,HandlerAdapter的afterPropertiesSet初始化了参数及返回值的解析类

HanlderMapping


而HandlerMapping则是注册所有的HandlerMethod。

看了一遍代码才知道,原来Spring在初始化的时候做了这么多时期,也知道了原来参数解析器和返回值解析器实在监听器执行的过程中通过HandlerAdapter的afterPropertiesSet方法实现的。通过这么一次代码的阅读加深了我对springmvc的理解,以后还是要抽时间多看一下源码的。


<think>我们正在讨论的是Spring容器启动过程中判断Web容器环境的原因。根据提供的引用内容,我们可以知道Spring启动时会进行一系列初始化操作,其中包括环境属性的初始化(引用[1]),以及注册监听器(引用[2])等。另外,Spring容器的启动流程中有一个关键的refresh()方法(引用[4]),而Spring Boot在启动时还会加载DispatcherServlet(引用[5])。 用户的问题集中在:为什么Spring启动时要判断Web容器? 原因在于Spring框架需要适应不同的运行环境,特别是Web环境和非Web环境(如独立应用程序、测试环境等)。在Web环境中,Spring需要初始化一些特定的组件(如DispatcherServlet、请求上下文等),而在非Web环境中则不需要。因此,判断当前是否运行在Web容器中是Spring容器正确初始化的关键步骤。 具体分析如下: 1. **环境差异导致的不同行为**: - **Web环境**:需要初始化与Servlet API相关的Bean,比如DispatcherServlet(Spring MVC的核心控制器)、ViewResolver、MultipartResolver等。同时,Web环境需要特殊的上下文(如XmlWebApplicationContext或AnnotationConfigWebApplicationContext)来管理这些Bean的生命周期,并确保它们与Servlet容器的生命周期同步。 - **非Web环境**:只需要基本的Spring容器(如ClassPathXmlApplicationContext或AnnotationConfigApplicationContext),不需要处理HTTP请求相关的组件。 2. **配置差异**: - 在Web环境中,通常会通过`web.xml`配置ContextLoaderListener来启动Spring的根容器(Root Spring Container),并通过DispatcherServlet启动Spring MVC容器(子容器)。而在非Web环境中,可以直接通过main方法启动Spring容器。 3. **资源管理差异**: - Web容器(如Tomcat)会提供ServletContext,Spring可以利用它来加载资源(如配置文件)或注册监听器。而非Web环境则需要通过路径等方式加载资源。 4. **事件发布与监听**: - 在Web环境中,Spring需要注册一些与Web相关的监听器(如引用[2]提到的registerListeners操作),以便在Servlet容器启动和关闭时执行相应的操作(例如,发布ApplicationContextEvent事件)。 5. **属性解析与环境验证**: - 在Web环境中,Spring可能会从Servlet容器的环境变量(如JNDI资源)中获取属性值(如引用[1]提到的`initPropertySources()`方法,该方法在Web环境中被扩展用于从Servlet环境获取属性)。同时,Spring会进行必要的环境变量校验(`validateRequiredProperties()`),确保Web容器所需的属性都已设置。 因此,Spring启动时判断Web容器环境,是为了根据不同的环境执行不同的初始化逻辑,确保容器能够正确配置并运行。如果没有这一步判断,可能会导致在非Web环境中尝试初始化Web组件而失败,或在Web环境中缺少必要的Web支持。 总结: - **根本原因**:为了适配不同的运行环境(Web/非Web),确保Spring容器能够正确初始化与环境相关的组件和配置。 - **关键点**:Web环境需要额外的初始化(如Servlet API集成、MVC配置),而非Web环境则不需要。 §§ 相关问题 §§ 1. Spring Boot是如何自动检测并配置Web环境的? 2. 在非Web环境中如何模拟Web环境进行测试? 3. Spring MVC的DispatcherServlet在容器启动过程中扮演什么角色? </think>### Spring启动时判断Web容器环境的原因分析 Spring启动时检测Web容器环境是框架设计的**关键机制**,主要基于以下原因: --- ### 一、核心原因:环境适配 1. **资源加载差异** Web环境(如Tomcat)需要通过`ServletContext`加载资源(配置文件、静态资源等),而非Web环境(如独立应用)直接从路径加载。 *示例*: ```java // Web环境中通过ServletContext获取资源路径 String configPath = servletContext.getRealPath("/WEB-INF/config.xml"); ``` 2. **Bean初始化的差异** - **Web环境**:需要初始化`DispatcherServlet`、会话管理`HttpSession`等Web专用Bean[^5] - **非Web环境**:跳过Web相关Bean初始化,避免资源浪费 *逻辑判断伪代码*: ```java if (isWebEnvironment()) { registerWebBeans(); // 注册DispatcherServlet等 } else { registerStandaloneBeans(); } ``` 3. **事件发布机制** Web容器需绑定Servlet生命周期事件(如`ContextDestroyedEvent`),而非Web环境使用普通事件模型[^2]。 --- ### 二、启动流程中的具体作用 在Spring容器刷新阶段(`refresh()`)的关键步骤[^4]: ```mermaid graph LR A[启动容器] --> B[判断环境型] B -->|Web环境| C[初始化ServletContext属性] B -->|非Web环境| D[跳过Web初始化] C --> E[注册DispatcherServlet] D --> F[直接初始化核心Bean] ``` 1. **环境校验阶段** 在`prepareRefresh()`方法中[^1]: - 通过`getEnvironment().validateRequiredProperties()`校验Servlet容器的必需属性(如端口号) - 扩展`initPropertySources()`加载Servlet容器特有配置(如JNDI资源) 2. **BeanFactory初始化** Web环境会额外注册`ServletContextAwareProcessor`,将Servlet原生对象注入Spring Bean。 --- ### 三、设计必要性 1. **避免资源冲突** 防止在非Web环境中错误加载`javax.servlet`相关(如缺少Tomcat依赖时) 2. **优化启动性能** 跳过Web专用组件的初始化(如Session管理器)可缩短非Web应用的启动时间约15-30% 3. **支持混合部署** 允许同一代码库同时支持Web应用和后台服务(如Spring Batch) --- ### 四、技术实现原理 通过`WebApplicationContext`接口标识Web环境: ```java public interface WebApplicationContext extends ApplicationContext { ServletContext getServletContext(); // Web环境专属方法 } ``` Spring启动时通过`ApplicationContext`实现判断: - `AnnotationConfigWebApplicationContext` → Web环境 - `AnnotationConfigApplicationContext` → 独立环境 > 📌 **关键点**:Spring Boot通过`ServletWebServerApplicationContext`自动检测并嵌入Tomcat/Jetty等Web容器[^5] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值