springboot

文章目录
  1. 1. 自动装配
  2. 2. 嵌入式容器
  3. 3. 为生产做准备
  4. 4. springboot踩坑记录

springboot三大特性(2.1.6)


自动装配

springboot核心注解 @SpringBootApplication。该注解是由下面三个注解组合而成的。


@ComponentScan 默认会去扫描当前类所在包下面的所有类或者所有包,将这些类创建为spring所管理的bean。可以通过添加basePackages或者是basePackageClasses属性来指定扫描的包路径。


@EnableAutoConfiguration

关于META-INF/spring-autoconfigure-metadata.properties文件与META-INF/spring.factories文件

# spring-autoconfigure-metadata.properties

#Tue Jun 18 23:47:55 GMT 2019
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.neo4j.Neo4jBookmarkManagementConfiguration.Configuration=
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration=
org.springframework.boot.autoconfigure.kafka.KafkaAnnotationDrivenConfiguration.Configuration=
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration.ConditionalOnClass=org.influxdb.InfluxDB
# spring.factories,类似于 java 或者是 dubbo 中的 spi

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

该注解的实现主要依赖于@Import注解引入的AutoConfigurationImportSelector类的selectImports方法。

// 注解定义-》关键点:AutoConfigurationImportSelector类
@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 {};
}

// 导入筛选实现类 AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
....
// 若是 getAutoConfiguration 方法判断出用户关闭了自动装配,就返回该对象。
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 1. 判断是否可以导入,若是不可以导入,直接返回一个空数组 {}
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 2. 使用自动装配元信息加载类(AutoConfigurationMetadataLoader)的 loadMetadata 方法加载自动装配元信息。返回的AutoConfigurationMetadata对象包含了一个Properties类型的属性。(具体分析见 AutoConfigurationMetadataLoader的loadMetadata方法。)
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
// 3. 根据步骤2返回的自动装配元信息(AutoConfigurationMetadata)和 注解元信息(AnnotationMetadata)做进一步处理。
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);
// 4. 转化未String数组返回
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

// 判断是否可以导入的逻辑,若是用户在配置文件中配置了spring.boot.enableautoconfiguration属性未false,则不执行自动装配。默认执行自动装配。
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}

// 获取自动装配的元素
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
// 1. 若是判断出不能自动装配,注解返回一个 AutoConfigurationEntry 空对象
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 2. 获取到 EnableAutoConfiguration 注解的属性值
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 3. 读取候选装配组件-> EnableAutoConfiguration所对应的配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 4. 对这些配置类去重
configurations = removeDuplicates(configurations);
// 5. 获取用户配置的排除项(不自动装配)-> exclude,excludeName以及 spring.autoconfigure.exclude
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 6. 检查需要排除的项是否合法,非法的将抛出异常
checkExcludedClasses(configurations, exclusions);
// 7. 将这些排除向从配置中移除
configurations.removeAll(exclusions);
// 8. 执行过滤
configurations = filter(configurations, autoConfigurationMetadata);
// 9. 触发相关事件
fireAutoConfigurationImportEvents(configurations, exclusions);
// 10. 返回
return new AutoConfigurationEntry(configurations, exclusions);
}

// 读取给定注解元信息AnnotationMetadata的属性AnnotationAttributes
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
// 1. 获取到自动装配注解 EnableAutoConfiguration 的包名
// 如:org.springframework.boot.autoconfigure.EnableAutoConfiguration
String name = getAnnotationClass().getName();
// 2. 获取注解 @EnableAutoConfiguration 的属性值,该注解包含了两个属性 exclude 和 excludeName
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
// 3. 返回注解属性 AnnotationAttributes
return attributes;
}
// 返回 @EnableAutoConfiguration 注解的 Class
protected Class<?> getAnnotationClass() {
return EnableAutoConfiguration.class;
}

// 根据注解元信息 AnnotationMetadata 和注解属性 AnnotationAttributes 获取候选组件
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 调用 SpringFactoriesLoader 类的 loadFactoryNames 方法加载
// 参数值分别为 EnableAutoConfiguration.class 和 ClassLoader,该方法返回的是
// EnableAutoConfiguration 所对应的所有配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
...
// 返回该配置列表
return configurations;
}
// 返回EnableAutoConfiguration.class
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
// 返回ClassLoader
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}

