日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JAVA SPI机制及SPI机制在Tomcat中的应用

發(fā)布時間:2025/3/15 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAVA SPI机制及SPI机制在Tomcat中的应用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

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)

  • 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)類
  • 先定義一個基接口
  • public interface BaseDriver {void url(); }

    并使用maven打包,并安裝在本地maven倉庫

  • 新建maven項目,導(dǎo)入含有BaseDriver的maven坐標(biāo),編寫一個接口實(shí)現(xiàn)類
  • public class MysqlDriver implements BaseDriver {@Overridepublic void url() {System.out.println("this is a mysql url");} }

    以下這一步很重要:

    再次使用maven打包,并安裝在本地maven倉庫

    • 可是多寫一個實(shí)現(xiàn)類
    public class OracleDriver implements BaseDriver {public void url() {System.out.println("this is an oracle url");} }

    也要再次打包

  • 新建maven項目使用
    pom.xml
  • <dependency><groupId>com.spi</groupId><artifactId>MYSQL_SPI</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>com.spi</groupId><artifactId>ORACLE_SPI</artifactId><version>0.0.1-SNAPSHOT</version></dependency>
  • 測試
  • 四、SPI機(jī)制在Tomcat中的應(yīng)用

    • 我們知道:Servlet 2.5 實(shí)現(xiàn) webApp 加載的方式是 web.xml,獲取其中配置的ServletContextListener 的實(shí)現(xiàn)類,通過實(shí)現(xiàn)類的 contextInitialized 方法來加載 webapp。
    <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>

    具體可參考我另一篇文章

    • 而 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源碼

    public interface ServletContainerInitializer {public void onStartup(Set<Class<?>> c, ServletContext ctx)throws ServletException; }
    • 那么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)容:

    org.springframework.web.SpringServletContainerInitializer

    于是,我們直接找到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:
    public void onStartup(ServletContext servletContext) throws ServletException {this.registerContextLoaderListener(servletContext);}
    • AbstractDispatcherServletInitializer.onstartup:
    public void onStartup(ServletContext servletContext) throws ServletException {//調(diào)用父類super.onStartup(servletContext);this.registerDispatcherServlet(servletContext);}

    參考文章
    參考文章

    總結(jié)

    以上是生活随笔為你收集整理的JAVA SPI机制及SPI机制在Tomcat中的应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。