ServletContainerInitializer与ServletContextInitializer学习笔记
其他
2021-03-08 20:16:54
阅读次数: 0
ServletContainerInitializer加载原理及过程
- Servlet3.x规范利用spi机制,自动加载实现ServletContainerInitializer接口的类,并调用onStartup方法。
- SpringServletContainerInitializer根据@HandlesTypes(WebApplicationInitializer.class)进行class筛选。
- 在onStartup中调用所有WebApplicationInitializer对象的onStart方法。
- SpringServletContainerInitializer代码实现
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
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);
}
}
}
ServletContextInitializer加载原理及过程
- ServletWebServerApplicationContext容器刷新方法onRefresh() 调用 createWebServer() 创建Web服务
- 创建容器时,通过getSelfInitializer() => getServletContextInitializerBeans() 获取所有的ServletContextInitializer
- 遍历ServletContextInitializer实例并调用onStartup(servletContext)方法
补充说明
- 在嵌入式容器中ServletContainerInitializer将不会被自动加载,替代方案可使用ServletContextInitializer解决
@Bean
public ServletContextInitializer servletContextInitializer(){
return servletContext ->{
CharacterEncodingFilter filter = new CharacterEncodingFilter() ;
FilterRegistration.Dynamic dynamic = servletContext.addFilter("c-filter", filter);
dynamic.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false,"/");
} ;
}
- 在嵌入式容器中@WebServlet不会被自动扫描,替代方案可使用@ServletRegistrationBean解决
// ServletRegistrationBean 继承ServletContextInitializer,所以这里与2中效果等同
@Bean
public ServletRegistrationBean servletRegistrationBean(){
ServletRegistrationBean registrationBean =
new ServletRegistrationBean(new AsyncServlet(), "/async-servlet") ;
return registrationBean ;
}
- Springboot应用部署到传统servlet容器也是利用ServletContainerInitializer的SPI实现
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
protected Log logger;
private boolean registerErrorPageFilter = true;
protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) {
this.registerErrorPageFilter = registerErrorPageFilter;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(getClass());
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
}
}
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
StandardServletEnvironment environment = new StandardServletEnvironment();
environment.initPropertySources(servletContext, null);
builder.environment(environment);
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
builder = configure(builder);
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(
Collections.singleton(ErrorPageFilterConfiguration.class));
}
return run(application);
}
protected SpringApplicationBuilder createSpringApplicationBuilder() {
return new SpringApplicationBuilder();
}
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext) application.run();
}
private ApplicationContext getExistingRootWebApplicationContext(
ServletContext servletContext) {
Object context = servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (context instanceof ApplicationContext) {
return (ApplicationContext) context;
}
return null;
}
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder;
}
}
转载自blog.csdn.net/yichengjie_c/article/details/114313222