Spring Boot 启动过程和自动装配

书籍引用: 《 SpringBoot 揭秘 快速构建微服务体系》 王福强 著

在线阅读

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.5.RELEASE</version>
    <relativePath/> 
</parent>

启动类 - 标准结构

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

第一次分解 注解分解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
        excludeFilters = {@ComponentScan.Filter(
                type = FilterType.CUSTOM,
                classes = {TypeExcludeFilter.class}
        ), @ComponentScan.Filter(
                type = FilterType.CUSTOM,
                classes = {AutoConfigurationExcludeFilter.class}
        )}
)
public class SpringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

第二次分解 类分解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
        excludeFilters = {@ComponentScan.Filter(
                type = FilterType.CUSTOM,
                classes = {TypeExcludeFilter.class}
        ), @ComponentScan.Filter(
                type = FilterType.CUSTOM,
                classes = {AutoConfigurationExcludeFilter.class}
        )}
)
public class SpringbootApplication {
}
public class MySpringbootRun {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

依据分解:启动类 MySpringbootRun 就是一个标准的 Standalone 类型Java程序的main函数启动类,没有什么特别的东西。

这是一种化繁为简的做法,springboot一站式复合Annotation显然更加方便

最上层复合注解

@SpringBootApplication

它的下层关键注解有三个

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

SpringBootConfiguration

@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@SpringBootConfiguration 是 @Configuration的上层注解

这里的@Configuration 并不陌生,它就是JavaConfig形式的 Spring IoC容器的配置类使用的@Configuration,SpringBoot 内在就是一个Spring应用,必然的需要加载某个IoC容器的配置,这里的启动类标注了@Configuration 之后,本身就是一个IoC容器的配置类,观察第二次分解的结构。


EnableAutoConfiguration

@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

在Spring框架中有各种@Enable开头的Annotation定义,比如那些@EnableScheduling、@EnableCaching等。

@EnableAutoConfiguration的理念和做事方式也是一脉相承的,简单概括一下就是,借助@Import 的支持,收集和注册特定场景相关的bean的定义:

  • @EnableScheduling 是通过 @Import 将Spring调度框架相关的bean定义加载到 IoC 容器。
  • @EnableMBeanExport 是借助 @Import 将JMX 相关的bean定义加载到 IoC 容器。

而@EnableAutoConfiguration 也是借助了 @Import 的帮助,将所有复合自动配置条件的bean定义加载到 IoC 容器,仅此而已!

@Import({AutoConfigurationImportSelector.class})

@EnableAutoConfiguration 借助AutoConfigurationImportSelector.class 可以帮助SpringBoot应用将所有符合条件的 @Configuration 配置都加载到当前SpringBoot创建并使用的IoC容器,就像是一个积木一样,把需要的零件拼接进来,不需要的排除在外。

自动配置的关键 是Spring框架原有的一个工具类 SpringFactoriesLoader

import org.springframework.core.io.support.SpringFactoriesLoader;

SpringFactoriesLoader 属于Spring 框架私有的一种扩展方案(类似于Java的SPI方案 Java.util.ServiceLoader),其主要功能就是从指定的配置文件 META-INF/spring.factories 加载配置,spring.factories 是一个典型的java properties 文件,配置格式为 Key = Value 形式, Key 和 Value 都是Java类型的完整类名。然后框架就可以根据某个类型作为Key来查找对应的类型名称列表了。

META-INF/spring.factories 配置文件

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
..................

AutoConfigurationImportSelector 自动配置导入选择器

public class AutoConfigurationImportSelector//......
//......
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }
//......

SpringFactoriesLoader 工具类

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//......
    public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader)
    //......
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader 
    //......
    

对于@EnableAutoConfiguration 来说,SpringFactoriesLoader 的用途稍有不同一些,本意是为了提供 SPI 扩展的场景,而在 @EnableAutoConfiguration 的场景中,它更多提供一种配置查找的功能支持,根据org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为查找的Key,获取对应的一组 @Configuration类。

因此,@EnableAutoConfiguration 自动配置的魔法实其实就是从classpath 中搜寻所有META-INF/spring.factories配置文件,并将其中EnableAutoConfiguration 对应的配置项通过反射(Java Reflection)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总加载到 IoC 容器中。

