Spring Boot 核心介绍
Spring Boot 设计思想
约定优于配置(Convention over Configuration)的设计范式。减少配置数量、降低使用难度、提升开发效率。
Spring Boot 核心
- Spring 提供的 Ioc/AOP
- 自动装配
- 条件配置
- 开箱即用的 Starter,依赖自动装配和条件配置
- Actuator 监控
自动装配
核心注解 @EnableAutoConfiguration
@EnableAutoConfiguration
public @interface SpringBootApplication {
}
@EnableXXX 注解
Spring 3.1.x 开始支持各种 @Enable 注解,作用:把相关组件的 Bean 装配到 IoC 容器,减少代码配置和使用难度。
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@Import 是 @Enable 注解的核心,Spring 会解析 @Import 参数,来实现 Bean 的装配
AutoConfigurationImportSelector.class
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
}
继承关系
ImportSelector.class
方法表示返回的参数(String[])就是要装配到 IoC 容器的类,可以实现批量装配,可以在方式实现中进行更灵活复杂的装配逻辑
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
AutoConfigurationImportSelector 的实现
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 后面会有介绍
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 先关注此方法
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
// 获得要自动装配的类
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取 exclude\excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取所有自动装配的配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 去除重复配置
configurations = removeDuplicates(configurations);
// 去除exclude配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
// 广播装配事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
获取所有自动装配的配置类,使用到 SpringFactoriesLoader
,扫描 classpath 下的 META-INF/spring.factories
文件得到结果
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;
}
META-INF/spring.factories
内容是 KEY=VALUE
格式
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
xxxx.xxxxx.xxxListener,\
xxxx.xxxxx.xxxListener,\
xxxx.xxxxx.xxxListener,\
上面的方法获取到 KEY 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration
的 VALUE,发现都是
XXXXConfiguration 类,就是基于 JavaConfig 的配置类.
@Configuration
@ConditionalOnProperty(
value = {"eureka.client.enabled"},
matchIfMissing = true
)
@ConditionalOnDiscoveryEnabled
public class EurekaClientAutoConfiguration {
}
广播装配事件
private void fireAutoConfigurationImportEvents(List<String> configurations,
Set<String> exclusions) {
// 再次用到 SpringFactoriesLoader,获取所有 AutoConfigurationImportListener 实现类,并广播事件
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class,
this.beanClassLoader);
}
条件配置
一种条件配置的方法是使用条件注解
@Conditional,参数是 Condition 数组
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
Condition 接口提供 matches 方法,符合配置条件的返回 true
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
实现一个自定义条件注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnXXXXXCondition.class)
public @interface ConditionalOnXXXXX {
}
public OnXXXXXCondition implements Conditional {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO
}
}
@OnXXXXXCondition
public TargetClass {
// TODO
}
Springboot 提供的常用条件注解
org.springframework.boot.autoconfigure.condition
条件化注解 | 配置生效条件 |
---|---|
@ConditionalOnBean | 配置了某个特定 Bean |
@ConditionalOnMissingBean | 没有配置特定的 Bean |
@ConditionalOnClass | Classpath 里有指定的类 |
@ConditionalOnMissingClass | Classpath 里缺少指定的类 |
@ConditionalOnExpression | 给定的 Spring Expression Language 表达式计算结果为 true |
@ConditionalOnJava | Java 的版本匹配特定值或者一个范围值 |
@ConditionalOnJndi | 参数中给定的 JNDI 位置必须存在一个,如果没有给参数,则要有 JNDI InitialContext |
@ConditionalOnProperty | 指定的配置属性要有一个明确的值 |
@ConditionalOnResource | Classpath 里有指定的资源 |
@ConditionalOnWebApplication | 这是一个 Web 应用程序 |
@ConditionalOnNotWebApplication | 这不是一个 Web 应用程序 |
批量条件配置
Spring boot 提供了 spring-autoconfigure-metadata.properties
文件来实现批量自动装配条件配置,可以更有效的降低启动时间
META-INF/spring-autoconfigure-metadata.properties 文件格式为:自动配置类的全路径.条件=值
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration.ConditionalOnClass=\
org.influxdb.InfluxDB
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration.AutoConfigureAfter=\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.session.RedisSessionConfiguration.ConditionalOnClass=\
org.springframework.data.redis.core.RedisTemplate,org.springframework.session.data.redis.RedisOperationsSessionRepository
批量自动装配的代码
回到前面的自动装配的代码中
// 加载 META-INF/spring-autoconfigure-metadata.properties 文件
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
进入 getAutoConfigurationEntry
方法
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
// ...
// 执行条件过滤
configurations = filter(configurations, autoConfigurationMetadata);
// ..
}
private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// AutoConfigurationImportFilter 执行 filter
// 实现类:
// OnBeanCondition
// OnClassCondition
// OnWebApplicationCondition
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
// ...
}