SpringBoot注解原理&启动流程

523 阅读6分钟

Spring Boot项目,都会用到如下的启动类

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@SpringBootApplication是springboot的基本注解,是写在springboot的启动类上的注解,目的是开启springboot的自动配置。

1 @SpringBootApplication

@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication

@SpringBootApplication看作是下列注解的集合

  • @SpringBootConfiguration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类
  • @ComponentScan:扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类
  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制

1.1 @SpringBootConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {...}

任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类 @Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。

@Configuration
public class MockConfiguration{
    //bean定义
}

1.2 @ComponentScan

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};
    ...
    }

@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描(SpringBoot的启动类最好是放在root package下)。

1.3 @EnableAutoConfiguration

@EnableAutoConfiguration定义

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}

定义中有两个注解

  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)

1.3.1 @AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {...}

@AutoConfigurationPackage中使用注解@Import导入了AutoConfigurationPackages.Registrar.class到容器中。

@AutoConfigurationPackage就是添加该注解的类所在的包作为自动配置包进行管理。实现上就是依赖于工具类AutoConfigurationPackages中的内部类Registrar对所标注的包进行注册

1.3.2 AutoConfigurationImportSelector.class

作用

AutoConfigurationImportSelector的作用是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置类通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总加载到IoC容器。
注意 这些配置类一般使用了@ConditionalOnClass,需满足一定的条件才会激活配置。

源码分析
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

   ...
   @Override
   public String[] selectImports(AnnotationMetadata annotationMetadata) {
      if (!isEnabled(annotationMetadata)) {
         return NO_IMPORTS;
      }
      AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
      // 所返回的字符串数组为 所有的将要被导入的类的全类名
      return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
   }
      ...  }     

getAutoConfigurationEntry方法调用了getCandidateConfigurations方法, 在调用SpringFactoriesLoader.loadFactoryNames

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}


public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

SpringFactoriesLoader.loadFactoryNames方法调用loadSpringFactories方法从所有的jar包中读取META-INF/spring.factories文件信息。

    //这个方法返回的为Map<String, List<String>> 变量result。
    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
       //从全局变量cache中以classLoader为key获取所对应的value
        Map<String, List<String>> result = cache.get(classLoader);
       //判断在cache中是否存在相对应的value,如果已经存在则直接返回对应的result
        if (result != null) {
          return result;
       }
        //至此,已经判断得出result为空,所以实例化一个新的HashMap
       result = new HashMap<>();
       try {
           //从传入的类加载器中获取资源,路径为"META-INF/spring.factories"
          Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
           //将获取到的urls进行遍历
          while (urls.hasMoreElements()) {
             URL url = urls.nextElement();
              //获取资源的url在当前项目中的位置等
             UrlResource resource = new UrlResource(url);
              //通过配置类加载工具类加载配置类,获取所有配置
             Properties properties = PropertiesLoaderUtils.loadProperties(resource);
              //遍历所有配置实体
             for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                 String[] factoryImplementationNames =
                      StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                //遍历当前配置实体中的所有value,如果不存在当前key,则将当前key以及下面遍历所得到的value一起添加到result中,如果存在则将该value添加到所对应的key下面。
                 for (String factoryImplementationName : factoryImplementationNames) {
                   result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                         .add(factoryImplementationName.trim());
                }
             }
          }

          // Replace all lists with unmodifiable lists containing unique elements(译文:用包含唯一元素的不可修改列表替换所有列表)
          result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
          //将该classLoader以及对应的result添加到cache中
           cache.put(classLoader, result);
       }
       catch (IOException ex) {
          throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
       }
        //至此,返回了META-INF/spring.factories中的所有的配置
       return result;
    }
应用

下面是spring-boot-autoconfigure这个jar中spring.factories文件部分内容

image.png

1.4 流程图

image.png

2 启动流程

2.1 创建SpringApplication实例

启动类调的run方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
   return run(new Class<?>[] { primarySource }, args);
}


public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

SpringApplication 构造器

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   //启动类传入的Class
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   //判断当前项目类型,有三种:NONE、SERVLET、REACTIVE
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   //设置ApplicationContextInitializer
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   //设置监听器
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   //判断主类,初始化入口类
   this.mainApplicationClass = deduceMainApplicationClass();
}

2.2 run方法调用

2.2.1 run方法主要流程

