美文网首页
(转载)Spring 梳理 - JavaConfig、SPI、S

(转载)Spring 梳理 - JavaConfig、SPI、S

作者: 云中人山 | 来源:发表于2020-09-12 13:19 被阅读0次

最近在看Spring boot编程思想,看到8.2节,始终未理解所谓自动装配的处理。 之后在一篇博客中看到有人评价WebApplicationInitializer实际上的SCI机制。首次听闻还有所谓的SCI机制。搜索后将其文章转发于此,主要目的是为了防止原文章被删除。
原文地址

总结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

  1. 如果您想保留Spring Boot MVC功能并想要添加其他MVC配置 (interceptors, formatters, view controllers, and other features),则可以添加自己的类型为WebMvcConfigurer的@Configuration类,但不添加@EnableWebMvc。 如果希望提供RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver的自定义实例,则可以声明一个WebMvcRegistrationsAdapter实例以提供此类组件。

总结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的功能,即同时配置了DispatcherServletContextLoaderListener
  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

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项目中

    • 你的 servlet 配置类只需要继承 AbstractAnnotationConfigDispatcherServletInitializer,就会被发现而用于 servlet 容器的配置。

    • AbstractAnnotationConfigDispatcherServletInitializer用于创建两种应用上下文:DispatcherServlet 创建的和拦截器 ContextLoaderListener 创建的上下文

    • AbstractAnnotationConfigDispatcherServletInitializer 中 DispatcherServlet 和 ContextLoaderListener 都会被创建,而基类中的方法就可用来创建不同的应用上下文:

      • getServletConfigClasses():定义 DispatcherServlet 应用上下文中的 beans;
      • getRootConfigClasses():定义拦截器 ContextLoaderListener 应用上下文中的 beans
    • 示例

      • 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[] { "/" };
          }
        }
        
    • 配置额外的 servlets 和 filters(非Bean,非DispatcherServlet 和 ContextLoaderListener中的元素)

      • 如果需要定义额外的 servlets 或 filters,只需要创建额外的初始化类。在 Spring MVC 中可以通过继承 WebApplicationInitializer 接口来实现。

      • 定义一个新的 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/*");
          
          }
          
      • 如果你需要为 DispatcherServlet 添加 filter 的话,就不用这么麻烦了,你只要重写 AbstractAnnotationConfigDispatcherServletInitializer 类的 getServletFilters() 方法就行了:

        • @Override
          protected Filter[] getServletFilters() {
              return new Filter[] { new MyFilter() };
          }
          
        • 不需要 mapping,因为会自动 mapping 到 DispatcherServlet 上,通过返回多个 filter,可以添加多个 filter


使用JavaConfig普通web应用(非MVC)

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);

    }
}

相关文章

网友评论

      本文标题:(转载)Spring 梳理 - JavaConfig、SPI、S

      本文链接:https://www.haomeiwen.com/subject/pbrwektx.html