ComponentScan

可有可无的 ComponentScan !原则上来说,作为Spring框架里的老一辈革命家,@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件或Bean定义,最终将这些bean定义加载到容器中。加载bean定义到Spring的IoC容器,我们可以手工单个注册,不一定非要通过批量的自动扫描完成,所以说@ComponentScan是可有可无的。

SpringApplication:SpringBoot 程序启动的一站式解决方案

SpringApplication 将一个典型的Spring 应用启动的流程”模板化“(这里是动词),在没有特殊需求的情况下,默认的执行流程就可以满足需求;但有特殊需求的情况下,SpringApplication 在合适的流程节点开放了一系列不同类型的扩展点,我们可以通过这些扩展点对SpringBoot 程序的启动和关闭过程进行扩展。

SpringApplication 执行流程

SpringApplication 的 run 方法的实现是主要的执行线路,该方法的主要流程大体有以下几点:

  1. 如果我们使用的是 SpringApplication的静态run方法,这个方法里面首先需要创建一个SpringApplication对象实例,然后调用这个创建好的 SpringApplication 的实例run方法。在 SpringApplication 实例化时提前做几件事:

    • 根据classpath 里面是否存在某个特征类org.springframework.web.context.WebApplicationContext来决定是否应该创建一个为WEB 应用所使用的 ApplicationContext类型,还是创建一个标准Standalone应用使用的类型。

    • 使用 SpringFactoriesLoader 在应用的 classpath中查找并加载所有可用的 ApplicationContextInitializer

    • 使用 SpringFactoriesLoader 在应用的 classpath中查找并加载所有可用的 ApplicationListener

    • 推断并设置main方法的定义类

    • # spring.factories
      # Application Context Initializers
      org.springframework.context.ApplicationContextInitializer=\
      org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
      org.springframework.boot.context.ContextIdApplicationContextInitializer,\
      org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
      org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
      org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
      
      # Application Listeners
      org.springframework.context.ApplicationListener=\
      org.springframework.boot.ClearCachesApplicationListener,\
      org.springframework.boot.builder.ParentContextCloserApplicationListener,\
      org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
      org.springframework.boot.context.FileEncodingApplicationListener,\
      org.springframework.boot.context.config.AnsiOutputApplicationListener,\
      ......
      
  2. SpringApplication 实例初始化完成并且完成设置后,开始执行 run 方法的逻辑了,方法执行开始,首先遍历执行所有通过 SpringFactoriesLoader 可以查找到并加载的 SpringApplicationRunListener, 调用它们的 started() 方法,告诉这些SpringApplicationRunListener ,“嘿,SpringBoot 应用要开始执行了"。

    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    
    public class EventPublishingRunListener //......
        //......
        public void started(ConfigurableApplicationContext context) {
            context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
            AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
        }
    
  3. 创建并配置当前 SpringBoot 应用将要使用的 Environment(包括配置要使用的 PropertySource 以及 Profile)

  4. 遍历调用所有 SpringApplicationRunListener 的 environmentPrepared() 的方法,告诉它们: "当前SpringBoot应用使用的 Environment 准备好了"!

  5. 如果 SpringApplication 的showBanner 属性被设置为 true;则打印banner,这一步不用关心。

    # application.properties  关闭打印banner
    spring.main.banner-mode=off
    
  6. 根据用户是否明确设置了 applicationContextClass 类型以及初始化阶段的推断结果,决定该为当前 SpringBoot 应用创建什么类型的 ApplicationContext 并创建完成,然后根据条件决定是否添加 ShutdownHook,决定是否使用自定义的 BeanNameGenerator, 决定是否使用自定义的 ResourceLoader,当然,最重要的,将之前准备好的 Environment 设置给创建好的 ApplicationContext 使用。

  7. ApplicationContext 创建好之后,SpringApplication 会再次借助 SpringFactoriesLoader,查找并加载 classpath 中所有可用的 ApplicationContextInitializer ,然后遍历调用这些类中的initialize(applicationContext) 方法对已经创建好的ApplicationContext 进一步的处理。

    # Application Context Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
    org.springframework.boot.context.ContextIdApplicationContextInitializer,\
    
    public class ConfigurationWarningsApplicationContextInitializer//......
            public void initialize(ConfigurableApplicationContext context) {
        //......
        }
    
    public class ContextIdApplicationContextInitializer//......
            public void initialize(ConfigurableApplicationContext applicationContext) {
        //......
        }
    

    都是对ApplicationContext 的进一步处理

  1. 遍历调用所有 SpringApplicationRunListenercontextPrepared 方法,通知它们:"SpringBoot应用使用的ApplicationContext 准备 好了!

    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    
