最近在看Spring boot编程思想,看到8.2节,始终未理解所谓自动装配的处理。 之后在一篇博客中看到有人评价WebApplicationInitializer
实际上的SCI机制。首次听闻还有所谓的SCI机制。搜索后将其文章转发于此,主要目的是为了防止原文章被删除。
原文地址
总结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
- 如果您想保留Spring Boot MVC功能并想要添加其他MVC配置 (interceptors, formatters, view controllers, and other features),则可以添加自己的类型为WebMvcConfigurer的@Configuration类,但不添加@EnableWebMvc。 如果希望提供
RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
或ExceptionHandlerExceptionResolver
的自定义实例,则可以声明一个WebMvcRegistrationsAdapter
实例以提供此类组件。
总结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
-
-
- 参考: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]
使用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);
}
}
网友评论