// ...
public ConfigurableApplicationContext run(String... args) {
//监控任务执行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//创建应用上下文
ConfigurableApplicationContext context = null;
//用来记录关于启动的错误报告
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//监听SpringBoot应用启动过程中的一些生命周期
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
//运行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//加载相关配置文件
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//打开banner
Banner printedBanner = this.printBanner(environment);
//创建应用上下文
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
// ...
通过一个简单的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}
)}
)
// ...
@SpringBootConfiguration
继承自@Configuration
,二者功能也一致,只不过@SpringBootConfiguration
是springboot的注解,而@Configuration
是spring的注解,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean
注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //<-----
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
SpringBoot的约定大于配置在这体现
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //<--
@Import({AutoConfigurationImportSelector.class}) //<--
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
利用@Import注解,将所有符合自动装配条件的bean注入到IOC容器中,@Import注解的原理…头皮发麻…
@Import原理
进入类AutoConfigurationImportSelector
,观察selectImports
方法,这个方法执行完毕后,Spring会把这个方法返回的类的全限定名数组里的所有的类都注入到IOC容器中
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
//...
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;
}
首先会去加载所有Spring预先定义的配置条件信息,这些配置信息在org.springframework.boot.autoconfigure
包下的META-INF/spring-autoconfigure-metadata.properties
文件中
如果你要自动装配某个类的话,你觉得先存在哪些类或者哪些配置文件等等条件,这些条件的判断主要是利用了@ConditionalXXX
注解,例如springMVC的自动配置类
通过这些注解判断是否需要自动装配,详情就不赘述了。
总结:
1.springBoot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值
2.将这些自动配置类导入容器,自动配置就会生效,帮我们进行自动配置
3.它会把所有需要导入的组件,以类名的形式返回,这些组件就会被添加到容器
4.容器存在非常多的XXXAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Cofiguration,JavaConfig
5.有了自动配置类,免去了我们手动编写的工作
6.Springboot帮我们写好了配置类(约定大于配置),启动时进行自动配置
自动扫描并加载符合条件的Bean到容器中,这个注解会默认扫描声明类所在的包,例如:类cn.shiyujun.Demo类上标注了@ComponentScan 注解,则cn.shiyujun.controller、cn.shiyujun.service等等包下的类都可以被扫描到
配置文件路径的优先级:
我们从属性:DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/
可以看出文件路径的先后顺序(注意:后加载的会覆盖先加载的):
classpath:/
classpath:/config/
file:./
file:./config/
在配置文件中能配置的东西,都存在一个固有的规律
xxxAutoConfiguration 默认值 xxx.Properties 配置文件绑定
// ....
@ConfigurationProperties(
prefix = "spring.mvc"
)
public class WebMvcProperties {
private Format messageCodesResolverFormat;
private Locale locale;
private WebMvcProperties.LocaleResolver localeResolver;
private String dateFormat;
private boolean dispatchTraceRequest;
private boolean dispatchOptionsRequest;
private boolean ignoreDefaultModelOnRedirect;
private boolean publishRequestHandledEvents;
private boolean throwExceptionIfNoHandlerFound;
private boolean logResolvedException;
private String staticPathPattern;
private final WebMvcProperties.Async async;
private final WebMvcProperties.Servlet servlet;
private final WebMvcProperties.View view;
private final WebMvcProperties.Contentnegotiation contentnegotiation;
private final WebMvcProperties.Pathmatch pathmatch;
//...
一 一 对应,我们配置yml即修改默认配置类的参数
xxxAutoConfiguration 默认值 xxx.Properties 配置文件绑定
在SpringBoot启动时一起加载