Spring MVC项目的启动

本文介绍了Spring MVC项目在没有web.xml文件的情况下如何启动。在JavaEE中,ServletContainerInitializer接口在web应用启动时用于动态注册servlets、filters和listener。实现该接口的类必须在JAR文件的META-INF/services目录下声明,Tomcat通过该机制找到并执行onStartup方法,从而启动Spring MVC。

Spring MVC项目启动过程

注:如无特别说明,本系列文章使用的Spring框架版本均为5.0.4.RELEASE

在以前的web项目中,通常会有一个web.xml文件,部署在WEB-INF目录下,诸如Tomcat之类的Servlet容器会通过读取web.xml文件来启动项目,从而初始化配置在web.xml中的一些类,如Spring MVC中的DispatcherServlet类。但是,现在的项目往往没有这个web.xml文件,那么Tomcat是如何知道要去初始化Spring MVC中的相关类呢?
首先,我们来看一个JavaEE中的ServletContainerInitializer接口.

javax.servlet
Interface ServletContainerInitializer

Interface which allows a library/runtime to be notified of a web application’s startup phase and perform any required programmatic registration of servlets, filters, and listeners in response to it.

Implementations of this interface may be annotated with HandlesTypes, in order to receive (at their onStartup(java.util.Set>, javax.servlet.ServletContext) method) the Set of application classes that implement, extend, or have been annotated with the class types specified by the annotation.

If an implementation of this interface does not use this annotation, or none of the application classes match the ones specified by the annotation, the container must pass a null Set of classes to onStartup(java.util.Set>, javax.servlet.ServletContext).

When examining the classes of an application to see if they match any of the criteria specified by the HandlesTypes annotation of a ServletContainerInitializer, the container may run into classloading problems if any of the application’s optional JAR files are missing. Because the container is not in a position to decide whether these types of classloading failures will prevent the application from working correctly, it must ignore them, while at the same time providing a configuration option that would log them.

Implementations of this interface must be declared by a JAR file resource located inside the META-INF/services directory and named for the fully qualified class name of this interface, and will be discovered using the runtime’s service provider lookup mechanism or a container specific mechanism that is semantically equivalent to it. In either case, ServletContainerInitializer services from web fragment JAR files excluded from an absolute ordering must be ignored, and the order in which these services are discovered must follow the application’s classloading delegation model.

上面这段文字的大意就是说,ServletContainerInitializer接口用来给web应用在启动时动态注册servlets,filters和listener。这个接口的实现必须要在被jar包声明,这个声明文件要放在META-INF/services目录下,并且这个文件的名称要叫做javax.servlet.ServletContainerInitializer,其中这个文件内容写着这个接口的实现类的限定名,如Spring MVC中这个接口的实现类就是org.springframework.web.SpringServletContainerInitializer
spring mvc图片

接下来我们再去看一下这个SpringServletContainerInitializer类。(注:为使下类简洁,已去掉部分注释)

package org.springframework.web;

import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;


@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

    @Override
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {

        List<WebApplicationInitializer> initializers = new LinkedList<>();

        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                // Be defensive: Some servlet containers provide us with invalid classes,
                // no matter what @HandlesTypes says...
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)
                                ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }

        servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
        AnnotationAwareOrderComparator.sort(initializers);
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }

}

SpringServletContainerInitializer类实现了ServletContainerInitializer接口,在web启动时,容器将会执行onStartup方法,并且将实现了WebApplicationInitializer接口的类(由HandlesTypes注解指定)通过webAppInitializerClasses参数传递给onStartup方法。就这样,onStartup方法的执行带动了整个Spring MVC。

参考文献:

https://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html

https://stackoverflow.com/questions/32550131/how-does-tomcat-exactly-bootstrap-the-app-without-web-xml

