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 )采用反射的方式创建对象并配置到对应的属性中。
总结一下这个流程:
- 初始化配置类
- 配置系统监听 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 Group子类AutoConfigurationImportSelectorAutoConfigurationGroup 的 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;
}
}