SpringBoot启动流程及自动装配

SpringBoot启动流程

SpringBoot的启动类

我们先来看一下SpringBoot的启动类:

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

可以看到启动类的关键在于注解 @SpringBootApplication 和 SpringApplication 的 run() 方法。

@SpringBootApplication

该注解代码如下:

@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 { ... }

可以看到该注解包含三个核心注解:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

@SpringBootConfiguration

该注解其实就是一个 @Configuration 注解,用来标明我们的启动类其实也是一个配置类

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

@ComponentScan

这个注解就不用过多介绍了,在这里并没有指明 @ComponentScan 要扫描的路径,因此它将按照默认情况扫描该注解所在路径下的 @Controller、@Service 等注解。

@EnableAutoConfiguration

该注解是 SpringBoot 实现自动配置的核心注解,其内部包含了两个重要的注解,分别是 @AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class)

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

@AutoConfigurationPackage 注解的内部使用了 @Import(AutoConfigurationPackages.Registrar.class) 注册Bean定义

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImports(metadata));
    }
}

Registrar 是 AutoConfigurationPackages 的静态内部类,实现了 ImportBeanDefinitionRegistrar 接口,因此在IOC容器初始化过程中,将会调用它的 registerBeanDefinitions() 方法注册bean定义。(后面详细说)

@Import(AutoConfigurationImportSelector.class)

可以看到该注解中配置了一个 ImportSelector 的子类 AutoConfigurationImportSelector,我们也将在接下来的过程中看到其重要的作用。

SpringBoot的启动流程

SpringBoot应用的启动随着我们执行启动类的run()方法而开始,接下来我们就一起看一下run()中究竟做了什么:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
   return run(new Class<?>[] { primarySource }, args);
}
// 创建SpringApplication对象并调用run()方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

初始化SpringApplication

在最开始时,随着run()方法的执行,容器将会调用SpringApplication的run()方法启动应用。而run()方法中也是先创建一个 SpringApplication实例并调用该实例的run(args),我们先来看一下创建SpringApplication的过程中做了什么:

public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 初始化资源加载器,这里传入为null
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 初始化配置类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 解析应用类型,一般是 SERVLET
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 配置Spring提供的一些 ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 配置系统监听
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 配置应用主类
    this.mainApplicationClass = deduceMainApplicationClass();
}

可以看到这里关键的两步:配置监听、ApplicationContextInitializer。监听就不用多说了,ApplicationContextInitializer组件会在启动初期被调用执行内部的 initialize() 方法(后面详细介绍)。

这里在获取的流程中调用了 SpringApplication 的 getSpringFactoriesInstances() 方法获取对应类型的组件。

getSpringFactoriesInstances() 方法的内部使用了 SpringFactoriesLoader 的 loadFactoryNames() 方法进行获取

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

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

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        Enumeration<URL> urls = (classLoader != null ?
                                 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

可以看到本方法本质上是读取 spring-boot 这个包下的 META-INF/spring.factories 的内容,并存储到缓存cache中。spring-boot 包下的 spring.factories 中配置了一些系统的加载器、监听等的全限定类名。

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
...

通过这个方式读取完配置文件内容并存到缓存cache之后,调用方(SpringApplication )采用反射的方式创建对象并配置到对应的属性中。

总结一下这个流程:

  1. 初始化配置类
  2. 配置系统监听 ApplicationListener 及 ApplicationContextInitializer

容器启动

SpringApplication中容器启动的流程如下:

public ConfigurableApplicationContext run(String... args) {
    // 定义任务执行时间等信息
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 设置系统属性 java.awt.headless=true
    configureHeadlessProperty();
    // 1.1,创建并获取,这里同样是通过SpringFactoriesLoader获取
    // 不过是在前面已经被加到了缓存cache中,这里只需要从cache中直接获取
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 1.2, 使用事件派发器派发应用启动事件 ApplicationStartingEvent
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 打印banner,我们可以在配置文件配置并打印自定义banner
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] { ConfigurableApplicationContext.class }, context);
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

流程1:发布应用启动事件

通过以下两行代码,SpringBoot获取应用启动的监听 EventPublishingRunListener 并使用其内部的派发器 SimpleApplicationEventMulticaster 派发应用启动事件 ApplicationStartingEvent

// 1.1,创建并获取,这里同样是通过SpringFactoriesLoader获取
// 不过是在前面已经被加到了缓存cache中,这里只需要从cache中直接获取
SpringApplicationRunListeners listeners = getRunListeners(args);
// 1.2, 使用事件派发器派发应用启动事件 ApplicationStartingEvent
listeners.starting();

再次简述一下派发器的作用:我们或程序将监听注册到派发器,之后由派发器派发事件,然后派发器根据监听器事件类型,在事件发布时,派发器找到对应的监听器执行监听方法

流程2:加载配置

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 配置环境变量
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);

