JAVA SPI机制及SPI机制在Tomcat中的应用
SPI 是 JAVA 提供的一種服務(wù)提供發(fā)現(xiàn)接口,其實(shí)就是一種面向接口的編程,為接口去匹配具體服務(wù)實(shí)現(xiàn)的機(jī)制,這一點(diǎn)上與 IOC 的思想類似,都是把裝配的控制權(quán)放到了程序之外,下面具體看看什么是 SPI。
一、什么是 SPI
SPI 全稱為 Service Provider Interface,即服務(wù)提供發(fā)現(xiàn)接口,這里的服務(wù)指的不是我們經(jīng)常聽到的微服務(wù)服務(wù)發(fā)現(xiàn),這里的一個服務(wù) Service 指的是一個接口或抽象類,服務(wù)提供方則是對這個接口或抽象類的實(shí)現(xiàn)。SPI 是 ”基于接口的編程 + 策略模式 + 配置文件“ 組合實(shí)現(xiàn)的動態(tài)加載機(jī)制
二、為什么使用 SPI
模塊化設(shè)計中,模塊之間基于接口編程,把裝配的控制權(quán)放到程序之外,實(shí)現(xiàn)系統(tǒng)的解耦
適用于調(diào)用方根據(jù)實(shí)際需求啟用、擴(kuò)展、替換服務(wù)的策略實(shí)現(xiàn)。許多開源框架中都使用了 Java 的 SPI 機(jī)制,如 JDBC 的 SPI 加載模式、日志框架 SLF4J 加載不同提供商的日志實(shí)現(xiàn)、Spring 中也大量適用了 SPI、Dubbo 的擴(kuò)張機(jī)制、ServiceComb Java Chassis (CSE) 的 Filter、異常處理等擴(kuò)展機(jī)制
三、SPI 的實(shí)現(xiàn)
- 在類路徑下的 META-INF/services 目錄下,創(chuàng)建以服務(wù)接口的”全限定名“命名的文件,文件的內(nèi)容為接口實(shí)現(xiàn)類的全限定名
- 實(shí)現(xiàn)類必須在當(dāng)前程序的 classpath 下
- 使用 bash java.util.ServiceLoader 動態(tài)加載實(shí)現(xiàn),會掃描 META-INF/services 下的配置文件加載實(shí)現(xiàn)類
并使用maven打包,并安裝在本地maven倉庫
以下這一步很重要:
再次使用maven打包,并安裝在本地maven倉庫
- 可是多寫一個實(shí)現(xiàn)類
也要再次打包
pom.xml
四、SPI機(jī)制在Tomcat中的應(yīng)用
- 我們知道:Servlet 2.5 實(shí)現(xiàn) webApp 加載的方式是 web.xml,獲取其中配置的ServletContextListener 的實(shí)現(xiàn)類,通過實(shí)現(xiàn)類的 contextInitialized 方法來加載 webapp。
具體可參考我另一篇文章
-
而 Servlet 3.0 就無需配置 web.xml, 直接通過實(shí)現(xiàn)接口 ServletContainerInitializer 就可以實(shí)現(xiàn) webApp 的加載,就如類名一樣,這個類的作用就是在 servlet 容器初始化過程中加入自定義操作,因為是自定義的,所以可擴(kuò)展性就非常強(qiáng),能干很多事情。
-
兩者執(zhí)行位置的區(qū)別
兩個方法的執(zhí)行位置都是在 Tomcat 啟動過程中,Context 容器啟動時。具體方法是 StandardContext 類的 startInternal() 方法,一個 context 容器就代表了一個 webapp。 -
ServletContainerInitializer源碼
- 那么servlet3.0規(guī)范下,是如何不通過web.xml的方式,使用純java代碼的方式加載spring應(yīng)用的呢
直接看到spring-web:5.3.13的源碼:
沒錯,tomcat啟動后,可以根據(jù)servlet3.0的規(guī)范,通過SPI機(jī)制,將實(shí)現(xiàn)了ServletContainerInitializer 接口的類全部進(jìn)行加載,并排序后,依次調(diào)用onstartup方法
在看看javax.servlet.ServletContainerInitializer的內(nèi)容:
于是,我們直接找到SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer {@Overridepublic void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException {List<WebApplicationInitializer> initializers = Collections.emptyList();if (webAppInitializerClasses != null) {initializers = new ArrayList<>(webAppInitializerClasses.size());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)ReflectionUtils.accessibleConstructor(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;}servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");AnnotationAwareOrderComparator.sort(initializers);for (WebApplicationInitializer initializer : initializers) {initializer.onStartup(servletContext);}} }這里要先知道@HandleTypes這個注解的含義,該注解也是Servlet規(guī)范所定義的,作用就是:將注解指定的Class對象(包括其實(shí)現(xiàn)類及子類)作為參數(shù)傳遞到onStartup(也就是@HandleTypes只能作用在實(shí)現(xiàn)了ServletContainerInitializer 的類上)
//含義就是,將WebApplicationInitializer.class的子類獲取實(shí)現(xiàn)類, //作為一個Set<Class<?>>參數(shù)傳入道到SpringServletContainerInitializer.onstartup方法中 @HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer {}然后,我們就可以在onstartup方法中,進(jìn)行我們的開發(fā),例如初始化web容器,初始化DispatcherServlet等操作
- AbstractContextLoaderInitializer.onstartup:
- AbstractDispatcherServletInitializer.onstartup:
參考文章
參考文章
總結(jié)
以上是生活随笔為你收集整理的JAVA SPI机制及SPI机制在Tomcat中的应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HR不会告诉你的薪资谈判技巧
- 下一篇: 互联网日报 | 字节跳动否认进军社区团购