美文网首页
spring boot

spring boot

作者: lj72808up | 来源:发表于2021-05-18 19:27 被阅读0次

    1. spring boot 启动代码

    首先, 看一段 spring boot 的启动代码如下. 发现启动分为2大部分:

    • SpringApplication.run() 容器启动
    • @SpringBootApplication 注解
    // (1) 标明是 spring boot 应用, 开启自动配置
    @SpringBootApplication   
    public class Example {
        public static void main(String[] args) {
            // (2) SpringApplication.run() xxx启动 spring boot 应用
            ConfigurableApplicationContext context = SpringApplication.run(Example.class);
        }
    }
    

    2. 关于 SpringApplication.run() 容器启动

    看到使用 SpringApplication.run(Class<?>[] primarySources) 这句话启动应用. 内部其实返回了

    return new SpringApplication(primarySources).run(args);
    

    这个方法的关键在于两点:

    1. new SpringApplication(primarySources) 构造器
      public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
          // resourceLoader 是 spring framework 的内容, 参看 spring framework
          this.resourceLoader = resourceLoader;
          Assert.notNull(primarySources, "PrimarySources must not be null");
          this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));   // java config 数组
          //(1) 判断是否是 web 应用. 返回 (REACTIVE, NONE, SERVLET) 类型.  内部就是用 try{Class.forName()}, 看几个 class 是否存在
          this.webApplicationType = WebApplicationType.deduceFromClasspath();
          // (2)
          setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
          // (3)
          setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
          // (4)
          this.mainApplicationClass = deduceMainApplicationClass();
      }
      
      private ResourceLoader resourceLoader;  // spring 资源加载器
      private Set<Class<?>> primarySources;   // java config 数组
      private WebApplicationType webApplicationType;   // web 类型
      
      
      private List<ApplicationContextInitializer<?>> initializers;
      private List<ApplicationListener<?>> listeners;
      
      • (2),(3) 两步用到了 getSpringFactoriesInstances 方法来初始化字节的 initializers 属性和 listener 属性. 该方法如下:
        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = getClassLoader();
            // 解析 'META_INF/spring.factories' 中的配置, 该文件每行是一个 key-value 对; key: 工厂类名, value: 工厂实现类的类名
            // 将 factoryName 和 fatoryImplementationName 解析出来
            Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            // 内部用反射 constructor 构造对象
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
        
        因此, (2),(3)两步就是反射构造函数去实例化 META_INF/spring.factories 文件中配置的2组类 ApplicationContextInitializerApplicationListener. 文件内容为:
        # Initializers
        org.springframework.context.ApplicationContextInitializer=\
        org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
        org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
        
        # Application Listeners
        org.springframework.context.ApplicationListener=\
        org.springframework.boot.autoconfigure.BackgroundPreinitializer
        
        [说明]: META_INF/spring.factories 文件是 spring-boot 自动配置的关键
        以 mybatis 为例, 和 spring-boot 集成时, 会引入一个 mybatis-spring-boot-autoconfigure.jar 包. 最重要的是在 META_INF/spring.factories中定义了一些列工厂类, 这些类本身作为 bean, 又可以加载其它 bean. 像 mybatis 的自动配置入口就注册了 sqlSessionFactorysqlSessionTemplate:
        @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
        class MybatisAutoConfiguration implements InitializingBean{
         @Bean
         @ConditionalOnMissingBean
         public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
           return new SqlSessionTemplate(sqlSessionFactory);
         }
        }
        
      • 第(4)步: deduceMainApplicationClass() 是尝试获取执行 main 方法的类. 没什么用, 主要用来打日志
        // 通过构造一个 RuntimeException, 查找 main 方法的调用栈来决定启动类是什么
        private Class<?> deduceMainApplicationClass() {
                try {
                    StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
                    for (StackTraceElement stackTraceElement : stackTrace) {
                        // 调用栈发现 main 方法, 获取主类
                        if ("main".equals(stackTraceElement.getMethodName())) {
                            return Class.forName(stackTraceElement.getClassName());
                        }
                    }
                }
                catch (ClassNotFoundException ex) {
                    // Swallow and continue
                }
                return null;
            }
        
    2. SpringApplication 的 run 方法
      该方法逻辑较多
      public ConfigurableApplicationContext run(String... args) {
          ////////////////////////////////////
          //StopWatch 主要用来统计 run() 方法的启动时长. 后面有掉用 stopWatch.stop() 方法
          StopWatch stopWatch = new StopWatch();
          stopWatch.start();
          ////////////////////////////////////
          ConfigurableApplicationContext context = null;
          Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
          // 设置了 java.awt.headless 环境变量, 和 awt 有关, 可以无视
          configureHeadlessProperty();
          ////////////////////////////////////
          // 反射构造器, 实例化 spring.factories 中的 SpringApplicationRunListener 实现类并启动
          SpringApplicationRunListeners listeners = getRunListeners(args);
          listeners.starting();
          ////////////////////////////////////
          try {
              ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
              ////////////////////////////////////
              // (1) prepareEnvironment 加载配置变量. 该方法, 根据内部会根据 spring-boot 配置的 profile 加载配置.
              //     生成 PropertySource 和 Environment. 这两个类时干什么的, 参见 spring-framework
              ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
              configureIgnoreBeanInfo(environment);
              ////////////////////////////////////
              // (2) 打印 banner
              Banner printedBanner = printBanner(environment);
              // (3) 创建 ApplicaitonContext. 详见下方
              context = createApplicationContext();
              // (4) 对 spring.factories 中记录的配置的 SpringBootExceptionReporter 实现类
              //     org.springframework.boot.diagnostics.FailureAnalyzers 做初始化
              exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                      new Class[] { ConfigurableApplicationContext.class }, context);
              // (5) 见下面
              prepareContext(context, environment, listeners, applicationArguments, printedBanner);
              // (6) 内部就是替我们手动执行了 applicationContext.refresh()
              refreshContext(context);
              afterRefresh(context, applicationArguments);
              stopWatch.stop();
              if (this.logStartupInfo) {
                  new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
              }
              // 同下(8)
              listeners.started(context);
              // (7) 执行所有类型为 `ApplicationRunner` 和 `CommandLineRunner` 的 bean 的 run() 方法
              callRunners(context, applicationArguments);
          }
          catch (Throwable ex) {
              handleRunFailure(context, ex, exceptionReporters, listeners);
              throw new IllegalStateException(ex);
          }
      
          try {
              // (8) 有关 SpringApplicationRunListeners 
              listeners.running(context);
          }
          catch (Throwable ex) {
              handleRunFailure(context, ex, exceptionReporters, null);
              throw new IllegalStateException(ex);
          }
          return context;
      }
      
    • (1)处, prepareEnvironment 加载配置变量
      private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                  ApplicationArguments applicationArguments) {
              // Create and configure the environment
              ConfigurableEnvironment environment = getOrCreateEnvironment();
              configureEnvironment(environment, applicationArguments.getSourceArgs());
              ConfigurationPropertySources.attach(environment);
              listeners.environmentPrepared(environment);
              bindToSpringApplication(environment);
              if (!this.isCustomEnvironment) {
                  environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                          deduceEnvironmentClass());
              }
              ConfigurationPropertySources.attach(environment);
              return environment;
          }
      
    • (2)处, 打印的 banner 如下
        .   ____          _            __ _ _
       /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
      ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
       \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
        '  |____| .__|_| |_|_| |_\__, | / / / /
       =========|_|==============|___/=/_/_/_/
       :: Spring Boot ::
      
    • (3) createApplicationContext 创建 ConfigurableApplicationContext
      该方法根据判断出的 web 类型(普通web, webserver等)通过反射构造器, 创建 ApplicationContext 对象. 相当于帮你手写了 new AnnotationConfigApplicationContext() 初始化容器
      protected ConfigurableApplicationContext createApplicationContext() {
          // 根据 webApplicationType 类型,获得 ApplicationContext 类型
          Class<?> contextClass = this.applicationContextClass;
          if (contextClass == null) {
              try {
                  switch (this.webApplicationType) {
                  case SERVLET:
                      contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                      break;
                  case REACTIVE:
                      contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                      break;
                  default:
                      contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                  }
              } catch (ClassNotFoundException ex) {
                  throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex);
              }
          }
          // 创建 ApplicationContext 对象
          return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
      }
      
    • (5) prepareContext 准备 ApplicationContext 对象,主要是初始化它的一些属性。
      private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                  SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
          // (1) 给 applicationContext 设置配的变量
          context.setEnvironment(environment);
          postProcessApplicationContext(context);
          // (2) 缓存 ApplicationContextInitializer
          applyInitializers(context);
          listeners.contextPrepared(context);
          if (this.logStartupInfo) {
              logStartupInfo(context.getParent() == null);
              logStartupProfileInfo(context);
          }
          ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
          //(3) 在 applicationContexdt 的 BeanFactory 中缓存启动参数. 名称为 "springApplicationArguments"
          //    内部是缓存在 SingletonBeanRegistry 的 LinkedHashSet 数据结构中
          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());
          }
          Set<Object> sources = getAllSources();
          Assert.notEmpty(sources, "Sources must not be empty");
      
          // (4) 加载一系列的 BeanDefinition. 具体如下方法
          load(context, sources.toArray(new Object[0]));
          listeners.contextLoaded(context);
      }
      // prepareContext() 的第(4)步骤: 分析怎么加载的 BeanDefinition
      protected void load(ApplicationContext context, Object[] sources) {
          if (logger.isDebugEnabled()) {
              logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
          }
          // (1) 创建 BeanDefinitionRegistry 对象. 具体什么是 BeanDefinitionRegistry 和 BeanDefinition, 参见 spring framework
          BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
          if (this.beanNameGenerator != null) {
              loader.setBeanNameGenerator(this.beanNameGenerator);
          }
          if (this.resourceLoader != null) {
              loader.setResourceLoader(this.resourceLoader);
          }
          if (this.environment != null) {
              loader.setEnvironment(this.environment);
          }
          // 执行 BeanDefinition 加载. 具体加载步骤参见 spring-framework
          loader.load();
      }
      
    • (8) 是 SpringApplicationRunListeners 相关处理, 包括 started()run() 方法. 参看事件设计模式

    3. @SpringBootApplication 注解开启自动配置

    自动配置和自动装配不同. 自动装配指 spring bean 的生成, 自动配置是 spring-boot 的主要动能. 首先先看看该注解的定义

    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication 
    

    下面逐步分析 @SpringBootApplication 上的每个注解

    1. @Inherited
      该注解是 java 自带注解, 其含义是: 当用 @Inherited 实现了自定义注解, 如果在 class 上使用自定义注解, 则继承该 class 的子类自动被标记了自定义注解

    2. @SpringBootConfiguration
      spring boot 的注解. 看到定义时用到了 @Configuration, 因此两者含义一致, 表示该类作为 beans.xml 的替代者来声明 bean

      @Configuration
      public @interface SpringBootConfiguration {}
      
    3. @ComponentScan
      spring 注解, 自动扫描包下@Componment@Configuration@Service

    1. @EnableAutoConfiguration
      spring boot 实现自动配置的核心注解
      @AutoConfigurationPackage
      @Import(AutoConfigurationImportSelector.class)
      public @interface EnableAutoConfiguration {
      
      • @AutoConfigurationPackage 注解

        @Import(AutoConfigurationPackages.Registrar.class)
        public @interface AutoConfigurationPackage
        
      • @Import

        1. 该注解的功能

          • (1) @Import(OtherConfiguration.class): 导入其它 @Configuration 声明的类, 相当于 beans.xml 中 <import> 标签的代替
          • (2) @Import(OtherImportSelector.class): 导入 ImportSelector 中代码定义的类名
            package com.test;
            class ServiceImportSelector implements ImportSelector {
                @Override
                public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                    //可以是@Configuration注解修饰的类,也可以是具体的Bean类的全限定名称
                    return new String[]{"com.test.ConfigB"};
                }
            }
            
            @Import(ServiceImportSelector.class)
            @Configuration
            class ConfigA {
                @Bean
                @ConditionalOnMissingBean
                public ServiceInterface getServiceA() {
                    return new ServiceA();
                }
            }
            
          • (3) @Import(ImportBeanDefinitionRegistrar.class): 用于在导入 bean 时, 重新定义或更改 bean. 例如动态注入属性,改变Bean的类型和Scope等等
            // 定义ServiceC
            package com.test;
            class ServiceC implements ServiceInterface {
            
                private final String name;
            
                ServiceC(String name) {
                    this.name = name;
                }
            
                @Override
                public void test() {
                    System.out.println(name);
                }
            }
            
            // 定义ServiceImportBeanDefinitionRegistrar动态注册ServiceC,修改EnableService
            package com.test;
            
            @Retention(RetentionPolicy.RUNTIME)
            @Documented
            @Target(ElementType.TYPE)
            @Import(ServiceImportBeanDefinitionRegistrar.class)
            @interface EnableService {
                String name();
            }
            
            class ServiceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
                @Override
                public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                    Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
                    String name = (String) map.get("name");
                    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(ServiceC.class)
                            //增加构造参数
                            .addConstructorArgValue(name);
                    //注册Bean
                    registry.registerBeanDefinition("serviceC", beanDefinitionBuilder.getBeanDefinition());
                }
            }
            
            // 使用 @Import 注解 
            package com.test;
            @EnableService(name = "TestServiceC")
            @Configuration
            class ConfigA {}     // 会自动生成
            
            // main 函数 
            public static void main(String[] args) {
                ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigA.class);
                ServiceInterface bean = ctx.getBean(ServiceInterface.class);
                bean.test();
            }
            

          [注]: @Import 注解的解析, 参考 https://zhuanlan.zhihu.com/p/147025312

        2. @Import(AutoConfigurationImportSelector.class) 是重头戏的开始, 如后文

    4. @Import(AutoConfigurationImportSelector.class) 自动配置重头戏

    AutoConfigurationImportSelector 类定义如下. 可以发现它除了实现几个Aware类接口外,最关键的就是实现了 DeferredImportSelector (继承自ImportSelector)接口 https://blog.csdn.net/dm_vincent/article/details/77619752

    class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
            ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered{
    
    }
    
    1. 该类中的 getCandidateConfigurations 获取配置类数组

      1. 函数展示

        // AutoConfigurationImportSelector.java
        
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            // <1> 加载指定类型 EnableAutoConfiguration 对应的,在 `META-INF/spring.factories` 里的类名的数组
            // getSpringFactoriesLoaderFactoryClass(): return EnableAutoConfiguration.class;
            // SpringFactoriesLoader.loadFactoryNames():  在 `META-INF/spring.factories` 里的类名的数组
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
            return configurations;
        }
        
      2. getCandidateConfigurations 方法如何被调用的

        • (1)处: 在上述分析 .run() 方法时, 第(6)步, 替我们手动执行了 applicationContext.refresh()
        • (3)处:
        public Iterable<Group.Entry> getImports() {
            for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
                // <1> 处理被 @Import 注解的注解
                // this.group 由 getImportGroup() 方法返回, 具体在"该类的 `getImportGroup()`"章节分析
                this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                        deferredImport.getImportSelector());
            }
            // <2> 选择需要导入的
            return this.group.selectImports();
        }
        
    2. 该类的 getImportGroup()

      1. 函数展示
      // AutoConfigurationImportSelector.java
      
      @Override    // 实现自 DeferredImportSelector 接口
      public Class<? extends Group> getImportGroup() {
          return AutoConfigurationGroup.class;
      }
      

    相关文章

      网友评论

          本文标题:spring boot

          本文链接:https://www.haomeiwen.com/subject/tmuyjltx.html