最近看起spring源码,突然想知道没有web.xml的配置,spring是怎么通过一个继承于AbstractAnnotationConfigDispatcherServletInitializer的类来启动自己的。鉴于能力有限以及第一次看源码和发博客,不到之处请望谅~

  我用的IDE是IntelliJ IDEA,这个比myEclipse看源码方便一点,而且黑色背景挺喜欢。然后项目是在maven下的tomcat7插件运行。spring版本是4.3.2.RELEASE。

  如果写过纯注解配置的spring web,应该知道需要继承一个初始化类来装载bean,然后从这个类开始就会加载我们自定义的功能和bean了,下面是我的一个WebInitializer

 @Order(1)
 public class WebMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
     protected Class<?>[] getRootConfigClasses() {
         return new Class[]{RootConfig.class,WebSecurityConfig.class};
     }

     protected Class<?>[] getServletConfigClasses() {
         return new Class[]{WebConfig.class};
     }

     protected String[] getServletMappings() {
         return new String[]{"/"};
     }

     @Override
     protected Filter[] getServletFilters() {
         return new Filter[]{new HiddenHttpMethodFilter()};
     }

 }

  首先看下AbstractAnnotationConfigDispatcherServletInitializer类的结构,这个也是IDEA的一个uml功能,在类那里右键Diagrams->show Diagrams就有啦

  然后我们直接点进AbstractAnnotationConfigDispatcherServletInitializer,可以看到这个类很简单,只有四个方法,然后我们关注下createRootApplicationContext()

 @Override
     protected WebApplicationContext createRootApplicationContext() {
         Class<?>[] configClasses = getRootConfigClasses();
         if (!ObjectUtils.isEmpty(configClasses)) {
             AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
             rootAppContext.register(configClasses);
             return rootAppContext;
         }
         else {
             return null;
         }
     }

  这个方法大概意思是获取用户(程序员)传过来的RootClasses,然后注册里面的bean,这些都不是我们关注的,不过这个方法应该是要在启动后执行的,所以我们可以从这个方法往上找

  IDEA下Ctrl+G可以找调用某个方法或类,然后设置寻找范围为project and library

  我们找到,AbstractContextLoaderInitializer下registerContextLoaderListener(ServletContext servletContext)方法调用子类的createRootApplicationContext()获取WebApplicationContext,继续找registerContextLoaderListener(ServletContext servletContext)方法的调用者,结果发现就是该类下的onStartup(ServletContext servletContext),下面贴下AbstractContextLoaderInitializer类

 public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {

     /** Logger available to subclasses */
     protected final Log logger = LogFactory.getLog(getClass());

     @Override
     public void onStartup(ServletContext servletContext) throws ServletException {
         registerContextLoaderListener(servletContext);
     }

     /**
      * Register a {@link ContextLoaderListener} against the given servlet context. The
      * {@code ContextLoaderListener} is initialized with the application context returned
      * from the {@link #createRootApplicationContext()} template method.
      * @param servletContext the servlet context to register the listener against
      */
     protected void registerContextLoaderListener(ServletContext servletContext) {
         WebApplicationContext rootAppContext = createRootApplicationContext();
         if (rootAppContext != null) {
             ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
             listener.setContextInitializers(getRootApplicationContextInitializers());
             servletContext.addListener(listener);
         }
         else {
             logger.debug("No ContextLoaderListener registered, as " +
                     "createRootApplicationContext() did not return an application context");
         }
     }

     /**
      * Create the "<strong>root</strong>" application context to be provided to the
      * {@code ContextLoaderListener}.
      * <p>The returned context is delegated to
      * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will
      * be established as the parent context for any {@code DispatcherServlet} application
      * contexts. As such, it typically contains middle-tier services, data sources, etc.
      * @return the root application context, or {@code null} if a root context is not
      * desired
      * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
      */
     protected abstract WebApplicationContext createRootApplicationContext();

     /**
      * Specify application context initializers to be applied to the root application
      * context that the {@code ContextLoaderListener} is being created with.
      * @since 4.2
      * @see #createRootApplicationContext()
      * @see ContextLoaderListener#setContextInitializers
      */
     protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
         return null;
     }

 }

  注意的是这里我们跳过了AbstractDispatcherServletInitializer抽象类(看uml图),这个类主要配置DispatcherServlet,这里就是spring mvc等功能的实现了。

  那谁来加载AbstractContextLoaderInitializer?WebApplicationInitializer已经是接口,不会再有一个抽象类来调用了,于是我尝试性地搜WebApplicationInitializer接口,因为spring这种大项目肯定是面向接口的,所以调用的地方一般是写接口,然后我们找到了SpringServletContainerInitializer类,它实现了ServletContainerInitializer接口,这个类大概是说把所有WebApplicationInitializer都startUp一遍,可以说这个类很接近我们的目标了。下面贴下SpringServletContainerInitializer

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

         servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
         AnnotationAwareOrderComparator.sort(initializers);
         for (WebApplicationInitializer initializer : initializers) {
             initializer.onStartup(servletContext);
         }
     }

 }

  在最后的foreach把所有的WebApplicationInitializer都启动一遍。那么问题来了,谁来启动SpringServletContainerInitializer,spring肯定不能自己就能启动的,在

