写点什么

Spring Boot「19」WebApplicationInitializer 源码分析

作者:Samson
  • 2022-10-31
    上海
  • 本文字数:1967 字

    阅读完需:约 6 分钟

01-WebApplicationInitializer源码分析

ServletContainerInitializer是 javax.servlet-api-*.jar 中定义的接口,在 web 容器启动时为提供给第三方组件机会做一些初始化的工作。使用的就是 Java SPI 机制,详细信息在下面的章节中介绍。例如,Tomcat 遵循了 Java SPI 自己实现了一套服务发现机制org.apache.catalina.startup.WebappServiceLoader


org.apache.catalina.startup.ContextConfig#processServletContainerInitializers方法中,使用 WebappServiceLoader 加载 SPI javax.servlet.ServletContainerInitializer的所有实现。



// 忽略其他
WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);detectedScis = loader.load(ServletContainerInitializer.class);
// 忽略其他
复制代码


org.springframework.web.WebApplicationInitializer由 spring-web-5.1.5.RELEASE.jar 提供,该包的**/META-INFO/services**目录下提供了对 SPI javax.servlet.ServletContainerInitializer的实现org.springframework.web.SpringServletContainerInitializer,并且标注其处理的类是WebApplicationInitializer.class


当我们在应用中实现WebApplicationInitializer接口,并实现其中的方法onStartup方法,就会在SpringServletContainerInitializer#onStartup被调用时,逐个调用WebApplicationInitializer#onStartup

02-SPI 机制

SPI(Service Provider Interface)是 Java 6 引入的一种发现和加载特定接口实现的特性。它由四部分组成:


  1. Service,一组接口或类,提供特定的功能或特性;

  2. SPI,一个接口或抽象类,作为 1.中服务的代理或接入点;

  3. Service Provider,2.中 SPI 的特定实现;一般配置在 /META-INF/services/ 文件夹下以 SPI 全量名为文件名,文件内容为服务提供者,即具体的特定实现。

  4. ServiceLoader,java.util.ServiceLoader,SPI 机制的核心,用来发现并加载 Service Provider;


注:1 和 2 组成了 Java 生态系统中常被提到的 API。

02.1-java.sql.Driver示例


ServiceLoader<Driver> serviceLoader = ServiceLoader.load(Driver.class);
Iterator<Driver> iterator = serviceLoader.iterator();
while (iterator.hasNext()) { Driver driver = iterator.next(); System.out.println(driver.getClass().getName()); /** * classpath 中包含 Hsqldb MySQL 实现 * 输出内容为: * org.hsqldb.jdbc.JDBCDriver 「hsqldb」 * com.mysql.cj.jdbc.Driver 「mysql」 */}
复制代码


完整代码参考gitee.com


java.sql.Driver 是 SPI,mysql-connector-java-8.0.7-dmr.jar hsqldb-2.3.4.jar是两个不同的 Service Provider,所以在 jar 包中的**/META-INF/services/java.sql.Driver**中包含对应的具体实现类。

02.2-源码分析

java.sql.DriverManager 中有一个静态代码块,当该类初始化时会被调用。


static {       loadInitialDrivers();       println("JDBC DriverManager initialized");}
复制代码


继续追踪loadInitialDrivers()方法的实现可以发现,该方法中包含了与上节示例中类似的代码:



AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated. * It may be the case that the driver class may not be there * i.e. there may be a packaged driver with the service class * as implementation of java.sql.Driver but the actual class * may be missing. In that case a java.util.ServiceConfigurationError * will be thrown at runtime by the VM trying to locate * and load the service. * * Adding a try catch block to catch those runtime errors * if driver not available in classpath but it's * packaged as service and that service is there in classpath. */ try{ while(driversIterator.hasNext()) { driversIterator.next(); } } catch(Throwable t) { // Do nothing } return null; }});
复制代码


执行完毕后,发现类路径下的两个实现类都被加载到 JVM 中。



[1] Java基础之SPI机制[2] Java Service Provider Interface[3] Java常用机制-SPI[4] 常见的SPI示例

发布于: 刚刚阅读数: 3
用户头像

Samson

关注

还未添加个人签名 2019-07-22 加入

InfoQ签约作者 | 阿里云社区签约作者

评论

发布
暂无评论
Spring Boot「19」WebApplicationInitializer源码分析_Java_Samson_InfoQ写作社区