// List列表去重-> list转为LinkedHashSet,然后在转为ArrayList
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}

// 获取排除的自动装配项
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
// @EnableAutoConfiguration注解的exclude属性值
excluded.addAll(asList(attributes, "exclude"));
// @EnableAutoConfiguration注解的excludeName属性值
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
// 在 application.properties配置文件中配置的排除项
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}

protected final List<String> asList(AnnotationAttributes attributes, String name) {
String[] value = attributes.getStringArray(name);
return Arrays.asList((value != null) ? value : new String[0]);
}
// 获取配置文件中配置的 spring.autoconfigure.exclude 属性值
private List<String> getExcludeAutoConfigurationsProperty() {
if (getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(getEnvironment());
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
.orElse(Collections.emptyList());
}
String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}
// 检查配置的排除项是否合法,需要参数:自动装配列表,排除项列表
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
// 1. 创建一个无效的排除项列表 invalidExcludes
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
// 2. 循环遍历这些排除项,将无效的放到 invalidExcludes列表中
for (String exclusion : exclusions) {
// 排除项无效的判断依据:是对应类加载器加载并且没有包含在 configurations列表中
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
// 3.若是排除项为非空,处理这些排除项
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
// 封装非法的排除项,并抛出运行时异常
protected void handleInvalidExcludes(List<String> invalidExcludes) {
StringBuilder message = new StringBuilder();
for (String exclude : invalidExcludes) {
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));
}



/*************************执行fileter过滤*******************/
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
// 1. 获取开始的时间(纳秒)
long startTime = System.nanoTime();
// 2. 将候选组件转为数组
String[] candidates = StringUtils.toStringArray(configurations);
// 3. 创建一个boolean类型的数组,长度为candidates.length
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// 4. 循环遍历这些过滤器
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 设置相关的属性值
invokeAwareMethods(filter);
// 调用该过滤器的 match 方法,通过参数 candidates 和 autoConfigurationMetadata 来判断是否匹配该过滤器。
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
// 遍历 match 列表
for (int i = 0; i < match.length; i++) {
// 若是第 i 项需要被过滤,将该项的 skip 设置为 true,并将该项设置为 null, 并将 skipped标识设置为 true
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
// 若是没有出现被过滤的,直接返回原始的
if (!skipped) {
return configurations;
}
// 若是有出现被过滤的,将被过滤的去掉,返回未被过滤的
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
...
return new ArrayList<>(result);
}
// 获取自动装配导入过滤器
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
// 1. 通过SpringFactoriesLoader加载这些过滤器
// 该版本存在三个过滤器 OnBeanCondition OnClassCondition OnWebApplicationCondition
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.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);
}
}
}

/*************************触发相关事件*******************/
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
// 1. 获取到 AutoConfigurationImportListener 列表
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
// 2. 若是列表非空,循环遍历 AutoConfigurationImportListener, 触发事件
if (!listeners.isEmpty()) {
// 创建一个 AutoConfigurationImportEvent 事件
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}

...
}

/*********************AutoConfigurationMetadataLoader**********************/
/*
自动装配元信息加载类,该类在自动装配方面的作用就是加载了 PATH 路径下面的资源文件,并将其转换为 Properties 文件,该文件会做为 AutoConfigurationMetadata 对象的一个属性(properties)。
在自动装配注解 @EnableAutoConfiguration 的拦截装配类 AutoConfigurationImportSelector的 selectImports 方法的第二步,通过自动装配元信息加载类 AutoConfigurationMetadata 的 loadMetadata 方法来加载自动装配的一些元信息。
*/
final class AutoConfigurationMetadataLoader {

// 自动装配元信息的存储路径
protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";

// 根据给定的 ClassLoader 获取到自动装配元信息
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}

