- 总结1:
- SCI:Servlet容器(Tomcat)提供的初始化Servlet容器本身的接口,可替换web.xml
- SpringSCI:SpringServletContainerInitializer,Srping提供的SCI的一个实现,起到中转桥接作用,桥接到 WebApplicationInitializer 接口上
- WebApplicationInitializer :可以自定义配置servlet、listener,进而可以通过代码手动控制两个上下文的创建过程(参考:基于xml 参考:支持注解);只能创建上线文但不能配置上下文详细内容,但可以指定上下文配置文件的路径或配置类的类路径
AbstractAnnotationConfigDispatcherServletInitializer
:将两个上下文自动已创建好,已创建好的上下文的类型都是支持注解的;手动继承后只需要指定两个上下文配置类路径即可。- WebMvcConfigurer:
WebMvcConfigurationAdapter已经废弃,最好用implements WebMvcConfigurer代替
@Configuration public class MyConfig implements WebMvcConfigurer { }
如果使用继承,WebMvcConfigurationSupport,DelegatingWebMvcConfiguration,或者使用@EnableWebMvc -
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 typeWebMvcConfigurer
but without@EnableWebMvc
. If you wish to provide custom instances ofRequestMappingHandlerMapping
,RequestMappingHandlerAdapter
, orExceptionHandlerExceptionResolver
, you can declare aWebMvcRegistrationsAdapter
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:
- java提供SPI,用于加载指定接口的 特定实现类
- servlet容器提供SCI接口,用于配置servlet容器初始化;这个过程使用了java的SPI机制;类似Logfactory的工作原理
- Spring提供SpringServletContainerInitializer类(该类实现类SCI接口),该类简称SpringSCI,该类不直接配置servlet容器,而是通过查找WebApplicationInitializer接口(用于web初始化)的实现类,间接配置servlet容器;
- Spring MVC提供基类 AbstractAnnotationConfigDispatcherServletInitializer,用于DispatcherServlet初始化(实现了WebApplicationInitializer接口),该基类既要完成WebApplicationInitializer接口中配置servlet容器的功能,又完成了配置MVC的功能,即同时配置了DispatcherServlet 和 ContextLoaderListener
- Spring提供的接口
WebApplicationInitializer,用于配置servlet容器,并不是只用于配置MVC
,可通过自定义二次开发继承WebApplicationInitializer,结合ServletRegistration.Dynamic
,配置servlet容器,添加自定义的servlet、filter等,可完全替换掉web.xml - 如果是在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等。
- Servlet 3.0 规范和 Spring DispatcherServlet 配置
在 Servlet 3.0 的环境中,容器会在 classpath 中寻找继承了 javax.servlet.ServletContainerInitializer 接口的类,用它来配置 servlet 容器。
Spring 提供了一个继承这个接口的类 SpringServletContainerInitializer,在这个类中,它会寻找任何继承了 WebApplicationInitializer 接口的类并用其来配置 servlet 容器。 - Spring 3.2 提供了一个继承了 WebApplicationInitializer 接口的基类 AbstractAnnotationConfigDispatcherServletInitializer,用于配置Spring MVC项目。Spring 3.2 开始引入一个简易的 WebApplicationInitializer 实现类,这就是 AbstractAnnotationConfigDispatcherServletInitializer。
-
在AbstractAnnotationConfigDispatcherServletInitializer中,给我们留下了三个抽象方法要求我们去实现:
- protected abstract String[] getServletMappings(); 这个方法在AbstractDispatcherServletInitializer 中,这个方法可以将一个或多个路径映射到DispatcherServlet上,如果路径设置为“/”,则所有的请求都会由DispatcherServlet处理。
- protected abstract Class<?>[] getRootConfigClasses(); 这两个方法在AbstractAnnotationConfigDispatcherServletInitializer中
- protected abstract Class<?>[] getServletConfigClasses();
- 在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
- 如果需要定义额外的 servlets 或 filters,只需要创建额外的初始化类。在 Spring MVC 中可以通过继承
- 参考:https://blog.csdn.net/w1196726224/article/details/52687324
- 参考:[Servlet3.0研究之ServletContainerInitializer接口 - 夫礼者的专栏 - CSDN博客: https://blog.csdn.net/lqzkcx3/article/details/78507169]
- 参考:[java中的SPI机制 - 司刚军的个人专栏 - CSDN博客: https://blog.csdn.net/sigangjun/article/details/79071850]
- 参考:[JavaSPI机制学习笔记 - 琴水玉 - 博客园: http://www.cnblogs.com/lovesqcc/p/5229353.html]
- 参考:[Servlet3.0研究之ServletContainerInitializer接口 - 夫礼者的专栏 - CSDN博客: https://blog.csdn.net/lqzkcx3/article/details/78507169]
- Service Provider Interface:
java spi的具体使用如下 :
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。
jdk提供服务实现查找的一个工具类:java.util.ServiceLoader
- Servlet 3.0 规范和 Spring DispatcherServlet 配置
- 使用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); } }
-