一、什么是SpringBoot自动装配
SpringBoot的核心功能就是自动装配,只有掌握了SpringBoot自动装配机制,才算熟练掌握SpringBoot。早期我们使用SSH或SSM框架进行应用开发的时候,我们在开启某些Spring特性或引入第三方依赖时需要基于XML或注解进行配置,而这就体现了Spring Framework的自动装配。
而SpringBoot是基于Spring的基础上,通过SPI机制(如对SPI机制不了解,请看SPI机制探秘这篇文章进行了解,看不懂来砍我)的方式,进行了深层次的优化。
根据SPI机制,SpringBoot定义了一套接口标准规范,这套标准规范规定:SpringBoot 在启动时会通过Spring去扫描引用外部 jar 包中的META-INF/spring.factories的配置类文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装配进 SpringBoot。
因此我们使用Spring Boot进行开发时,只需引入一个 starter 即可,通过少量注解和一些简单的配置就能开启引入组件提供的功能,从而对starter提供好的xxxAutoConfiguration、配置文件进行读取进行开发了,就是这么简单。
二、SpringBoot启动源码分析
基于SpringBoot创建应用程序,首先创建一个应用启动类如下图所示:
我们知道SpringBoot程序的入口是SpringApplication.run(Application.class, args);方法,因此点击run()方法进去进行分析,直到点击到最里层的run()方法如下源码所示:【SpringBoot版本为2.1.18.RELEASE,不同版本有细微差别,但原理是一样的】
public ConfigurableApplicationContext run(String... args) {
// 1、创建并启动计时监控进行计时
StopWatch stopWatch = new StopWatch();
// 启动开始计时
stopWatch.start();
// 2、声明应用上下文对象
ConfigurableApplicationContext context = null;
// 异常报告集合
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// 3、设置系统属性java.awt.headless的值
this.configureHeadlessProperty();
// 4、创建所有Spring运行监听器(此处读取META-INF/spring.factories配置文件)
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 发布应用启动事件
listeners.starting();
Collection exceptionReporters;
try {
// 5、处理启动类获取到的args参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 6、准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// 配置忽略的bean信息
this.configureIgnoreBeanInfo(environment);
// 7、设置Banner的打印,可以设置打印banner off、console、log
Banner printedBanner = this.printBanner(environment);
// 8、创建应用上下文
context = this.createApplicationContext();
// 9、实例化异常报告集合
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 10、准备应用上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 11、刷新应用上下文
this.refreshContext(context);
// 12、应用上下文刷新后置事件处理
this.afterRefresh(context, applicationArguments);
// 13、停止计时监控
stopWatch.stop();
// 14、记录日志,输出执行主类名、时间信息等
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 15、发布应用上下文启动完成事件
listeners.started(context);
// 16、运行所有Runner执行器
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
// 处理运行错误信息
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 17、发布应用上下文就绪事件
listeners.running(context);
// 18、返回应用上下文对象
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
从源码解析中可知SpringBoot启动共分为18个步骤,从中了解SpringBoot的启动流程,从而进行一些自定义的处理过程。
三、SpringBoot如何实现自动装配
基于上文内容,我们分析了run()方法,接着从应用启动类上的核心注解SpringBootApplication开始进行自动装配分析,知道SpringBoot关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar中。
点击@SpringBootApplication进去,如下源码所示:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 点击进去是@Configuration注解,即允许在上下文中注册额外的bean或导入其他配置类
@SpringBootConfiguration
// 启用SpringBoot的自动配置机制
@EnableAutoConfiguration
// 扫描被@Component/@Service/@Controller注解的bean,注解默认会扫描启动类所在的包下所有的类 ,同时可以自定义不扫描的bean。
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
从源码中可知@EnableAutoConfiguration是实现自动装配的重要注解,从该注解继续点击进去,如下源码所示:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 将main包下所有组件注册到容器中
@AutoConfigurationPackage
// 加载自动装配类XxxxAutoConfiguration类
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
// 用来覆盖自动配置开关的功能
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 根据类排除指定的自动配置
Class<?>[] exclude() default {};
// 根据类名排除指定的自动配置
String[] excludeName() default {};
}
从源码中可知AutoConfigurationImportSelector是负责加载自动装配类的,其中实现了多个接口,我们看下整个继承体系,如下图所示:
从图中可知,AutoConfigurationImportSelector 类实现了DeferredImportSelector接口从而实现了 ImportSelector接口,也就实现了这个接口中的 selectImports()方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类将被加载到 IoC 容器中。
点击进去,如下源码所示:【这块代码看懂了就没问题了】
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry();
private static final String[] NO_IMPORTS = new String[0];
private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
public AutoConfigurationImportSelector() {
}
// 重写ImportSelector接口的selectImports()方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断是否开启自动装配配置,默认spring.boot.enableautoconfiguration=true,可在 application.properties或application.yml中进行设置
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 获取所有需要装配的bean
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 {
// 获取主类中配置的exclude或excludeName注解的属性值
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获取所有候选的配置类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 对重复的配置类进行去重
configurations = this.removeDuplicates(configurations);
// 获得注解中被exclude或excludeName所指定被排除的类的集合
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 检查被排除的类是否可实例化,是否被自动注册配置所使用,不符合条件则抛出异常
this.checkExcludedClasses(configurations, exclusions);
// 从所有候选的自动配置类集合中移除被排除的类
configurations.removeAll(exclusions);
// 过滤配置类的注解是否符合META-INF/spring.factories文件中AutoConfigurationImportFilter指定的注解检查条件
configurations = this.filter(configurations, autoConfigurationMetadata);
// 将候选完成的配置类和被排除的配置类构建为事件类,并通知监听器 this.fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回候选完成的配置类和被排除的配置类
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
public Class<? extends Group> getImportGroup() {
return AutoConfigurationImportSelector.AutoConfigurationGroup.class;
}
protected boolean isEnabled(AnnotationMetadata metadata) {
return this.getClass() == AutoConfigurationImportSelector.class ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
}
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = this.getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> {
return "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?";
});
return attributes;
}
protected Class<?> getAnnotationClass() {
return EnableAutoConfiguration.class;
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 使用SpringFactoriesLoader工具类,查找classpath目录下所有jar包中的META-INF/spring.factories,找出其中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的属性定义的工厂类名称【核心代码】
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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList(exclusions.size());
Iterator var4 = exclusions.iterator();
while(var4.hasNext()) {
String exclusion = (String)var4.next();
if (ClassUtils.isPresent(exclusion, this.getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
if (!invalidExcludes.isEmpty()) {
this.handleInvalidExcludes(invalidExcludes);
}
}
protected void handleInvalidExcludes(List<String> invalidExcludes) {
StringBuilder message = new StringBuilder();
Iterator var3 = invalidExcludes.iterator();
while(var3.hasNext()) {
String exclude = (String)var3.next();
message.append("\t- ").append(exclude).append(String.format("%n"));
}
throw new IllegalStateException(String.format("The following classes could not be excluded because they are not auto-configuration classes:%n%s", message));
}
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet();
// 排除配置的自动装配类
excluded.addAll(this.asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
// 排除通过配置文件spring.autoconfigure.exclude=全限定类名的方法
excluded.addAll(this.getExcludeAutoConfigurationsProperty());
return excluded;
}
private List<String> getExcludeAutoConfigurationsProperty() {
if (this.getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(this.getEnvironment());
return (List)binder.bind("spring.autoconfigure.exclude", String[].class).map(Arrays::asList).orElse(Collections.emptyList());
} else {
String[] excludes = (String[])this.getEnvironment().getProperty("spring.autoconfigure.exclude", String[].class);
return excludes != null ? Arrays.asList(excludes) : Collections.emptyList();
}
}
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
// 将所有加载的自动配置类封装成数组
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// 遍历过滤器进行判断自动装配是否生效
Iterator var8 = this.getAutoConfigurationImportFilters().iterator();
while(var8.hasNext()) {
AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
this.invokeAwareMethods(filter);
// 主要判断逻辑,candidates是自动装配类数组,autoConfigurationMetadata这个是META-INF/spring-autoconfigure-metadata.properties文件中的值
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for(int i = 0; i < match.length; ++i) {
if (!match[i]) {
skip[i] = true;
// 将不生效的自动配置类置为null
candidates[i] = null;
// 判断标志位
skipped = true;
}
}
}
// 判断标志位,true,则跳过;如果为false,返回配置类
if (!skipped) {
return configurations;
} else {
// 保存最终的配置类
List<String> result = new ArrayList(candidates.length);
int numberFiltered;
// 遍历所有的自动装配类数组
for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) {
if (!skip[numberFiltered]) {
// 将不为null的值加入进去
result.add(candidates[numberFiltered]);
}
}
if (logger.isTraceEnabled()) {
numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
// 返回最中的自动装配类集合
return new ArrayList(result);
}
}
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
// 具体的加载META-INF/spring.factories中的 AutoConfigurationImportFilter的值
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList(new LinkedHashSet(list));
}
protected final List<String> asList(AnnotationAttributes attributes, String name) {
String[] value = attributes.getStringArray(name);
return Arrays.asList(value != null ? value : new String[0]);
}
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
// 加载META-INF/spring.factories文件中类型为 AutoConfigurationImportListener的监听器
List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
// 封装AutoConfigurationImportEvent自动装配导入事件
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
// 遍历监听器
Iterator var5 = listeners.iterator();
while(var5.hasNext()) {
AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
this.invokeAwareMethods(listener);
// 广播事件,所有监听AutoConfigurationImportEvent 事件的监听器会做出相应的处理
listener.onAutoConfigurationImportEvent(event);
}
}
}
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
// 具体加载META-INF/spring.factories中的 AutoConfigurationImportFilter的值
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}
private void invokeAwareMethods(Object instance) {
if (instance instanceof Aware) {
if (instance instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware)instance).setBeanClassLoader(this.beanClassLoader);
}
if (instance instanceof BeanFactoryAware) {
((BeanFactoryAware)instance).setBeanFactory(this.beanFactory);
}
if (instance instanceof EnvironmentAware) {
((EnvironmentAware)instance).setEnvironment(this.environment);
}
if (instance instanceof ResourceLoaderAware) {
((ResourceLoaderAware)instance).setResourceLoader(this.resourceLoader);
}
}
}
public int getOrder() {
return 2147483646;
}
protected static class AutoConfigurationEntry {
private final List<String> configurations;
private final Set<String> exclusions;
private AutoConfigurationEntry() {
this.configurations = Collections.emptyList();
this.exclusions = Collections.emptySet();
}
AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
this.configurations = new ArrayList(configurations);
this.exclusions = new HashSet(exclusions);
}
public List<String> getConfigurations() {
return this.configurations;
}
public Set<String> getExclusions() {
return this.exclusions;
}
}
private static class AutoConfigurationGroup implements Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap();
private final List<AutoConfigurationImportSelector.AutoConfigurationEntry> autoConfigurationEntries = new ArrayList();
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private AutoConfigurationMetadata autoConfigurationMetadata;
private AutoConfigurationGroup() {
}
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
});
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(this.getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();
while(var4.hasNext()) {
String importClassName = (String)var4.next();
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
} else {
Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
return new Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
}).collect(Collectors.toList());
}
}
// 。。。。。。删除了部分代码
分析完了源码,在进行一次总结,如下图所示:
四、SpringBoot条件注解
从源码分析中知道,有些类会被过滤掉,这就得说SpringBoot条件注解,而SpringBoot自动装配通常有一个原则:只有当容器中不存在特定的类型的bean或特定bean时,SpringBoot自动装配才会装配该类型的bean或特定bean。
条件注解可用于修饰@Configuration类或@Bean方法等,表示只有当特定条件有效时,被修饰的配置类或配置方法才会生效。
SpringBoot条件注解分类如下:
1、类条件注解
@ConditionalOnClass:当类路径下存在被修饰的类或方法时生效。可通过value或name属性指定它所要求存在的类,其中value属性值是被检查类的Class对象,name属性值是被检查类的字符串形式的全限定类名,一般使用name属性值居多。
@ConditionalOnMissingClass:当类路径下没有被修饰的类或方法时生效。只能通过value属性指定它所要求不存在的类,value属性值只能是被检查类的字符串形式的全限定类名。
2、Bean条件注解
@ConditionalOnBean:当容器里存在指定 Bean 的条件下生效
@ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下生效
@ConditionalOnSingleCandidate:@ConditionalOnBean的增强版,当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean时才生效。
@ConditionalOnMissingFilterBean:@ConditionalOnMissingBean的特殊版本,专门用于检查容器中是否存在指定类型的javax.servlet.Filter,因此它只能通过value属性指定其要检查的Filter的类型。
3、属性条件注解
@ConditionalOnProperty:指定的属性是否存在指定的属性值
4、Web应用条件注解
@ConditionalOnWebApplication:当前项目是Web应用时,修饰的类或方法才生效。
@ConditionalOnNotWebApplication:当前项目不是Web应用时,修饰的类或方法才生效。
@ConditionalOnWarDeployment:只有当前应用以传统WAR包方式被部署到Web服务器或应用服务器时,修饰的类或方法才生效。
5、资源条件注解
@ConditionalOnResource:当指定的资源存在时,修饰的类或方法才生效。
6、SpEL表达式条件注解
@ConditionalOnExpression:基于指定SpEL 表达式的值为true时,修饰的类或方法才生效。
7、特殊条件注解
@ConditionalOnJava:对部署平台的Java版本进行检测,配置才生效。
@ConditionalOnJndi:在 JNDI 存在的条件下,通过value属性指定要检查的JNDI。
@ConditionalOnCloudPlatform:应用被部署在特定云平台上,修饰的类或方法才生效。
@ConditionalOnRepositoryType:当特定的Spring Data Repository被启用时,修饰的类或方法才生效。
8、自定义注解
只有当满足自定义的注解时,修饰的类或方法才生效。