<think>首先,用户的问题是关于Spring MVC项目启动方法。根据系统指令,我需要使用中文回答,并确保LaTeX语法正确。用户提供了站内引用,我应该参考这些引用。 回顾用户提供的引用: - 引用[1]:描述了Spring MVC启动的关键点,涉及SpringServletContainerInitializer类和onStartup方法。 - 引用[2]:是关于Spring MVC启动流程的问题,并展示了一个XML配置示例。 用户的问题是:“我想查找spring mvc项目启动方法 请问spring mvc项目启动方法”,所以核心是解释Spring MVC项目启动方法。 我的回答需要结构清晰,帮助用户逐步解决问题。参考系统指令的示例,我应该先给出解释,然后可能包括代码示例(如果需要),最后生成相关问题。 关键点从引用中提取: - 从引用[1]:Web启动时,容器执行SpringServletContainerInitializer的onStartup方法,它会处理所有实现了WebApplicationInitializer接口的类。这是Spring MVC启动的核心机制。 - 从引用[2]:包括一个XML配置片段,展示了注解驱动和组件扫描,这有助于配置Spring MVC。 在Spring MVC中,启动方法通常指的是Web应用的初始化和Spring容器的启动。这涉及到Servlet规范中的ServletContainerInitializer机制。 步骤总结: 1. **Web容器启动**:当Web应用部署到Servlet容器(如Tomcat)时,容器会查找所有jar包中的META-INF/services/javax.servlet.ServletContainerInitializer文件。 2. **Spring的ServletContainerInitializer**:Spring提供了一个实现org.springframework.web.SpringServletContainerInitializer,它被HandlesTypes注解指定为处理WebApplicationInitializer接口的类。 3. **onStartup方法**:容器调用SpringServletContainerInitializer的onStartup方法,传入一组实现了WebApplicationInitializer的类。 4. **WebApplicationInitializer**:用户可以实现WebApplicationInitializer接口来配置Spring MVC。例如,使用AbstractAnnotationConfigDispatcherServletInitializer来简化配置。 5. **XML配置**:或者,使用web.xml文件来配置DispatcherServlet和其他组件,但现代Spring应用倾向于使用Java配置。 从引用[2]的XML配置,它展示了Spring MVC的XML配置,这可能用于web.xml中。 所以,在回答中,我需要覆盖: - 启动过程的核心机制。 - 如何配置(Java配置或XML配置)。 - 提供代码示例来演示。 确保使用中文回答。 结构: 1. **解释启动方法**:简述Spring MVC如何启动。 2. **详细步骤**:分步骤描述启动流程。 3. **代码示例**:给出一个简单的Java配置示例或XML配置示例。 4. **总结**。 最后,生成相关问题,如系统指令要求。 生成相关问题: - 类似于示例中的问题,针对Spring MVC启动。 例如: 1. Spring MVC启动流程中的关键角色是什么? 2. 如何使用Java配置启动Spring MVC项目? 3. Spring MVC的XML配置有哪些常见元素? 引用标识:在回答中,如果引用用户提供的引用,末尾添加引用标识如[^1]。 用户提供了引用[1]和引用[2],所以我需要在相关部分添加它们。 代码块:如果需要代码示例,使用格式如示例中的Python代码块。 对于Spring MVC,可能是一个Java代码块或XML配置块。 现在,构建回答。 </think>以下是关于 Spring MVC 项目启动方法的详细解释。Spring MVC(Model-View-Controller)是一个基于 Spring Framework 的 Web 框架,其启动过程依赖于 Servlet 容器的初始化和 Spring 容器的配置。启动方法主要通过 Java 配置或 XML 配置实现,核心机制涉及 Servlet 规范中的 `ServletContainerInitializer` 接口和 Spring 的实现类。我将逐步解释启动流程,并提供代码示例参考。 ### Spring MVC 项目启动方法的核心机制 Spring MVC启动由 Servlet 容器(如 Tomcat)触发,具体步骤如下: 1. **容器初始化**:当 Web 应用部署时,Servlet 容器扫描所有 JAR 包中的 `META-INF/services/javax.servlet.ServletContainerInitializer` 文件。 2. **SpringServletContainerInitializer 的作用**:Spring 提供了 `org.springframework.web.SpringServletContainerInitializer` 类实现 `ServletContainerInitializer` 接口。容器会调用其 `onStartup()` 方法,并通过 `@HandlesTypes(WebApplicationInitializer.class)` 注解传递所有实现了 `WebApplicationInitializer` 接口的类作为参数[^1]。 3. **WebApplicationInitializer 的调用**:在 `onStartup()` 方法中,Spring 遍历并初始化每个 `WebApplicationInitializer` 实现类。这负责配置 Spring MVC 的核心组件,如 `DispatcherServlet` 和 Spring 应用上下文。 4. **Spring 容器的创建**:`DispatcherServlet` 被初始化,它会加载 Spring 配置文件(XML 或 Java 配置),创建根应用上下文(例如 `AnnotationConfigWebApplicationContext`),并注册控制器、拦截器等组件。 启动方法的关键在于实现 `WebApplicationInitializer` 接口(Java 配置)或配置 `web.xml`(XML 配置)。现代开发推荐 Java 配置,因为它更简洁灵活。 ### 配置 Spring MVC 启动的代码示例 下面提供两种常见配置方法:Java 配置和 XML 配置。您可以根据项目需求选择。 #### 方法 1: Java 配置(推荐) 使用 Java 类实现 `WebApplicationInitializer` 接口,无需 `web.xml` 文件。例如,创建一个 `AppInitializer` 类: ```java // 导入必要的 Spring 包 import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.ServletContext; import javax.servlet.ServletRegistration; public class AppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { // 创建 Annotation 配置的 Web 应用上下文 AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(AppConfig.class); // 注册配置类 // 创建并注册 DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(context); ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", servlet); registration.setLoadOnStartup(1); registration.addMapping("/"); // 映射所有请求 } } ``` 同时,定义一个 Spring 配置类 `AppConfig`(使用 `@Configuration`): ```java import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ComponentScan; @Configuration @ComponentScan(basePackages = "com.example.controller") // 扫描控制器包 public class AppConfig { // 可添加其他配置,如视图解析器 } ``` 在这个示例中: - `AppInitializer` 实现了 `WebApplicationInitializer`,在容器启动时自动调用 `onStartup()` 方法。 - `AppConfig` 使用注解驱动配置,避免了 XML 文件[^2]。 #### 方法 2: XML 配置(传统方式) 如果使用 `web.xml` 文件,需要在项目中添加以下内容: 1. **web.xml 配置**(在 `src/main/webapp/WEB-INF/web.xml` 中): ```xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置 DispatcherServlet --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-mvc-config.xml</param-value> <!-- 指向 Spring MVC 配置文件 --> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app> ``` 2. **Spring MVC 配置文件**(例如 `src/main/webapp/WEB-INF/spring-mvc-config.xml`),参考用户提供的引用[^2]: ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 扫描控制器包 --> <context:component-scan base-package="com.example.controller"/> <!-- 启用注解驱动 --> <mvc:annotation-driven/> </beans> ``` 在这个 XML 配置中: - `<context:component-scan>` 自动扫描并注册控制器组件。 - `<mvc:annotation-driven>` 启用 Spring MVC 的注解支持,如 `@RequestMapping`[^2]。 ### 启动流程的数学建模(可选理解) Spring MVC启动过程可以抽象为时序序列问题。假设容器初始化时间为 $t_0$,`onStartup()` 执行时间为 $t_1$,DispatcherServlet 加载时间为 $t_2$,则整体启动延迟 $\Delta t$ 满足: $$ \Delta t = t_2 - t_0 \quad \text{其中} \quad t_1 \leq t_2 $$ 优化启动性能的目标是减少 $\Delta t$,例如通过异步初始化或懒加载机制。 ### 总结 Spring MVC 项目启动方法主要通过实现 `WebApplicationInitializer`(Java 配置)或配置 `web.xml`(XML 配置)来完成。核心是 `SpringServletContainerInitializer` 的 `onStartup()` 方法驱动整个流程[^1]。推荐使用 Java 配置,因为它更现代、易于维护。确保您的项目依赖正确的 Spring Web MVC 库(如通过 Maven 添加 `spring-webmvc`)。 如果您有具体的项目环境(如 Tomcat 或 Spring Boot),可以提供更多细节,我可以给出更针对性的建议。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值