Spring MVC项目的启动

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值