程序通过 prepareEnvironment() 加载我们的配置文件,我们主要看一下配置文件是如何被加载的。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments) {
    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;
}

prepareEnvironment() 中通过 listeners.environmentPrepared(environment) 加载配置,源码如下:

void environmentPrepared(ConfigurableEnvironment environment) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.environmentPrepared(environment);
    }
}

前面我们又知道了此时应用中只有一个 SpringApplicationRunListener 那就是 EventPublishingRunListener ,因此此刻调用的就是 EventPublishingRunListener 的如下方法:

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
   this.initialMulticaster
         .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

在该方法中,使用事件派发器派发了一个 ApplicationEnvironmentPreparedEvent 事件,然后由派发器调用监听处理。

需要注意的是这里的监听有一个 ConfigFileApplicationListener,它同时作为一个监听和一个 EnvironmentPostProcessor存在

@Override
public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
    }
    if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent(event);
    }
}

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    postProcessors.add(this);
    AnnotationAwareOrderComparator.sort(postProcessors);
    // 获取 EnvironmentPostProcessor 并执行 postProcessEnvironment() 方法
    for (EnvironmentPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
    }
}

可以看到这里在 ConfigFileApplicationListener 这个监听内部又获取了所有的 EnvironmentPostProcessor 并执行他们的 postProcessEnvironment() 方法

在它的 postProcessEnvironment() 方法中,首先获取路径属性:

private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/";

然后获取默认文件名称:

private static final String DEFAULT_NAMES = "application";

然后依次获取 spring.factories 中配置的两个资源加载器:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

PropertiesPropertySourceLoader、YamlPropertySourceLoader用于获取配置文件后缀:

// PropertiesPropertySourceLoader
@Override
public String[] getFileExtensions() {
   return new String[] { "properties", "xml" };
}
// YamlPropertySourceLoader
@Override
public String[] getFileExtensions() {
    return new String[] { "yml", "yaml" };
}

由此可见配置文件的加载顺序为:properties、xml、yml、yaml

流程3:打印Banner

// 打印Banner,可以通过配置文件自定义banner
Banner printedBanner = printBanner(environment);

流程4:准备IOC容器

// 创建IOC容器
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
      new Class[] { ConfigurableApplicationContext.class }, context);
/*
	准备环境变量
	执行ApplicationContextInitializer的initialize()方法
	发布ApplicationContextInitializedEvent等
	注册早期组件
*/
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器
refreshContext(context);
// 模版方法
afterRefresh(context, applicationArguments);

流程4:发布容器启动事件

listeners.started(context);

流程5:调用Runner

callRunners(context, applicationArguments);

我们可以通过定义ApplicationRunner或CommandLineRunner来调用他们的run()方法,并通过传入的参数来做一些事。

自动装配原理

首先@EnableAutoConfiguration包含以下两个注解:

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage { ... }

可以看到该类使用 AutoConfigurationPackages.Registrar 来注入bean定义:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    // 注册bean定义
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }
	...
}

该Bean定义主要是将BEAN也就是当前类的类名为id,将注解所在的包名如com.zsp注册到容器中:

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    if (registry.containsBeanDefinition(BEAN)) {
        BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
        ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
        constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
    }
    else {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(BasePackages.class);
        beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(BEAN, beanDefinition);
    }
}

private static final String BEAN = AutoConfigurationPackages.class.getName();

那么这么做有什么作用呢?@ComponentScan 本来就已经可以扫描所在路径下的所有组件,但是 @ComponentScan 主要是用来扫描自有的组件注解,如@Controller、@Service这种。但是一些其他的如@Mapper这种就扫描不了了,在学习SpringBoot整合Mybatis时我们都知道,如果在每个Mapper上都加上@Mapper注解可以起到和@MapperScan相同的作用,这就得益于 @AutoConfigurationPackage 提供的路径,帮助 MybatisAutoConfiguration 在扫描时获取路径并扫描下面的 @Mapper。

@Import(AutoConfigurationImportSelector.class)

我们先来看 AutoConfigurationImportSelector:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ... }

可以看到 AutoConfigurationImportSelector 属于 DeferredImportSelector 的子类。

DeferredImportSelector 是ImportSeletor的子类,意为延迟执行的ImportSeletor。接下来一起看一下他的执行时机:

首先在IOC容器的刷新过程中,Bean定义的加载是在 AbstractApplicationContext 的refresh() 方法中调用 invokeBeanFactoryPostProcessors() 进行的:

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

而从前面对IOC初始化过程的学习我们知道,ConfigurationClassPostProcessor 作为 BeanDefinitionRegistryPostProcessor 的子类也将在此时执行。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
      PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware { ... }

