这就是Tomcat给我们提供的规范,通过这个规范我们就能实现Spring的零xml配置启动,直接来看Spring是如何做的。
根据上面所说我们可以在spring-web工程下找到META-INF/services/javax.servlet.ServletContainerInitializer配置:
核心的实现就是WebApplicationInitializer,先看看其继承体系
AbstractReactiveWebInitializer不用管,主要看另外一边,但是都是抽象类,也就是说真的实例也是由我们自己实现,但需要我们实现什么呢?我们一般直接继承AbstractAnnotationConfigDispatcherServletInitializer类,有四个抽象方法需要我们实现: //父容器 @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[]{SpringContainer.class}; } //SpringMVC配置子容器 @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[]{MvcContainer.class}; } //获取DispatcherServlet的映射信息 @Override protected String[] getServletMappings() { return new String[]{"http://www.likecs.com/"}; } // filter配置 @Override protected Filter[] getServletFilters() { MyFilter myFilter = new MyFilter(); CorsFilter corsFilter = new CorsFilter(); return new Filter[]{myFilter,corsFilter}; }
这里主要注意getRootConfigClasses和getServletConfigClasses方法,分别加载父、子容器:
@ComponentScan(value = "com.dark",excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}) }) public class SpringContainer { } @ComponentScan(value = "com.dark",includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}) },useDefaultFilters = false) public class MvcContainer { }看到这两个类上的注解应该不陌生了吧,父容器扫描装载了所有不带@Controller注解的类,子容器则相反,但需要对象时首先从当前容器中找,如果没有则从父容器中获取,为什么要这么设计呢?直接放到一个容器中不行么?先思考下, 稍后解答。
回到onStartup方法中,直接回调用到AbstractDispatcherServletInitializer类:
先是调用父类:
public void onStartup(ServletContext servletContext) throws ServletException { registerContextLoaderListener(servletContext); } protected void registerContextLoaderListener(ServletContext servletContext) { //创建spring上下文,注册了SpringContainer WebApplicationContext rootAppContext = createRootApplicationContext(); if (rootAppContext != null) { //创建监听器 ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); listener.setContextInitializers(getRootApplicationContextInitializers()); servletContext.addListener(listener); } }然后调用createRootApplicationContext创建父容器:
protected WebApplicationContext createRootApplicationContext() { Class<?>[] configClasses = getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(configClasses); return context; } else { return null; } }