SpringBoot 核心介绍

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;
               }
          }
     }
     // ...
}

Add a Comment

电子邮件地址不会被公开。 必填项已用*标注

11 + 7 =