package org.springframework.web; import java.lang.reflect.Modifier; import java.util.LinkedList; import java.util.List; import java.util.ServiceLoader; import java.util.Set; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.HandlesTypes; import org.springframework.core.annotation.AnnotationAwareOrderComparator; @HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { // Be defensive: Some servlet containers provide us with invalid classes, // no matter what @HandlesTypes says... if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) 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; } AnnotationAwareOrderComparator.sort(initializers); servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers); for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
这个类会查找实现WebApplicationInitializer的类并将容器配置任务交给它们。
AbstractAnnotationConfigDispatcherServletInitializer继承的父类
实现WebApplicationInitializer,在spring3.2版本以后。
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class MyWebAppInitializer 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[] {"/"}; //将DispatcherServlet映射到"/" } }
因此我们只要继承AbstractAnnotationConfigDispatcherServletInitializer重写它的三个方法就可以配置DispatcherServlet和Spring应用上下文,而不需要在web.xml中配置。
getRootConfigClasses()方法返回带有@Configuration注解类会用来配置ContextLoaderListener创建应用上下文的Bean。getServletConfigClasses返回带有@Configuration注解的类会用来创建DispatcherServlet上下文中的bean,这两个上下文是父子关系。
下面我们看一下RootConfig和WebConfig
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; @ImportResource({"classpath:applicationContext.xml"}) //为了注入Environment,需要在RootConfig时加载配置文件 @PropertySource(value="classpath:application.properties") @ComponentScan(basePackages={"com.asclepius.slhdt.serv"},excludeFilters ={ @Filter(type = FilterType.ASPECTJ, pattern = "com.asclepius.slhdt.serv.modules.*.controller.*Controller"), //排除所有控制器 @Filter(type = FilterType.REGEX, pattern = "com.asclepius.slhdt.serv.config.*")}) public class RootConfig { }
@EnableWebMvc @ComponentScan("com.asclepius.slhdt.serv.modules.*.controller") public class WebConfig extends WebMvcConfigurerAdapter { // Thymeleaf view Resolver @Bean public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setTemplateEngine(templateEngine); viewResolver.setCharacterEncoding("UTF-8"); return viewResolver; } // Template resolver @Bean public TemplateResolver templateResolver() { TemplateResolver templateResolver = new ServletContextTemplateResolver(); templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); templateResolver.setTemplateMode("HTML5"); templateResolver.setCharacterEncoding("UTF-8"); templateResolver.setOrder(1); return templateResolver; } public void configureDefaultServletHandling( DefaultServletHandlerConfigurer configurer) { // 静态资源可访问 configurer.enable(); } }
webconfig继承了WebMvcConfigurerAdapter 重写了configureDefaultServletHandling()方法,是为了使对静态资源的请求转发到servlet容器上,而不是控制器。注意webconfig与rootconfig扫描装备的bean不应该有重复,否则会产生两个同类的bean。webconfig只要扫描装配控制器,视图解析器等相关的bean,让dispatcherServlet映射,rootconfig则要扫描装配一般的bean。