于是在对实现了 PriorityOrdered 优先级接口的 BeanDefinitionRegistryPostProcessor 进行处理时,ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry() 方法将会被调用,而此方法内部又调用了其 processConfigBeanDefinitions() 方法用于处理配置中定义的bean。

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    ...
	// 调用 processConfigBeanDefinitions() 方法
    processConfigBeanDefinitions(registry);
}

在 processConfigBeanDefinitions() 方法中,关键看如下几步:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    // 初始化配置类解析器
    ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    
    ...
    parser.parse(candidates);//位置1
    parser.validate();

    Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
    configClasses.removeAll(alreadyParsed);
	// 初始化BeanDefinition阅读器
    if (this.reader == null) {
       this.reader = new ConfigurationClassBeanDefinitionReader(
             registry, this.sourceExtractor, this.resourceLoader, this.environment,
             this.importBeanNameGenerator, parser.getImportRegistry());
    }
    // 加载Bean定义
    this.reader.loadBeanDefinitions(configClasses);
    ...
}

位置1:扫描系统配置类

可以看到在执行位置1的代码之前,首先初始化一个配置类解析器 ConfigurationClassParser 并通过其 parse() 方法解析配置类中的注解,方法内容如下:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
    // 处理 DeferredImportSelector
    this.deferredImportSelectorHandler.process();
}

可以看到在该方法中,首先使用 parse() 方法处理配置类中的注解,处理时会将 @Import 中配置的 DeferredImportSelector 处理到 this.deferredImportSelectorHandler 中:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
	...
    if (selector instanceof DeferredImportSelector) {
        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
    }
	...
}

然后再由上面的 this.deferredImportSelectorHandler.process() 处理这些 DeferredImportSelector:

// DeferredImportSelectorHolder 中的 process() 方法
public void process() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            deferredImports.forEach(handler::register);
            handler.processGroupImports();// 处理
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}

在DeferredImportSelectorHolder 的 process() 处理过程中,通过调用 DeferredImportSelector G r o u p 子 类 A u t o C o n f i g u r a t i o n I m p o r t S e l e c t o r Group 子类AutoConfigurationImportSelector GroupAutoConfigurationImportSelectorAutoConfigurationGroup 的 process() 方法,最终来到了 AutoConfigurationImportSelector 的 getAutoConfigurationEntry() 方法::

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 获取META-INF/spring.factories的key为EnableAutoConfiguration全类名的配置
    // SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 利用 LinkedHashSet 去重
    configurations = removeDuplicates(configurations);
    // 排除
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    /*  通过META-INF/spring.factories配置的
    	三个过滤器OnBeanCondition,OnClassCondition,OnWebApplicationCondition过滤
    	1, OnBeanCondition: 检查@ConditionalOnMissingBean、@ConditionalOnBean、@ConditionalOnSingleCandidate中有无Bean
    	2, OnClassCondition: 检查@ConditionalOnClass、@ConditionalOnMissingClass有无指定的类
    	3, OnWebApplicationCondition: ConditionalOnWebApplication,ConditionalOnNotWebApplication
   	*/
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

经过这么个处理流程,我们需要的自动配置类就已经被成功获取,接下来回到 ConfigurationClassPostProcessor 中的 processConfigBeanDefinitions() 方法,执行接下来的两步获取配置类Bean定义阅读器 ConfigurationClassBeanDefinitionReader 并使用其加载bean定义:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    ...
	// 初始化BeanDefinition阅读器
    if (this.reader == null) {
       this.reader = new ConfigurationClassBeanDefinitionReader(
             registry, this.sourceExtractor, this.resourceLoader, this.environment,
             this.importBeanNameGenerator, parser.getImportRegistry());
    }
    // 加载Bean定义
    this.reader.loadBeanDefinitions(configClasses);
    ...
}

xxAutoConfiguration

接下来以 JdbcTemplateAutoConfiguration 为例,讲一下自动配置是如何生效的:

@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 {

}

首先看两个Conditional相关的注解,该组件的加载首先依赖 DataSource 和 JdbcTemplate 的存在与否。

然后通过@AutoConfigureAfter这个注解我们可以知道,它的加载依赖于 DataSourceAutoConfiguration 的加载:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration { ... }

DataSourceAutoConfiguration 的加载过程中会通过@EnableConfigurationProperties(DataSourceProperties.class)来加载数据库配置:

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean { ... }

利用Bean生命周期中的 InitializingBean,DataSourceProperties可以在bean实例化过程中依赖配置文件中spring.datasource相关配置初始化数据库连接,然后在 JdbcTemplateConfiguration 中配置JdbcTemplate 并将其注册到容器中。

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {
    @Bean
    @Primary
    JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        JdbcProperties.Template template = properties.getTemplate();
        jdbcTemplate.setFetchSize(template.getFetchSize());
        jdbcTemplate.setMaxRows(template.getMaxRows());
        if (template.getQueryTimeout() != null) {
            jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());
        }
        return jdbcTemplate;
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值