详解spring注解配置启动过程_java

       最近看起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注解
spring4注解配置详解、spring注解详解、springmvc注解详解、springboot注解详解、spring常用注解详解,以便于您获取更多的相关知识。

时间: 2024-10-04 07:45:07

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

详解spring与jdbc整合操作_java

先上一段简单示例 public class MyTemplate { private DataSource dataSource; public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void insert(String sql) throws SQLExc

详解linux系统的启动过程及系统初始化

一.linux系统的启动流程 关于linux系统的启动流程我们可以按步进行划分为如下: POST加电自检-->BIOS(Boot Sequence)-->加载对应引导上的MBR(bootloader)-->主引导设置加载其BootLoader-->Kernel初始化-->initrd->/etc/init进程加载/etc/inittab,其进程流程图如下:

详解Spring事务Transaction配置的五种注入方式

前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识.通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的. 总结如下: Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource.TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分. DataSource.TransactionManager这两部分只是会根据数据访问方式有所变化,

垃圾收集器详解及参数配置

垃圾收集器详解及参数配置 垃圾搜集器简介 垃圾搜集器大致分为以下三类 串行搜集器(serial collector):它只有一条GC线程,且就像前面说的,它在运行的时候需要暂停用户程序(stop the world). 并行搜集器(parallel collector):它有多条GC线程,且它也需要暂停用户程序(stop the world). 并发搜集器(concurrent collector):它有一条或多条GC线程,且它需要在部分阶段暂停用户程序(stop the world),部分阶段

图文详解Win8.1 Update启动失败问题

  图文详解Win8.1 Update启动失败问题: 要解决windows8.1 update无法启动问题,其实只要使用Win8.1安装镜像+应急命令来修复,例如最经典的sfc /scannow命令.该修复命令可以扫描所有保护的系统文件的完整性,同时使用正确的微软版本替换受损或者异常的版本文件. 具体操作步骤: 1.下载Win8.1系统镜像,刻录DVD安装光盘或者制作U盘启动工具; 下载ISO后,可以用魔方电脑大师中的魔方文件校验来获取SHA1.MD5.CRC等校验值,确保下载的是官方纯净版,不

详解Spring配置事务的五种方式_java

Spring配置文件中关于事务配置总是由三个组成部分,分别是 DataSource .TransactionManager  和 代理机制 这三部分,无论哪种配置方式,一般变化的只是代理机制这部分. DataSource.TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager. 具

详解spring开发_JDBC操作MySQL数据库_java

本文介绍spring开发_JDBC操作MySQL数据库,具体如下: 项目结构: 数据库表: /spring_1100_spring+jdbc/src/com/b510/bean/Person.java package com.b510.bean; /** * 普通的javaBean类Person * * @author Hongten * */ public class Person { /** * id号 */ private int id; /** * 姓名 */ private Strin

详解Java注解教程及自定义注解_java

Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许多java框架中大量使用注解,如Hibernate.Jersey.Spring.注解作为程序的元数据嵌入到程序当中.注解可以被一些解析工具或者是编译工具进行解析.我们也可以声明注解在编译过程或执行时产生作用. 在使用注解之前,程序源数据只是通过java注释和javadoc,但是注解提供的功能要远远超

详解Spring连接数据库的几种常用的方式_java

本文简单的讲解使用Spring连接数据库的几种常用方法: 测试主类为: package myspring2; import java.sql.*; import javax.sql.DataSource; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MySp