spring注解配置启动过程

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

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

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

 1 @Order(1)
 2 public class WebMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
 3     protected Class<?>[] getRootConfigClasses() {
 4         return new Class[]{RootConfig.class,WebSecurityConfig.class};
 5     }
 6
 7     protected Class<?>[] getServletConfigClasses() {
 8         return new Class[]{WebConfig.class};
 9     }
10
11     protected String[] getServletMappings() {
12         return new String[]{"/"};
13     }
14
15     @Override
16     protected Filter[] getServletFilters() {
17         return new Filter[]{new HiddenHttpMethodFilter()};
18     }
19
20 }

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

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

 1 @Override
 2     protected WebApplicationContext createRootApplicationContext() {
 3         Class<?>[] configClasses = getRootConfigClasses();
 4         if (!ObjectUtils.isEmpty(configClasses)) {
 5             AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
 6             rootAppContext.register(configClasses);
 7             return rootAppContext;
 8         }
 9         else {
10             return null;
11         }
12     }

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

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

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

 1 public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
 2
 3     /** Logger available to subclasses */
 4     protected final Log logger = LogFactory.getLog(getClass());
 5
 6
 7     @Override
 8     public void onStartup(ServletContext servletContext) throws ServletException {
 9         registerContextLoaderListener(servletContext);
10     }
11
12     /**
13      * Register a {@link ContextLoaderListener} against the given servlet context. The
14      * {@code ContextLoaderListener} is initialized with the application context returned
15      * from the {@link #createRootApplicationContext()} template method.
16      * @param servletContext the servlet context to register the listener against
17      */
18     protected void registerContextLoaderListener(ServletContext servletContext) {
19         WebApplicationContext rootAppContext = createRootApplicationContext();
20         if (rootAppContext != null) {
21             ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
22             listener.setContextInitializers(getRootApplicationContextInitializers());
23             servletContext.addListener(listener);
24         }
25         else {
26             logger.debug("No ContextLoaderListener registered, as " +
27                     "createRootApplicationContext() did not return an application context");
28         }
29     }
30
31     /**
32      * Create the "<strong>root</strong>" application context to be provided to the
33      * {@code ContextLoaderListener}.
34      * <p>The returned context is delegated to
35      * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will
36      * be established as the parent context for any {@code DispatcherServlet} application
37      * contexts. As such, it typically contains middle-tier services, data sources, etc.
38      * @return the root application context, or {@code null} if a root context is not
39      * desired
40      * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
41      */
42     protected abstract WebApplicationContext createRootApplicationContext();
43
44     /**
45      * Specify application context initializers to be applied to the root application
46      * context that the {@code ContextLoaderListener} is being created with.
47      * @since 4.2
48      * @see #createRootApplicationContext()
49      * @see ContextLoaderListener#setContextInitializers
50      */
51     protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
52         return null;
53     }
54
55 }

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

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

 1 @HandlesTypes(WebApplicationInitializer.class)
 2 public class SpringServletContainerInitializer implements ServletContainerInitializer {
 3     @Override
 4     public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
 5             throws ServletException {
 6
 7         List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
 8
 9         if (webAppInitializerClasses != null) {
10             for (Class<?> waiClass : webAppInitializerClasses) {
11                 // Be defensive: Some servlet containers provide us with invalid classes,
12                 // no matter what @HandlesTypes says...
13                 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
14                         WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
15                     try {
16                         initializers.add((WebApplicationInitializer) waiClass.newInstance());
17                     }
18                     catch (Throwable ex) {
19                         throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
20                     }
21                 }
22             }
23         }
24
25         if (initializers.isEmpty()) {
26             servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
27             return;
28         }
29
30         servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
31         AnnotationAwareOrderComparator.sort(initializers);
32         for (WebApplicationInitializer initializer : initializers) {
33             initializer.onStartup(servletContext);
34         }
35     }
36
37 }

  在最后的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的机制。