public class EventPublishingRunListener//......
     public void contextPrepared(ConfigurableApplicationContext context) {
        this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
    }
  1. 最核心的一步 ,将之前通过 @EnableAutoConfiguration 获取的所有配置以及其他形式的 IoC 容器配置加载到已经准备完毕的 ApplicationContext. 这是核心的应用容器

  2. 遍历调用所有 SpringApplicationRunListenercontextLoaded 方法,告知所有 SpringApplicationRunListener ,ApplicationContext 装填 完毕!

    public class EventPublishingRunListener//......
        public void contextLoaded(ConfigurableApplicationContext context) {
            //......
            }
    
  3. 调用 ApplicationContext 的 refresh() 方法,完成 IoC 容器可用的最后一道工序。

    // interface
    public interface ConfigurableApplicationContext//......
          void refresh() throws BeansException, IllegalStateException;
    // AbstractApplicationContext implements ConfigurableApplicationContext
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext
        public void refresh() throws BeansException, IllegalStateException {
            synchronized(this.startupShutdownMonitor) {
              //......
            }
        }
    // package org.springframework.boot
    // SpringApplication
    public class SpringApplication //......
        protected void refresh(ApplicationContext applicationContext) {
            Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
            this.refresh((ConfigurableApplicationContext)applicationContext);
        }
        protected void refresh(ConfigurableApplicationContext applicationContext) {
            applicationContext.refresh();
        }
    
    
  4. 查找当前 ApplicationContext 中是否注册有 CommandLineRunner, 如果有,则遍历执行它们。

    package org.springframework.boot;
    @FunctionalInterface
    public interface CommandLineRunner {
        void run(String... args) throws Exception;
    }
    

    自定义一个类去继承它并打印输出

@Component
public class  MyCommand implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner Running");
    }
}

> 启动SpringBoot应用可以在控制台中看到输出
  1. 正常情况下,遍历执行 SpringApplicationRunListenerfinished() 方法,告知它们:“搞定”。(如果整个过程出现异常,则依然调用所有 finished() 方法,只不过这种情况下会将异常信息一并传入处理)

到这一个完整的 SpringBoot 应用启动完毕~!!

把所有流程压缩精简:

  1. 收集各种条件和回调接口 通告 started()
  2. 创建并准备 Environment 通告 environmentPrepared()
  3. 创建并初始化 ApplicationContext,设置Environment ,加载配置等
    1. 通告 contextPrepared()
    2. 通告 contextLoaded()
  4. 调用 ApplicationContext 的 refresh() 方法
    1. 执行 CommandLineRunner
    2. 最后通告 finished()

SpringApplicationRunListener

SpringApplicationRunListener 是一个只有 SpringBoot应用的main 方法执行过程中接收不同执行时点事件通知的监听者:

// package org.springframework.boot;
public interface SpringApplicationRunListener {
    default void starting() {
    }
    default void environmentPrepared(ConfigurableEnvironment environment) {
    }
    default void contextPrepared(ConfigurableApplicationContext context) {
    }
    default void contextLoaded(ConfigurableApplicationContext context) {
    }
    default void started(ConfigurableApplicationContext context) {
    }
    default void running(ConfigurableApplicationContext context) {
    }
    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }
}

基本没什么常见的场景需要自己实现一个 SpringApplicationRunListener ,即使 SpringBoot 默认只是实现了一个EventPublishingRunListener ,用于在 SpringBoot 启动的不同时点发布不同的应用事件类型(ApplicationEvent),如果有哪些 ApplicationListener 对这些应用事件感兴趣,则可以接收并处理。