// 根据给定的 ClassLoader 和 path 获取自动装配元信息
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
// 1. 读取到 PATH 路径下的资源文件,并将其以 key - value 的形式存储在 Properties文件中
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path): ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
// 2. 返回 AutoConfigurationMetadata 对象,该对象持有 Properties:properties属性
return loadMetadata(properties);
} catch (IOException ex) {
...
}
}

// 通过给定的一个Properties对象,获取到一个 AutoConfigurationMetadata对象,该方法返回的是实现类为 PropertiesAutoConfigurationMetadata。
static AutoConfigurationMetadata loadMetadata(Properties properties) {
return new PropertiesAutoConfigurationMetadata(properties);
}

// AutoConfigurationMetadata 实现类 PropertiesAutoConfigurationMetadata 的定义,该类持有一个类型为 Properties 的属性 properties。
private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata {

private final Properties properties;

PropertiesAutoConfigurationMetadata(Properties properties) {
this.properties = properties;
}
}
}


/***************************SpringFactoriesLoader*****************/
/*
SpringFactoriesLoader是Spring Framework工厂机制的加载器,该类的作用是加载 FACTORIES_RESOURCE_LOCATION 路径下的资源
*/
public final class SpringFactoriesLoader {
// 资源加载路径
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// 缓存,存储每个ClassLoader下的资源
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

// 通过给定的 Class 和 ClassLoader加载在配置文件中配置的类
// 参数分别为EnableAutoConfiguration和ClassLoader
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
// 1. 首先获取给定参数 Class<?> factoryClass 的类完全限定名
// 如:org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryClassName = factoryClass.getName();
// 2. 返回参数 ClassLoader 对应 MultiValueMap 中键为 factoryClassName 的列表
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

// 根据给定的ClassLoader,返回一个 MultiValueMap 类型的资源
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 1. 首先从缓存中获取
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
// 2.若是缓存中加载不到,就去 FACTORIES_RESOURCE_LOCATION 路径加载
try {
// 2.1 通过 ClassLoader和路径获取到 URL集合
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 2.2 遍历这些URL集合
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 2.3 将其转化为properties文件
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
// 2.4 将结果放入到缓存中去
cache.put(classLoader, result);
return result;
} catch (IOException ex) {}
}


public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
// 确定使用的ClassLoader
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 调用 loadFactoryNames 返回 factoryClass 所对应的配置
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
/// 创建一个集合,存放这些 class 的对象
List<T> result = new ArrayList<>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
// 排序后返回结果
AnnotationAwareOrderComparator.sort(result);
return result;
}
// 通过反射实例化
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
try {
Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
...
}
return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance();
}
catch (Throwable ex) {
...
}
}
}

关于 AutoConfigurationImportFilter

@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
...
}

@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {
...
}

@Order(Ordered.HIGHEST_PRECEDENCE + 20)
class OnWebApplicationCondition extends FilteringSpringBootCondition {
...
}

abstract class FilteringSpringBootCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {

// match方法,自动装配时会被自动调用
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
}

关于 @AutoConfigurationPackage 注解

// 关于该注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

public abstract class AutoConfigurationPackages {

private static final String BEAN = AutoConfigurationPackages.class.getName();


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

}

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

private static String[] addBasePackages(ConstructorArgumentValues constructorArguments, String[] packageNames) {
String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0, String[].class).getValue();
Set<String> merged = new LinkedHashSet<>();
merged.addAll(Arrays.asList(existing));
merged.addAll(Arrays.asList(packageNames));
return StringUtils.toStringArray(merged);
}
}

@SpringBootConfiguration


核心事件

ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
ApplicationStartingEvent
ApplicationReadyEvent
ApplicationFailedEvent

系统属性 System:getProperties


环境变量System:getEnv


嵌入式容器

为生产做准备

springboot踩坑记录
1. 当我们在springboot的配置文件中将启动端口配置为随机端口时 server.port=0,我们使用下面的方式无法获取到启动的真正端口,该port的值任然为0。

@Value("${server.port}")
private int port;

可以通过使用下面这种方式来获取真正的启动端口

private final Environment environment;
public OtherController(Environment environment) {
this.environment = environment;
}
private String getPort() {
return environment.getProperty("local.server.port");
}