时间: 2024-10-10 06:40:33

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

spring注解配置quartz

常规配置quartz可以参考我的另外一篇博文:http://www.cnblogs.com/yangzhilong/p/3349116.html spring配置文件里增加: 命令空间: http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd 配置: <task:annotation-driven/> 当然这还需要扫描注解等常规配置. ja

spring注解配置quartz应用

项目中会经常用到定时器,因此,其quartz的使用,我们必须要掌握.下面就以例子来讲解如何在spring中整合quartz, 使用注解配置的方式来实现定时执行任务. 一.引入jar包 项目中需引入quartz的jar包,由于整合到spring中,肯定也引入了spring的相关jar包. 例子中引用的是quartz-2.1.1版本,使用maven管理项目,pom文件需引入quartz依赖 二.spring配置文件中配置 (applicationContext.xml) 1)      xmlns和

关于Spring注解配置的步骤

今天分享一下 关于Spring注解配置的流程 1 导包:如下图所示 2 书写User和Car类  代码如下 package cn.lijun.bean; public class Car { private String name; private String color; public String getName() { return name; } public void setName(String name) { this.name = name; } public String g

0808 Spring 注解配置

 summarize 注解配置将替换手动 Spring 注解配置 导包 配置约束 eclipse导入spring配置文件约束.可能你觉得这种配置文件网上一大堆,直接复制粘贴就搞定了,可是假如你去的是某些机密单位呢?所以多学点总归是好的!这篇文章是本人全部手工写出来的,没有盗用其他人的图片和文字信息等,转载请注明出处,谢谢! 点击window选择Preferences选项,搜索catalog 点击add 选中file System找自己下载好的spring的解压包下的schema文件夹,点进去选择

spring容器的启动过程(1)

容器启动过程总体流程 public void refresh() throws BeansException, IllegalStateException { //容器在启动之前要获得对象锁,保证容器只有一个启动synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the inter

SpringBoot:Spring容器的启动过程

一.简述 Spring的启动过程就是IoC容器的启动过程,本质上就是创建和初始化Bean的工厂(BeanFactory),BeanFactory是整个SpringIoC的核心,Spring使用BeanFactory来实例化.配置和管理Bean. 二.SpringBoot的启动过程 在SpringBoot中,SpringApplication封装了一套Spring应用的启动流程,对用户完全是透明的,这个类原本在Spring中是没有的. 一般来说,默认的SpringApplication执行流程可以

Spring 注解配置原理

声明 源码基于Spring 5.0.8 1. 简介 自从Spring Boot流行,基于注解的配置逐渐取代了XML配置.因为突然而来的兴趣从而阅读了Spring 对Configuration注解的解析流程. 2. 原理介绍 解析@Configuration的入口是ConfigurationClassPostProcessor.ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,使得Spring在初始过程中

Spring -- 注解配置Bean

通过注解配置Bean 特定组件包括: @Component: 基本注解, 标识了一个受 Spring 管理的组件 @Respository: 标识持久层组件 @Service: 标识服务层(业务层)组件 @Controller: 标识表现层组件 上面的组件可以混用,因为IOC容器并无法区分当前类是否为业务.持久.还是表现层. 对于扫描到的组件, Spring 有默认的命名策略: 使用非限定类名, 第一个字母小写. 也可以在注解中通过 value 属性值标识组件的名称 使用注解配置Bean前,我们

5.spring:注解配置 Bean

在classpath中扫描组件 组键扫描:能够从classpath下自动扫描,侦测和实例化具有特定注解的组件 特定的组件包括: ->@Componment:基于注解,标识一个受Spring管理的组键 ->@Respository:标识持久层组件 ->@Service:标识服务层 ->@controller:标识表现层组件 对于扫描到的组件,Spring有默认的命名策略,使用非限定类名,第一个字母小写,也可以通过注解中value属性值标识组建的名称 在classpath中扫描组键当在