假设 我们真的有场景需要自己定义一个 SpringApplicationRunListener 实现,任何一个实现类的构造方法需要有两个构造参数,一个构造参数是 SpringApplication ,另一个就是 args 参数列表的 String[]

public EventPublishingRunListener(SpringApplication application, String[] args) {

之后我们可以通过 SpringFactoriesLoader 立下的规矩,在META-INF/spring.factories 文件中修改配置

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.songlin.springboot.demo.DemoSpringApplicationRunListener

之后 SpringApplication 就会在运行的时候调用它

ApplicationListener

ApplicationListener 其实是老面孔,属于Spring 框架对Java 中实现的监听者模式的一种框架实现。

Spring中ApplicationListener的使用

背景

ApplicationListener是Spring事件机制的一部分,与抽象类ApplicationEvent类配合来完成ApplicationContext的事件机制。

如果容器中存在ApplicationListener的Bean,当ApplicationContext调用publishEvent方法时,对应的Bean会被触发。这一过程是典型的观察者模式的实现。

ApplicationListener源码

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);
}

ContextRefreshedEvent事件的监听

以Spring的内置事件ContextRefreshedEvent为例,当ApplicationContext被初始化或刷新时,会触发ContextRefreshedEvent事件,下面我们就实现一个ApplicationListener来监听此事件的发生。

@Component // 需对该类进行Bean的实例化
public class LearnListener implements ApplicationListener<ContextRefreshedEvent> {
   @Override
   public void onApplicationEvent(ContextRefreshedEvent event) {
      // 打印容器中出事Bean的数量
      System.out.println("监听器获得容器中初始化Bean数量:" + event.getApplicationContext().getBeanDefinitionCount());
   }
}

这便完成了一个事件监听类的实现和实例化

自定义事件及监听

首先自定义事件:NotifyEvent

public class NotifyEvent extends ApplicationEvent {

    private String email;
    private String content;
    
    public NotifyEvent(Object source) {
        super(source);
    }
    public NotifyEvent(Object source, String email, String content) {
        super(source);
        this.email = email;
        this.content = content;
    }
    // 省略getter/setter方法
}

定义监听器NotifyListener:

@Component
public class NotifyListener implements ApplicationListener<NotifyEvent> {

    @Override
    public void onApplicationEvent(NotifyEvent event) {
        System.out.println("邮件地址:" + event.getEmail());
        System.out.println("邮件内容:" + event.getContent());
    }
}

监听器通过@Component注解进行实例化,并在onApplicationEvent中打印相关信息。

单元测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ListenerTest {
    @Autowired
    private WebApplicationContext webApplicationContext;
    @Test
    public void testListener() {
        NotifyEvent event = new NotifyEvent("object", "pinephp@gmail.com", "This is the content");
        webApplicationContext.publishEvent(event);
    }
}

执行单元测试,会发现事件发布之后,监听器方法被调用,日志被打印出来


如果我们要为SpringBoot 应用添加自定义的 ApplicationListener,有两种方式:

  1. 通过 SpringApplication.addListeners(...) 或者 SpringApplication.setListeners(...)方法添加一个或者多个自定义的 ApplicationListener;

    package org.springframework.boot;
    
    public class SpringApplication {
        private List<ApplicationListener<?>> listeners;
        //......
        public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
            this.listeners = new ArrayList(listeners);
        }
        public void addListeners(ApplicationListener<?>... listeners) {
            this.listeners.addAll(Arrays.asList(listeners));
        }
    
  2. 借助 SpringFactoriesLoader 机制,在META-INF/spring.factories文件中添加配置 在factories中有默认配置

    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
    org.springframework.boot.context.FileEncodingApplicationListener,\
    org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    ......
    

ApplicationContextInitializer

ApplicationContextInitializer 也是 Spring 框架原有的概念,这个类的主要目的就是在 ConfigurableApplicationContext 类型(或者子类型)的ApplicationContext 做 refresh之前允许我们对 ConfigurableApplicationContext 的实例做进一步的设置或者处理。

实现一个ApplicationContextInitializer 很简单,它只有一个方法需要实现:

