使用SpringBoot的最大好处就是简化配置,它实现了自动化配置。
这里以SpringBoot 2.1.4.RELEASE版本和Spring 5.1.6.RELEASE版本为例。
API文档:https://docs.spring.io/spring-boot/docs/current/api/
自动化配置的原理如下:
一个SpringBoot构建的项目都会有一个入口启动类,其中有个最重要的注解就是@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 {
@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 {};
}在SpringBootApplication类上有一个重要的注解@EnableAutoConfiguration,它就是实现自动化配置的核心。当SpringBoot项目启动的时候,就会调用@EnableAutoConfiguration来进一步加载系统所需的一些配置信息,完成自动化配置。
@EnableAutoConfiguration的源码如下:
@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 {};
}在EnableAutoConfiguration类中,它使用@import注解来导入配置类AutoConfigurationimportSelector。
使用@import注解可以导入三种类型的配置类,如下:
(1)直接导入配置类:@import({xxxConfiguration.class})
(2)依据条件选择配置类:@import({xxxSelector.class})
(3)动态注册 Bean:@import({xxxRegistrar.class})
我们进入查看AutoConfigurationimportSelector类的源码,(由于源码太多,在此不再展示),其中用到了一个重要的类SpringFactoriesLoader,该类位于org.springframework.core.io.support包下,它才是真正加载项目所需要的jar包的类,它主要用于加载 classpath下所有 JAR 文件的 meta-INF/spring.factories 文件,并分析出其中定义的工厂类。这些工厂类进而被启动逻辑使用,应用于进一步初始化工作。
SpringFactoriesLoader类是spring框架自己使用的内部工具类,本身被声明为 final,表示不可以被其他类继承。
SpringFactoriesLoader类的源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.core.io.support;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.UrlResource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.linkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "meta-INF/spring.factories";
//日志
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map> cache = new ConcurrentReferenceHashMap();
private SpringFactoriesLoader() {
}
public static List loadFactories(Class factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//加载类型为factoryClass的工厂的名称,其实是一个个的全限定类名,使用指定的classloader:classLoaderToUse
List factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List result = new ArrayList(factoryNames.size());
Iterator var5 = factoryNames.iterator();
// 实例化所加载的每个工厂类
while(var5.hasNext()) {
String factoryName = (String)var5.next();
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
//对工厂类进行排序
AnnotationAwareOrderComparator.sort(result);
return result;
}
public static List loadFactoryNames(Class> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
// 1. 使用指定的classloader扫描classpath上所有的JAR包中的文件meta-INF/spring.factories,加载其中的多值工厂属性定义,使用多值Map的形式返回,
// 2. 返回多值Map中key为factoryClassName的工厂名称列表,如果没有相应的entry,返回空列表而不是返回null
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
// 扫描classpath上所有JAR中的文件meta-INF/spring.factories
Enumeration urls = classLoader != null ? classLoader.getResources("meta-INF/spring.factories") : ClassLoader.getSystemResources("meta-INF/spring.factories");
linkedMultiValueMap result = new linkedMultiValueMap();
while(urls.hasMoreElements()) {
// 找到的每个meta-INF/spring.factories文件都是一个Properties文件,将其内容
// 加载到一个 Properties 对象然后处理其中的每个属性
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry, ?> entry = (Entry)var6.next();
// 获取工厂类名称(接口或者抽象类的全限定名)
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [meta-INF/spring.factories]", var13);
}
}
}
private static T instantiateFactory(String instanceClassName, Class factoryClass, ClassLoader classLoader) {
try {
Class> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
} else {
return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance();
}
} catch (Throwable var4) {
throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);
}
}
} 一般情况下,springboot提供的一些JAR包里面会带有文件meta-INF/spring.factories,然后在Springboot启动的时候,根据启动阶段不同的需求,框架就会多次调用SpringFactoriesLoader加载相应的工厂配置信息。
使用了注解@EnableAutoConfiguration时,就会触发对SpringFactoriesLoader.loadFactoryNames()的调用。
看一下spring.factories所在的位置:
部分内容如下:
# Initializers org.springframework.context.ApplicationContextInitializer= org.springframework.boot.autoconfigure.SharedmetadataReaderFactoryContextInitializer, org.springframework.boot.autoconfigure.logging.ConditionevaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener= org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration import Listeners org.springframework.boot.autoconfigure.AutoConfigurationimportListener= org.springframework.boot.autoconfigure.condition.ConditionevaluationReportAutoConfigurationimportListener # Auto Configuration import Filters org.springframework.boot.autoconfigure.AutoConfigurationimportFilter= org.springframework.boot.autoconfigure.condition.OnBeanCondition, org.springframework.boot.autoconfigure.condition.OnClassCondition, org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
以上就是SpringBoot实现自动化配置的原理,或许你看到的源码会与我的不相同,那有可能是jar版本的不一致。
总结一下使用SpringBoot的好处:
(1)简化配置,不需要编写太多的xml配置文件;
(2)基于Spring构建,使开发者快速入门,门槛很低;
(3)SpringBoot可以创建独立运行的应用而不需要依赖于容器;
(4)内置tomcat服务器,不需要打包成war包,可以直接放到tomcat中运行;
(5)提供maven极简配置,以及可视化的相关监控功能,比如性能监控,应用的健康程度等;
(6)为微服务SpringCloud奠定了基础,使得微服务的构建变得简单;
(7)Spring可以整合很多各式各样的框架,并能很好的集成;
(8)活跃的社区与论坛,以及丰富的开发文档;