public ConfigurableApplicationContext run(String... args) {
   //创建计时器,开始计时
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   //定义上下文对象
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   //Headless模式设置
   configureHeadlessProperty();
   //加载SpringApplicationRunListeners监听器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //发送ApplicationStartingEvent事件
   listeners.starting();
   try {
      //封装ApplicationArguments对象
     // args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=8000
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      
      //配置环境

  // 创建并配置当前SpringBoot应用将要使用的Environment
  // 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      //根据环境信息配置要忽略的bean信息
      configureIgnoreBeanInfo(environment);
      //打印Banner标志
      Banner printedBanner = printBanner(environment);
      
      //创建Spring容器
      context = createApplicationContext();
      
      //加载SpringBootExceptionReporter
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
            
      //容器初始化:包含一个非常关键的操作,将启动类注入容器,为后续开启自动化配置奠定基础。
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      //刷新上下文:此方法用于解析配置文件,加载 bean 对象,并且启动内置的 web 容器等操作
      refreshContext(context);
      //刷新后的操作,由子类去扩展
      afterRefresh(context, applicationArguments);
      //计时结束
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      //发送ApplicationStartedEvent事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
      listeners.started(context);
      //查找容器中注册有CommandLineRunner或者ApplicationRunner的bean,遍历并执行run方法
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
   //发送ApplicationReadyEvent事件,触发所有的 SpringApplicationRunListener 监听器的 running 事件
      listeners.running(context);
   }
   catch (Throwable ex) {
   //报告异常,但是不发送任何事件
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

2.2.2 上下文初始化prepareContext

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   //设置容器环境,包括各种变量
   context.setEnvironment(environment);
   
   //设置上下文的 bean 生成器和资源加载器
   //如果application有设置beanNameGenerator、resourceLoader就将其注入到上下文中,并将转换工具也注入到上下文中
   postProcessApplicationContext(context);
   

 //执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
   applyInitializers(context);
   
   //发布ApplicationContextInitializedEvent事件
   //触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件
   listeners.contextPrepared(context);
   
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   
   // Load the sources
   // 这里获取到的是BootstrapImportSelectorConfiguration这个class,而不是自己写的启动类,这个class是在之前注册的BootstrapApplicationListener的监听方法中注入的
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
 
  //加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
   load(context, sources.toArray(new Object[0]));
   
   //发布ApplicationPreparedEvent事件
  //触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
   listeners.contextLoaded(context);
}

2.2.3 刷新上下文refresh

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 第一步 刷新前的预处理
      //记录启动时间、状态,web容器初始化其property,复制listener
      prepareRefresh();

     // 第二步 
     // 1)创建BeanFactory实例,默认实现是DefaultListableBeanFactory
     // 2)解析XML中的 <bean> 为BeanDefition并注册到 BeanDefitionRegistry
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
       // 第三步 BeanFactory的预准备⼯作(
       //beanFactory注入一些标准组件,例如ApplicationContextAwareProcessor,ClassLoader等)
      prepareBeanFactory(beanFactory);

      try {
         //第四步 BeanFactory准备工作完成后的后置处理工作
        // 给实现类留的一个钩子,例如注入BeanPostProcessors,这里是个空方法
         postProcessBeanFactory(beanFactory);

         // 第五步 实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
         invokeBeanFactoryPostProcessors(beanFactory);

         // 第六步 注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
         registerBeanPostProcessors(beanFactory);

         // 第七步 初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
         initMessageSource();

         // 第八步 初始化事件派发器
         // bean工厂注册一个key为applicationEventMulticaster的广播器
         initApplicationEventMulticaster();

        // 第九步 ⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑,钩子方法,这里是个空方法
         onRefresh();

         // 第十步 注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
         registerListeners();

         // 第十一步 初始化所有剩下的⾮懒加载的单例bean
         //1)初始化创建⾮懒加载⽅式的单例*Bean*实例(未设置属性)
         //2)填充属性
         //3) 如果bean实现了Aware相关接口 ,则调用Aware接口的实现方法
         //4) 调用 BeanPostProcessor处理器的前置方法
         //5)初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
         //6)调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
         finishBeanFactoryInitialization(beanFactory);

         // 第十二步 完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件ContextRefreshedEvent
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

其中,第五步 实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean 是和SpringBoot自动装配相关的步骤。

就是在这一步解析的@SpringBootApplication这个组合注解,BeanFactoryPostProcessor比较重要的一个实现类ConfigurationClassPostProcessor在这里调用,用来遍历BeanDefinitionRegistry中现有的BeanDefinition,解析@Import、@Configuration 、@ComponentScan等注解,将注解覆盖到的类也注册到BeanDefinitionRegistry中