web环境下,就只有web容器了。我们可以在上面某一个地方打个断点,然后Debug一下(事实上,完全可以全程Debug = =,这样准确又快捷,不过这样少了点寻找的意味,沿路风景还是挺不错的)

  可以看到包org.apache.catalina.core下的StandardContext类的startInternal方法,这个已经是tomcat的范围了,所以我们的目标算是达到了。注意的是ServletContainerInitializer接口并不是spring包下的,而是javax.servlet

  我猜测,tomcat通过javax.servlet的ServletContainerInitializer接口来找容器下实现这个接口的类,然后调用它们的OnStartUp,然后spring的SpringServletContainerInitializer就可以把所有WebApplicationInitializer都启动一遍,其中就有我们自己写的WebInitializer,另外spring security用注解配置也是实现WebApplicationInitializer启动的,所以这样spring的扩展性很强。这几天再看下tomcat源码,了解下tomcat的机制。

spring注解配置启动过程的更多相关文章

  1. 关于Spring注解配置的步骤

    今天分享一下 关于Spring注解配置的流程 1 导包:如下图所示 2 书写User和Car类  代码如下 package cn.lijun.bean; public class Car { priv ...

  2. Spring注解配置和xml配置优缺点比较

    Spring注解配置和xml配置优缺点比较 编辑 ​ 在昨天发布的文章<spring boot基于注解方式配置datasource>一文中凯哥简单的对xml配置和注解配置进行了比较.然后朋 ...

  3. Spring MVC的启动过程

    一.概述 下面一个基本的运用springMVC的的web.xml的配置,这里要注意两个地方,一个是ContextLoadListener,一个是DispatcherServlet.web容器正是通过这 ...

  4. 【Spring实战】Spring注解配置工作原理源码解析

    一.背景知识 在[Spring实战]Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题.目标去看,这样才能最 ...

  5. Spring注解配置Aop

    之前学习了SpringAop的基本原理.http://www.cnblogs.com/expiator/p/7977975.html 现在尝试使用注解来配置SpringAop. Aop,面向切面编程. ...

  6. 【转】【Spring实战】Spring注解配置工作原理源码解析

    一.背景知识 在[Spring实战]Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题.目标去看,这样才能最 ...

  7. Spring注解配置、Spring aop、整合Junit——Spring学习 day2

    注解配置: 1.为主配置文件引入新的命名空间(约束) preference中引入文件 2.开启使用注解代理配置文件 <?xml version="1.0" encoding= ...

  8. spring容器的启动过程

    spring的启动过程: 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环 ...

  9. SpringBoot:Spring容器的启动过程

    一.简述 Spring的启动过程就是IoC容器的启动过程,本质上就是创建和初始化Bean的工厂(BeanFactory),BeanFactory是整个SpringIoC的核心,Spring使用Bean ...

随机推荐

  1. FFRPC应用之Client/Server

    摘要: Ffrpc 进行了重构,精简了代码,代码更加清晰简洁,几乎完美的达到了我的预想.接下来将写几遍文章来介绍ffrpc可以做什么.简单总结ffrpc的特性是: Ffrpc是c++ 网络通信库 全异 ...

  2. windows原生开发之界面疑云

        windows桌面开发,界面始终是最大的困惑.我们对前端工具的要求,其实只有窗体设计器.消息映射,过分点的话自适应屏幕.模型绑定.能够免于手工书写,其实这个问题并不复杂,但VS不实现.QT语法 ...

  3. [转]一个小试验验证对象的指针计数置为nil的情况

    本文转载于新风作浪的博客专栏,博客地址:http://blog.csdn.net/duxinfeng2010/article/details/8757211 以下博客原文: 最近遇到这样一个问题,以前 ...

  4. cordova 学习笔记

    0.sdk安装 http://spring.io/guides/gs/android/ 1.安装(node.js 需要安装https://nodejs.org/) on OS X and Linux: ...

  5. Ubuntu 13.04 双显卡安装NVIDIA GT 630M驱动

    [日期:2013-05-24]   Linux系统:Ubuntu 13.04 安装 bumblebee 以管理双显卡,下面命令会自动安装NVIDIA显卡驱动 sudo add-apt-reposito ...

  6. ubuntu 安装bochs

    //首先官网下载bochs源码wget http://bochs.sourceforge.net/svn-snapshot/bochs-20150503.tar.gz//然后解压 tar vxzf b ...

  7. zeromq 测试总结

    总结 测试项目 github (https://github.com/solq360/jmzq) 非常不稳定 pub/sub 模式 30W压测丢了27W条消息,官方没有给出任何的发送状态供业务层处理 ...

  8. Linux高级编程--07.进程间通信

    每个进程各自有不同的用户地址空间,进程之间要交换数据必须通过在内核中开辟缓冲区,从而实现数据共享. 管道 管道是一种最基本的IPC机制,由pipe函数创建: int pipe(int filedes[ ...

  9. 【解决方案】VS2013外部工具中添加ildasm.exe

    VS2013安装在Win8.1的操作系统中,开始屏幕中找不到ildasm.exe没有显示,于是下面提供了一种方法将ildasm.exe工具添加到VS2013外部工具中,并将反编译的代码输出到VS201 ...

  10. django 快速实现session的操作

    说明: 从这一篇开始就不再完整的介绍django项目的创建过程了,因为前面几篇博客中都详细的介绍了这个创建过程,套路都是一样的,熟悉了这个套路,后面要做的是一些细节技术点的学习和练习. 上一节讲到了d ...