package org.springframework.context;
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C var1);
}

一般情况下,我们基本不会自定义一个ApplicationContextInitializer,SpringBoot 框架默认注册了几个实现

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

如果我们真的需要自定义一个 ApplicationContextInitializer ,那么只要像上面这样,通过SpringFactoriesLoader 机制,在META-INF/spring.factories 添加,或者通过 SpringApplication.addInitializers方法设置即可

package org.springframework.boot;

public class SpringApplication {
    //......
    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers)    {
        this.initializers = new ArrayList(initializers);
    }

    public void addInitializers(ApplicationContextInitializer<?>... initializers) {
        this.initializers.addAll(Arrays.asList(initializers));
    }

CommandLineRunner

CommandLineRunner 属于 SpringBoot 应用特定的回调扩展接口

其中有两点需要关注

  1. 所有的CommandLineRunner 的执行时间点在 SpringBoot 应用的ApplicationContext 完全初始化开始工作之后,也可以认为是 main 方法执行完成之前的最后一步
  2. 只要存在于当前 SpringBoot 应用的ApplicationContext 中的任何CommandLineRunner ,都会被加载执行。不管是手动注册,还是自动扫描到IoC容器里。

与其他几个扩展点接口类型相似,建议 CommandLineRunner 的实现类使用 @org.springframework.core.annotation.Order 注解进行标注 或者实现 org.springframework.core.Ordered接口,便于对它们的执行顺序进行调整,这其实十分重要,我们不希望顺序不当的CommandLineRunner 实现类阻塞了后面其他CommandLineRunner 的执行。


细谈 自动配置

​ 大概说明自动配置是: @EnableAutoConfiguration 借助 SpringFactoriesLoader 工具类 将标注了 @Configuration 的JavaConfig 类 ”一股脑“ 的汇总并加载到最终的 ApplicationContext 中。

​ 实际上,基于@EnableAutoConfiguration 的自动配置功能拥有更加强大的调控能力,通过配合比如基于条件的配置能力,或者调整加载顺序,我们可以对自动配置进行更加细粒度的调整和控制。

基于条件的自动配置

​ 基于条件的自动配置来源于 Spring 框架中”基于条件的配置" 这一特性。在 Spring框架中,我们可以使用@Conditional 这个 注解 配合 @Configuration 或者 @Bean 等注解来干预一个配置或者 bean定义是否能够生效,其最终实现的效果或者语义类似于如下伪代码

if( 符合@Conditional 规定的条件 ){
    加载当前配置( enable current Configuration )
    或者注册当前bean 定义
}

要实现基于条件的配置,我们只要通过 @Conditional 指定自己的 Condition 实现类就可以了(Type的标注和方法的标注)

@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

最主要的是,@Conditional 可以作为一个 Meta Annotation 用来标注其他 Annotation实现类,从而构建各色的复合注解类

在SpringBoot 中有很多这样的复合注解

@ConditionalOnClass()
@ConditionalOnMissingBean()
@ConditionalOnProperty()
//......
//此注解可以标注在类和方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) 
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

Condition 是一个接口 需要实现Condition接口,并重写方法来自定义match规则 true 通过,false 不通过

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}

这意味着所有的依赖模块都是按需加载的,只有符合某些特定田间,这些依赖模块才会生效,所谓的智能配置

调整自动配置的顺序

​ 我们可以对当前提供的配置或者组件的加载顺序进行相应调整,从而让这些配置或者组件之间的依赖分析和组装可以顺利完成。

​ 我们可以使用 @AutoConfigureAfter 或者 @AutoConfigureBefore 让当前配置或者组件在某个其他组件之前或者组件之后进行。

package org.springframework.boot.autoconfigure.jdbc;
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({DataSource.class, JdbcTemplate.class})
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({JdbcProperties.class})
@Import({JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class})
public class JdbcTemplateAutoConfiguration {
    public JdbcTemplateAutoConfiguration() {
    }
}

总结

​ 以上是对 SpringBoot 的核心组件完成了基本的剖析,大部分东西都是Spring 框架背后原有的一些概念和实践方法,SpringBoot 只是在这些概念和实践方法上对特定的场景进行了固化和升华。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容