下面我们来说一下SpringBoot的自动配置功能。我们在用SpringBoot的时候,都会用一个注解@SpringBootApplication,那你知道这个注解是怎么起作用的吗,下面我们就来说一下SpringBootApplication注解的原理。先来看一下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这个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration
我们可以看到,这个注解包含了@Configuration注解,也就是把这个类作为一个配置类。
再看一下EnableAutoConfiguration这个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
我们看到,EnableAutoConfiguration注解包含了AutoConfigurationPackage注解,下面我们来看一下这个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage
这个注解通过@Import注解导入了一个类AutoConfigurationPackages的内部类Registrar,我们来看一下这个类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
再到register方法
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);
}
}
这个方法向ioc容器注册了bean。
EnableAutoConfiguration注解还导入了AutoConfigurationImportSelector这个类,我们来看一下
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
再到getAutoConfigurationEntry方法
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
再到fireAutoConfigurationImportEvents方法
private void fireAutoConfigurationImportEvents(List<String> configurations,
Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
再到getAutoConfigurationImportListeners方法
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class,
this.beanClassLoader);
}
通过这个类加载了spring的配置信息,getCandidateConfigurations会到classpath下的读取META-INF/spring.factories文件的配置,并返回一个字符串数组。这就是SpringBoot自动配置的原理。
我们来看一下SpringBoot是怎么启动的
先来看一下一个类EventPublishingRunListener,这个类实现了SpringApplicationRunListener接口
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}
在SpringApplication的启动ioc容器时,获取到所有的listener
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
在ConfigFileApplicationListener的onApplicationEnvironmentPreparedEvent方法里执行EnvironmentPostProcessor的postProcessEnvironment方法
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
根据配置文件加载环境变量
调用SpringApplication的createApplicationContext方法创建SpringBoot的context对象
protected ConfigurableApplicationContext createApplicationContext() {
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);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
通过判断什么环境来创建Application对象,如果是web容器,就创建AnnotationConfigServletWebServerApplicationContext对象,如果是reactive容器,就创建AnnotationConfigReactiveWebServerApplicationContext对象,否则创建AnnotationConfigApplicationContext对象。
再来看一下ConfigFileApplicationContextInitializer的initialize方法
public void initialize(ConfigurableApplicationContext applicationContext) {
new ConfigFileApplicationListener() {
public void apply() {
addPropertySources(applicationContext.getEnvironment(),
applicationContext);
addPostProcessors(applicationContext);
}
}.apply();
}
加载PropertySource,就是配置文件
然后调用SpringApplication的refresh方法,刷新ioc容器
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
到这里,SpringBoot的启动过程就完成了。
SpringBoot的自动配置就分析到这里了。