spring学习(十二)--spring中WebApplicationInitializer解析

  上文中讲解了如何通过WebApplicationInitializer取代Web.xml进行spring容器的启动,WebApplicationInitializer是一个接口,通过实现WebApplicationInitializer,在其中可以添加servlet,listener等。在Web容器启动的时候,spring-web会通过SPI机制,加载这个接口的实现类,从而起到web.xml相同的作用。下面就看一下这个接口的详细内容:

public interface WebApplicationInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;

}

WebApplicationInitializer只有一个方法,比较简单,看不出什么头绪。在WebApplicationInitializer同级别有个SpringServletContainerInitializer类,我们来看下这个类的代码:

package org.springframework.web;

@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) {
       //这里指定了调用的类的方法,onStartup() initializer.onStartup(servletContext); } } }

SpringServletContainerInitializer这个类里也有个onStartup方法,看一下它的逻辑。首先判断webAppInitializerClasses这个set是否有值,如果有值,就找出里边不是接口、不是抽象类、实现了WebApplicationInitializer接口的类,保存到一个LinkedList中。

如果最终这个list为空,就返回空。如果最终list不为空,则按照一定顺序排序,将这些类实例化,调用其中的onStartup方法。

一旦我们实现了WebApplicationInitializer这个接口,spring-web就能扫描到WebApplicationInitializer的实现类,调用其中的onStartup方法,实现加载spring配置文件的目的。

但是,在web容器启动的时候,SpringServletContainerInitializer这个类又是怎么随着容器启动被调用呢?我们看一下SpringServletContainerInitializer这个类,它实现了ServletContainerInitializer接口,我们看一下ServletContainerInitializer接口代码:

public interface ServletContainerInitializer
{
    public abstract void onStartup(Set set, ServletContext servletcontext)
        throws ServletException;
}

ServletContainerInitializer这个类,是javax.servlet-api包下的类。

SpringServletContainerInitializer类中解释到,它实现了ServletContainerInitializer,通过SPI机制,当web容器启动时候,会到spring-web包的MATE-INF/services文件夹下扫描配置文件,在MATE-INF/services下有一个配置文件,文件名为ServletContainerInitializer接口全路径,文件内容为ServletContainerInitializer实现类的全路径,web容器扫描到该配置文件后,会将实现类实例化,我们看一下MATE-INF/services文件夹:

这样,web容器启动时,通过SPI机制,找到ServletContainerInitializer接口的实现类SpringServletContainerInitializer,将SpringServletContainerInitializer实例化,调用SpringServletContainerInitializer的onStartup方法,SpringServletContainerInitializer通过@HandlesTypes将WebApplicationInitializer注入进来,并调用WebApplicationInitializer的onStartup方法。至此,通过实现WebApplicationInitializer接口的onStartup方法,实现spring随着容器启动而被初始化。

个人理解,如有错误,欢迎指正!
原文地址:https://www.cnblogs.com/gllegolas/p/11731292.html