Spring中对于WebApplicationInitializer的理解

导读:本篇文章讲解 Spring中对于WebApplicationInitializer的理解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1、前言

《SpringMVC学习(五)——零配置实现SpringMVC》这篇文章中我们没有使用Spring的配置实现了一个正常的SpringMVC的功能,里面核心的一个点就是使用了WebApplicationInitializer,那这篇文章就详细说明一下这个接口的作用。

2、WebApplicationInitializer的定义

从起初的Spring配置文件,到后来的Spring支持注解到后来的SpringBoot,Spring框架在一步步的使用注解的方式来去除Spring的配置的发展过程。WebApplicationInitializer就是取代web.xml配置的一个接口。

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

通过覆盖接口提供的onStartup方法我们可以往Servlet容器里面添加我们需要的servletlistener等,并且在Servlet容器启动的过程中就会加载这个接口的实现类,从而起到和web.xml相同的中作用,从而可以替代以前在web.xml中所做的配置。

3、实现原理

我们首先可以从Spring源码中找到SpringServletContainerInitializer实现类。

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

    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        LinkedList initializers = new LinkedList();
        Iterator var4;
        if(webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class initializer = (Class)var4.next();
                if(!initializer.isInterface() && !Modifier.isAbstract(initializer.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(initializer)) {
                    try {
                        initializers.add((WebApplicationInitializer)initializer.newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if(initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            var4 = initializers.iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer1 = (WebApplicationInitializer)var4.next();
                initializer1.onStartup(servletContext);
            }

        }
    }
}

这个类上面有@HandlesTypes({WebApplicationInitializer.class})这个注解,这个注解的作用是将其value中配置的一些类放入到ServletContainerInitializer

initializers.add((WebApplicationInitializer)initializer.newInstance());

最后通过循环去执行WebApplicationInitializer中的onStartup方法来实现里面的具体的具体的逻辑。

while(var4.hasNext()) {
	WebApplicationInitializer initializer1 = (WebApplicationInitializer)var4.next();
	initializer1.onStartup(servletContext);
}

那问题来了,Tomcat容器怎么知道先执行这个SpringServletContainerInitializer类?
这里涉及一个知识点SPI机制SPI全称为 Service Provider Interface,是一种服务发现机制。SPI机制是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI机制为我们的程序提供拓展功能。

Tomcat启动过程中查找所有的ServletContainerInitializer实现类然后添加到StandardContext的initializers集合中,然后执行里面的onStartup方法。

Spring中SPI就是通过SpringServletContainerInitializer类来实现的
sci机制
关于Web应用的启动过程在《一个基于注解配置的Web项目的启动流程分析》这篇文章写的很好。

4、利用SPI我们能做什么?

可以加一些自己的启动配置信息,把自己的Servlet打成jar包放到Tomcat服务器或者其他工程中执行。我们可以实现一个自己的SPI接口。

4.1、定义一个MyWebAppInitializer
public interface MyWebAppInitializer {
    void loadOnStart(ServletContext var1) throws ServletException;
}
4.2、定义一个MySpringServletContainerInitializer
  1. 实现ServletContainerInitializer接口
  2. 修改@HandlesTypes为我们自己定义的MyWebAppInitializer.class
  3. 实例化MyWebAppInitializer的实现类,并且调用接口的loadOnStart方法
@HandlesTypes(MyWebAppInitializer.class)
public class MySpringServletContainerInitializer implements ServletContainerInitializer{
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletcontext)
            throws ServletException {
        if (set != null) {
            for (Class<?> waiClass : set) {
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        MyWebAppInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        //创建MyWebAppInitializer实现类的对象,并调用loadOnStart方法
                        ((MyWebAppInitializer) waiClass.newInstance()).loadOnStart(servletcontext);
                    } catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }
    }
}
4.3、写一个MyWebAppInitializer的实现类
public class MyWebApplicationInitializerTest implements MyWebAppInitializer{
    @Override
    public void loadOnStart(ServletContext servletContext){
        System.out.println("启动执行MyWebApplicationInitializerTest的loadOnStart方法");
        //注册一个为名字call的servlet
        ServletRegistration.Dynamic servletReg = servletContext.addServlet("call", CallServlet.class);
        servletReg.setLoadOnStartup(1);
        servletReg.addMapping("/call");
    }
}

其中里面的CallServlet.java如下

public class CallServlet extends HttpServlet {
    private static final long serialVersionUID = 3684613967452881093L;

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        resp.getWriter().write(name + ", if you like me, please call me!");
    }
}
4.4、添加配置文件javax.servlet.ServletContainerInitializerr

添加位置为:src -> main -> resources-> META-INF-> services ->javax.servlet.ServletContainerInitializerr

内容:com.leo.spi.MySpringServletContainerInitializer
位置

4.5、启动项目测试

启动日志:

启动执行MyWebApplicationInitializerTest的loadOnStart方法

浏览器测试:http://localhost:8080/springmvc/call?name=leo825
测试成功
上面也提到了,可以把这个SPI的方式打成jar包在其他项目或者直接在Tomcat容器这种运行。
本文的相关源码请参考:chapter-5-springmvc-zero-configuration
spi代码包路径:com.leo.spi
https://gitee.com/leo825/spring-framework-learning-example.git

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72699.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!