这一次搞懂Spring Web零xml配置原理以及父子容器关系 (2)

这就是Tomcat给我们提供的规范,通过这个规范我们就能实现Spring的零xml配置启动,直接来看Spring是如何做的。
根据上面所说我们可以在spring-web工程下找到META-INF/services/javax.servlet.ServletContainerInitializer配置:

@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) { // 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) 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); } } }

核心的实现就是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}; }

这里主要注意getRootConfigClassesgetServletConfigClasses方法,分别加载父、子容器:

@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 { super.onStartup(servletContext); //注册DispatcherServlet registerDispatcherServlet(servletContext); }

先是调用父类:

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; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjpjj.html