文章目录

  • 1、前言
  • 2、WebApplicationInitializer的定义
  • 3、实现原理
  • 4、利用SPI我们能做什么?
    • 4.1、定义一个`MyWebAppInitializer`
    • 4.2、定义一个`MySpringServletContainerInitializer`
    • 4.3、写一个`MyWebAppInitializer`的实现类
    • 4.4、添加配置文件`javax.servlet.ServletContainerInitializerr`
    • 4.5、启动项目测试

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类来实现的

关于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{@Overridepublic 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{@Overridepublic void loadOnStart(ServletContext servletContext){System.out.println("启动执行MyWebApplicationInitializerTest的loadOnStart方法");//注册一个为名字call的servletServletRegistration.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;@Overridepublic 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

Spring中对于WebApplicationInitializer的理解相关推荐

  1. spring 中单利模式的理解

    一.Spring单例模式与线程安全 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方. 单例模式的意思就是只有一个实例.单例模式确保某 ...

  2. spring扩展点之二:spring中关于bean初始化、销毁等使用汇总,ApplicationContextAware将ApplicationContext注入...

    <spring扩展点之二:spring中关于bean初始化.销毁等使用汇总,ApplicationContextAware将ApplicationContext注入> <spring ...

  3. spring(7)---深入理解Spring核心技术——Spring中的各模块详解

    深入理解Spring核心技术--Spring中的各模块详解 Spring框架的两个基本概念IOC容器和AOP,相信大家现在对Spring中的这两个部分的基本概念有了一定的认识,好了,那么今天我们就来正 ...

  4. SpringCloud学习笔记010---杂七杂八004:Spring中Bean及@Bean的理解

    Bean在Spring和SpringMVC中无所不在,将这个概念内化很重要,下面分享一下我的想法: 一.Bean是啥 1.Java面向对象,对象有方法和属性,那么就需要对象实例来调用方法和属性(即实例 ...

  5. python ioc框架_轻松理解 Spring 中的 IOC

    Spring 简介 Spring 是一个开源的轻量级的企业级框架,其核心是反转控制 (IoC) 和面向切面 (AOP) 的容器框架.我们可以把 Spring 看成是对象的容器,容器中可以包含很多对象, ...

  6. java day59【 AOP 的相关概念[理解] 、 Spring 中的 AOP[掌握] 、 Spring 整合 Junit[掌握] 】...

    第1章 AOP 的相关概念[理解] 1.1AOP 概述 1.1.1 什么是 AOP 1.1.2 AOP 的作用及优势 1.1.3 AOP 的实现方式 1.2AOP 的具体应用 1.2.1 案例中问题 ...

  7. Spring中Bean及@Bean的理解

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:https://www.cnblogs.com/bossen/p/5824067.html Bean ...

  8. java的lookup方法_深入理解Spring中的Lookup(方法注入)

    前言 本文主要给大家介绍了关于Spring中Lookup(方法注入)的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 在使用Spring时,可能会遇到这种情况:一个单例的Be ...

  9. 谈谈自己对Spring中IOC和AOP的理解

    Spring框架的核心思想主要是IOC和AOP的思想 IOC IOC概念 Spring中的IOC AOP AOP概念 百度百科的解释 个人理解 AOP思想 IOC IOC概念 所谓IOC即 Inver ...

最新文章

  1. 怎么让div显示一行,其余的隐藏。
  2. HDU 6143 Killer Names(排列+容斥,dp)
  3. 欢迎大家踊跃报名担当“讲师”
  4. Overview-ISA-2004-SP3
  5. java 只显示文本文件_Java设计并实现一个应用程序,能够读取一个文本文件中的内容并显示,同时能够计算出文本中的行数。...
  6. 扬尘监测系统_工地扬尘监测_工地扬尘监测解决方案
  7. php编译成二进制文件_2020年小米高级 PHP 工程师面试题
  8. mysql入门优化_MySQL数据库:MySQL十大优化技巧详解
  9. 小米键盘 键盘切换_“年轻人的第一把机械键盘”,小米机械键盘到底值不值...
  10. 所需即所获:像IDE一样使用 vim
  11. linux 部署 ibase4j,ibase4j学习
  12. C站一名 普通技术博主 的终端与【开端】,因为热爱,所以习惯,2021~2022
  13. 嵌入式软件开发到底是干什么的?
  14. 用idea对git的merge进行撤销
  15. php中怎么批量修改图片大小,怎么批量修改图片大小 光影魔术手批量处理图片...
  16. BugkuCTF 秋名山老司机wp
  17. js在ie下打开对话窗口的方式
  18. Nginx正则表达式locationrewrite
  19. 苹果7服务器是什么系统版本,最新系统 iOS14.7 Beta1 版本推出!
  20. 点餐系统-----数据库设计

热门文章

  1. 90年代谁最强?乔丹不可撼动石佛上榜
  2. SAP标准工单入库时产生成本差异-求解进行时
  3. 会计记忆总结之七:财产清查
  4. BASIS--Client 锁定和解锁
  5. 打车APP大数据宰客套路多:苹果比安卓贵、熟客比新客贵
  6. 2021热度不减的在线教育,正在努力成为线下教育的有益补充
  7. 华为q1设置虚拟服务器,如何使用华为华为Q1 Q1互联网路由设置图文教程 | 192.168.1.1手机登陆...
  8. 容易答错的java面试题_Java程序员面试中最容易答错的8道面试题,你中招了吗?...
  9. 基于c#的相关性分析_开源Math.NET基础数学类库使用(11)C#计算相关系数
  10. Swift 十进制二进制转换 (How to convert a decimal number to binary in Swift)