3. SpringBoot ——自动配置原理浅析

时间:2023-03-08 19:12:13
3. SpringBoot ——自动配置原理浅析

SpringBoot的功能之所以强大,离不开它的自动配置这一大特色。但估计很多人只是知其然而不知其所以然。下面本人对自动配置原理做一个分析:

在使用SpringBoot时我们通过引入不同的Starter,就自动地应用其相应的自动配置。这是由于每个Starter都会引一个相应xxx--autoconfigure.jar,并且在这个jar的META-INF/spring.factories文件中有类似如下的配置:

      org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

      org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

也就是通过org.springframework.boot.autoconfigure.EnableAutoConfiguration这个为 key引入自动配置类。这个自动类就会以代码的方式去完成以前Spring的xml配置文件做的事。

现在的关键问题就是,Spring的容器是如何知道这个自动配置类的存在的呢?带着这个问题,我们来跟踪SpringBoot启动类的实例run方法,先列出方法源码:

public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}

其实整个过程的秘密全在上面这段代码中体现出来了,只不过隐藏的比较深而已。上面这段有两个方法是和Spring容器发现这个自动配置类有关的

1. 在这个  prepareContext(context, environment, listeners, applicationArguments,printedBanner)    

          方法中完成了SpringBoot 的main方法中的启动类在Spring容器中的注册,并且是以源的方式进行注册。也就是相当于告诉spring容器,这个启动类就相当于是以前的xml文件了

2. refreshContext(context);    

       这个就是初始化 Spring容器,并实例化容器中注册的Bean,自然也就会对SpringBoot 的main方法中的启动类进行解析,这样一来,启动类上的 注解 @EnableAutoConfiguration就起作用了,但是要注意@EnableAutoConfiguration注解是springBoot提供的,springframeworke中的ApplicationContext是不会理会它的,而springboot接管后,相应的容器也是springBoot提供的,所以@EnableAutoConfiguration注解才会起作用,在createApplicationContext()中能看到springboot使用的容器类。

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);
}

下面看 @EnableAutoConfiguration的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {}; /**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {}; }

会发现这个 @EnableAutoConfiguration  注解里面 @Import(AutoConfigurationImportSelector.class) 

AutoConfigurationImportSelector里面有几段代码说明其会从META-INF/spring.factories文件,并加载其中配置key为org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的类,我们称之为自动配置类,这样类上都会有@Configuration注解,所以就能被spring容器处理了。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), 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;
} /**
* Return the class used by {@link SpringFactoriesLoader} to load configuration
* candidates.
* @return the factory class
*/
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}

 

 总结:

       @SpringBootApplication —— > @EnableAutoConfiguration ——> AutoConfigurationImportSelector——>META-INF/spring.factories

至此,一切真相大白。