Spring Boot自动装配的实现原理

158 阅读4分钟

Spring Boot自动装配的实现原理

原理就是注册了AutoConfigurationImportSelector,它是一个ImportSelector实现类,在配置类解析(ConfigurationClassPostProcessor.processConfigBeanDefinitions(..))的时候会将其返回的类名进行解析并注册到Spring中。

//开启自动装配的注解 主要配置排除那些类以及@Import(AutoConfigurationImportSelector.class)
@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 {};
}
​
  //自动装配的整体逻辑
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //1、读取需要自动装配的全限定类名
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        //去重
        configurations = removeDuplicates(configurations);
        //配置的排除
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        //2、过滤
        configurations = getConfigurationClassFilter().filter(configurations);
        //3、广播自动装配事件
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

ps:ConfigurationClassPostProcessorBeanFactoryPostProcessor实现类-BeanFactoryPostProcessor允许在对加载完的BeanDefinition进行修改或者增加,具体可以看Spring启动流程(AbstractApplicationContext.refresh())。而ConfigurationClassPostProcessor就是从启动配置类开始递归解析然后注册成BeanDefinition。例如我们常用的 @Bean methods@Import@ComponentScan等等。

@FunctionalInterface
public interface BeanFactoryPostProcessor {
   /**
    * Modify the application context's internal bean factory after its standard
    * initialization. All bean definitions will have been loaded, but no beans
    * will have been instantiated yet. This allows for overriding or adding
    * properties even to eager-initializing beans.
    * @param beanFactory the bean factory used by the application context
    * @throws org.springframework.beans.BeansException in case of errors
    */
   void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
​
}

1、spring.factories中读取需要自动装配的全限定类名

通过SpringFactoriesLoaderSPI机制加载META-INF/spring.factories得到哪些需要自动装载的类(key为org.springframework.boot.autoconfigure.EnableAutoConfiguration),在过滤掉剔除的类和重复类以及按条件过滤不需要加载的类。

//从META-INF/spring.factories得到哪些需要自动装载的类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
      AnnotationAttributes attributes) {
   //SPI机制 getSpringFactoriesLoaderFactoryClass() 指定加载的key
   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;
}
//返回的类名是EnableAutoConfiguration 即 读取factories中的key 为org.springframework.boot.autoconfigure.EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
}
//例如autoconfigure工厂下配置的自动装配key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
//Auto Configure 的配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

2、自动装配中条件装配

一些自动装配配置类可能存在前提条件,所以需要按照的自动装配元数据进行过滤,主要有三类过滤条件:

  • OnBeanCondition,判断是否存在某个bean作为过滤

     @ConditionalOnBean:某个bean存在时才装配
     @ConditionalOnMissingBean :某个bean不存在时才装配
     @ConditionalOnSingleCandidate:只有一个候选bean时
    
  • OnClassCondition,判断类是否存在做过滤条件

    @ConditionalOnClass:存在某类时才装配
    @ConditionalOnMissingClass:不存在某类时才加载
    
  • OnWebApplicationCondition:判断是否web应用程序作为过滤条件

    @ConditionalOnWebApplication:是web应用程序
    @ConditionalOnNotWebApplication:不是web应用程序
    

其实就是通过SPI机制加载的三个AutoConfigurationImportFilter

//读取Spring.factories下配置的AutoConfigurationImportFilter
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
​
# 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

ps:@Conditional 当指定的条件都满足时才有资格注册。 Conditional注解可以通过以下任何方式使用:

  • 作为直接或间接使用@Component注释的任何类的类型级注释,包括@Configuration类
  • 作为元注释,用于编写自定义构造型注释,例如@ConditionOnProperty
  • 作为任何@Bean方法的方法级注释
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

   //需要满足的条件集合
   Class<? extends Condition>[] value();

}

自动装配元数据来源

可以看到是读取 META-INF/spring-autoconfigure-metadata.properties下的配置文件,配置文件指定了Bean加载的条件以及顺序

ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
    //加载配置元数据
    this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
    this.filters = filters;
}
​
//AutoConfigurationMetadataLoader中的实现
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
​
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
   return loadMetadata(classLoader, PATH);
}
​
//META-INF/spring-autoconfigure-metadata.properties 部分配置
//SecurityFilterAutoConfiguration 自动装配要在SecurityAutoConfiguration后
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=

3、发布事件

提供事件机制便于在自动配置后做一些处理,和配置类过滤器一样通过SPI机制加载AutoConfigurationImportListener

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
   List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
   if (!listeners.isEmpty()) {
       //创建事件
      AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
       //执行监听器
      for (AutoConfigurationImportListener listener : listeners) {
         invokeAwareMethods(listener);
         listener.onAutoConfigurationImportEvent(event);
      }
   }
}
​
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

4、排序

配置类之间可能存在前后装配顺序,所以要在过滤后进行排序。例如@AutoConfigureAfter@AutoConfigureBefore、@AutoConfigureOrder这些,不过排序现在放在了配置类解析中,因为在类中可能也有装配的条件和顺序,而之前的使用自动装配元数据进行过滤时时还没有解析类的即还没有得到类上配置元数据,而且有可能最终需要加载配置类是跨ImportSelector的,提前排好序等于无用功。具体可以看DeferredImportSelector的作用-允许额外的过滤以及排序操作,具体的思路是给ImportSelector倒入的配置类进行分组,以组维度进行排序和过滤。