Spring 梳理 - JavaConfig、SPI、SCI、SpringSCI、WebApplicationInitializer、AbstractAnnotationConfigDispatcherServletInitializer、WebMvcConfigurationSupport


  1. 總結1:
    1. SCI:Servlet容器(Tomcat)提供的初始化Servlet容器本身的接口,可替換web.xml
    2. SpringSCI:SpringServletContainerInitializer,Srping提供的SCI的一個實現,起到中轉橋接作用,橋接到 WebApplicationInitializer 接口上
    3. WebApplicationInitializer :可以自定義配置servlet、listener,進而可以通過代碼手動控制兩個上下文的創建過程(參考:基於xml     參考:支持注解);只能創建上線文但不能配置上下文詳細內容,但可以指定上下文配置文件的路徑或配置類的類路徑
    4. AbstractAnnotationConfigDispatcherServletInitializer:將兩個上下文自動已創建好,已創建好的上下文的類型都是支持注解的;手動繼承后只需要指定兩個上下文配置類路徑即可。
    5. WebMvcConfigurer:

      WebMvcConfigurationAdapter已經廢棄,最好用implements WebMvcConfigurer代替

      @Configuration
      public class MyConfig implements WebMvcConfigurer {
          
      }
      如果使用繼承,WebMvcConfigurationSupport,DelegatingWebMvcConfiguration,或者使用@EnableWebMvc
    6. If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

      If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

  2. 總結2:
    1. java提供SPI,用於加載指定接口的 特定實現類
    2. servlet容器提供SCI接口,用於配置servlet容器初始化;這個過程使用了java的SPI機制;類似Logfactory的工作原理
    3. Spring提供SpringServletContainerInitializer類(該類實現類SCI接口),該類簡稱SpringSCI,該類不直接配置servlet容器,而是通過查找WebApplicationInitializer接口(用於web初始化)的實現類,間接配置servlet容器;
    4. Spring MVC提供基類 AbstractAnnotationConfigDispatcherServletInitializer,用於DispatcherServlet初始化(實現了WebApplicationInitializer接口),該基類既要完成WebApplicationInitializer接口中配置servlet容器的功能,又完成了配置MVC的功能,即同時配置了DispatcherServlet 和 ContextLoaderListener
    5. Spring提供的接口WebApplicationInitializer,用於配置servlet容器,並不是只用於配置MVC,可通過自定義二次開發繼承WebApplicationInitializer,結合ServletRegistration.Dynamic,配置servlet容器,添加自定義的servlet、filter等,可完全替換掉web.xml
    6. 如果是在MVC項目中,想添加自定義的servlet、filter,可不用實現WebApplicationInitializer,直接只要重寫 AbstractAnnotationConfigDispatcherServletInitializer 類的 getServletFilters() 方法就行了;不需要 mapping,因為會自動 mapping 到 DispatcherServlet 上,通過返回多個 filter,可以添加多個 filter。
  3. 總結3:spring  依據“java的SPI(Service Provider Interface)機制”和“servlet容器的SCI(ServletContainerInitializer)接口”,通過SpringServletContainerInitializer實現spring組件和servlet容器解耦,解耦后,spring組件可以直接使用spring提供的WebApplicationInitializer接口,實現類似SCI功能,通過實現WebApplicationInitializer,可以向servlet容器添加servlet,listener等。
    1. Servlet 3.0 規范和 Spring DispatcherServlet 配置
        在 Servlet 3.0 的環境中,容器會在 classpath 中尋找繼承了 javax.servlet.ServletContainerInitializer 接口的類,用它來配置 servlet 容器。
        Spring 提供了一個繼承這個接口的類 SpringServletContainerInitializer,在這個類中,它會尋找任何繼承了 WebApplicationInitializer 接口的類並用其來配置 servlet 容器。
    2. Spring 3.2 提供了一個繼承了 WebApplicationInitializer 接口的基類 AbstractAnnotationConfigDispatcherServletInitializer,用於配置Spring MVC項目。Spring 3.2 開始引入一個簡易的 WebApplicationInitializer 實現類,這就是 AbstractAnnotationConfigDispatcherServletInitializer。
    3. AbstractAnnotationConfigDispatcherServletInitializer中,給我們留下了三個抽象方法要求我們去實現:

      • protected abstract String[] getServletMappings();    這個方法在AbstractDispatcherServletInitializer 中,這個方法可以將一個或多個路徑映射到DispatcherServlet上,如果路徑設置為“/”,則所有的請求都會由DispatcherServlet處理。
      • protected abstract Class<?>[] getRootConfigClasses();    這兩個方法在AbstractAnnotationConfigDispatcherServletInitializer中
      • protected abstract Class<?>[] getServletConfigClasses();
    4. 在Spring MVC項目中
      1. 你的 servlet 配置類只需要繼承 AbstractAnnotationConfigDispatcherServletInitializer,就會被發現而用於 servlet 容器的配置。
      2. AbstractAnnotationConfigDispatcherServletInitializer用於創建兩種應用上下文:DispatcherServlet 創建的和攔截器 ContextLoaderListener 創建的上下文
      3. 在 AbstractAnnotationConfigDispatcherServletInitializer 中 DispatcherServlet 和 ContextLoaderListener 都會被創建,而基類中的方法就可用來創建不同的應用上下文:

        1. getServletConfigClasses():定義 DispatcherServlet 應用上下文中的 beans;

        2. getRootConfigClasses():定義攔截器 ContextLoaderListener 應用上下文中的 beans
      4. 示例
        1.   
          package spittr.config;
          import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
          import spittr.web.WebConfig;
          public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
            @Override
            protected Class<?>[] getRootConfigClasses() {
              return new Class<?>[] { RootConfig.class };
            }
            @Override
            protected Class<?>[] getServletConfigClasses() {
              return new Class<?>[] { WebConfig.class };
            }
            @Override
            protected String[] getServletMappings() {
              return new String[] { "/" };
            }
          }

           

      5. 配置額外的 servlets 和 filters(非Bean,非DispatcherServlet 和 ContextLoaderListener中的元素)
        1. 如果需要定義額外的 servlets 或 filters,只需要創建額外的初始化類。在 Spring MVC 中可以通過繼承 WebApplicationInitializer 接口來實現。
        2. 定義一個新的 servlet:
          
          package com.myapp.config;
          import javax.servlet.ServletContext;
          import javax.servlet.ServletException;
          import javax.servlet.ServletRegistration.Dynamic;
          import org.springframework.web.WebApplicationInitializer;
          import com.myapp.MyServlet;
          public class MyServletInitializer implements WebApplicationInitializer {
              @Override
              public void onStartup(ServletContext throws ServletException {
                  Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);
                  myServlet.addMapping("/custom/**");
              }
          }
          
          定義 filters 和 listeners:
          @Override
          public void onStartup(ServletContext servletContext)
          throws ServletException {
              // 定義filter
              javax.servlet.FilterRegistration.Dynamic filter =
                      servletContext.addFilter("myFilter", MyFilter.class);
              filter.addMappingForUrlPatterns(null, false, "/custom/*");
          
          }

           

        3. 如果你需要為 DispatcherServlet 添加 filter 的話,就不用這么麻煩了,你只要重寫 AbstractAnnotationConfigDispatcherServletInitializer 類的 getServletFilters() 方法就行了:

          @Override
          protected Filter[] getServletFilters() {
              return new Filter[] { new MyFilter() };
          }

          不需要 mapping,因為會自動 mapping 到 DispatcherServlet 上,通過返回多個 filter,可以添加多個 filter

    5. 參考:https://blog.csdn.net/w1196726224/article/details/52687324
    6. 參考:[Servlet3.0研究之ServletContainerInitializer接口 - 夫禮者的專欄 - CSDN博客: https://blog.csdn.net/lqzkcx3/article/details/78507169]
    7. 參考:[java中的SPI機制 - 司剛軍的個人專欄 - CSDN博客: https://blog.csdn.net/sigangjun/article/details/79071850]
    8. 參考:[JavaSPI機制學習筆記 - 琴水玉 - 博客園: http://www.cnblogs.com/lovesqcc/p/5229353.html]
    9. 參考:[Servlet3.0研究之ServletContainerInitializer接口 - 夫禮者的專欄 - CSDN博客: https://blog.csdn.net/lqzkcx3/article/details/78507169]
    10. Service Provider Interface:

      java spi的具體使用如下  :

      當服務的提供者,提供了服務接口的一種實現之后,在jar包的META-INF/services/目錄里同時創建一個以服務接口命名的文件。該文件里就是實現該服務接口的具體實現類。而當外部程序裝配這個模塊的時候,就能通過該jar包META-INF/services/里的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。 

      基於這樣一個約定就能很好的找到服務接口的實現類,而不需要再代碼里制定。

      jdk提供服務實現查找的一個工具類:java.util.ServiceLoader

  4. 使用JavaConfig普通web應用(非MVC)
    1. public class App {
          public static void main(String[] args) {
              ApplicationContext context = new AnnotationConfigApplicationContext(
                      AppConfig.class);
              CustomerBo customer = (CustomerBo) context.getBean("customer");
              customer.printMsg("Hello 1");
              SchedulerBo scheduler = (SchedulerBo) context.getBean("scheduler");
              scheduler.printMsg("Hello 2");
          }
      
      }
      
      
      ##############
      
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Import;
      @Configuration
      
      @Import({ CustomerConfig.class, SchedulerConfig.class })
      
      public class AppConfig {
      }
      
      ##############
      
      @Configuration
      public class CustomerConfig {
          @Bean(name="customer")
          public CustomerBo customerBo(){
              return new CustomerBo();
          }
      }
      
      ##############
      public class SchedulerBo {
          public void printMsg(String msg) {
              System.out.println("SchedulerBo : " + msg);
      
          }
      }

       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM