Spring项目通过JavaConfig配置web.xml的实现:ServletContainerInitializer

Servlet 3.0以上支持JavaConfig配置web.xml

1、web容器启动

2、web容器调用javax.servlet.ServletContainerInitializer接口下的onStartup方法

package javax.servlet;

import java.util.Set;

public interface ServletContainerInitializer {

    public void onStartup(Set<Class<?>> c, ServletContext ctx)
        throws ServletException; 
}

官方对该接口的解释如下:

An instance of the ServletContainerInitializer is looked up via the jar
services API by the container at container / application startup time. The framework
providing an implementation of the ServletContainerInitializer MUST
bundle in the META-INF/services directory of the jar file a file called
javax.servlet.ServletContainerInitializer, as per the jar services API,
that points to the implementation class of the ServletContainerInitializer.

大意为:

为了支持不使用web.xml,

web容器在启动时会扫描所有ServletContainerInitializer的实现,并调用其onStartup方法.

扫描的方法为在所有jar包下查找路径为META-INF/services/ServletContainerInitializer文件,根据文件内的全路径进行实例化.

org.springframework.web下就有这样一个文件:

通过这个路径,我们就能找到ServletContainerInitializer的实现类:

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;

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

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

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

        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) 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;
        }

        AnnotationAwareOrderComparator.sort(initializers);
        servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);

        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }

}

内容挺简单的,就是遍历一个Class集合,如果是WebApplicationInitializer的子类且不是接口或抽象类就实例化。

如果没有一个实例化成功的话,就会输出"org.apache.catalina.core.ApplicationContext.log No Spring WebApplicationInitializer types detected on classpath",

否则就挨个调用实例的onStartup方法.

那么明明参数1的泛型并没有指定某个类,这个WebApplicationInitializer是哪来的呢?

文档内还有一段也很重要:

In addition to the ServletContainerInitializer we also have an annotation -
HandlesTypes. The HandlesTypes annotation on the implementation of the
ServletContainerInitializer is used to express interest in classes that may
have anotations (type, method or field level annotations) specified in the value of the
HandlesTypes or if it extends / implements one those classes anywhere in the
class’ super types. The container uses the HandlesTypes annotation to determine
when to invoke the initializer's onStartup method. 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 class loading problems if one or more of the application's optional JAR
files are missing. Since the container is not in a position to decide whether these
types of class loading 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.

大意为:

可以是用@HadnlesTypes注解来注入某个类.

这个类及其子类的class将被web容器自动扫描并传入webAppInitializerClasses这个参数上。

3、通过这一流程,最终我们自己配置的WebApplicationInitializer就被调用了:

import org.springframework.web.WebApplicationInitializer;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

/**
 * @author ParanoidCAT
 * @since JDK 1.8
 */
public class MyWebApplicationInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        // TODO 这里写实现内容
    }
}

4、通过源码我们也能知道,如果没有通过JavaConfig配置web.xml,context的log会输出"org.apache.catalina.core.ApplicationContext.log No Spring WebApplicationInitializer types detected on classpath",然后去读取web.xml

猜你喜欢

转载自www.cnblogs.com/paranoidCAT/p/10414449.html
今日推荐