Factories配置对象机制(SPI)

* 作用

是SPI的一种实现方法,服务调用方法只需提供接口名称,而服务提供方需要在类路径的META-INF/spring.factories位置配置接口的实现类,服务调用后在根据类名进行反射实例化即可

如下

蓝色org.springframework.boot.SpringApplicationRunListener表示调用方需要的接口

绿色org.springframework.boot.context.event.EventPublishingRunListener表示服务提供方的实现类

  

* SpringApplication提供的getSpringFactoriesInstances方法就可实现获取指定接口名称的实例集合

  private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
      return getSpringFactoriesInstances(type, new Class<?>[] {});
   }

   private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
      ClassLoader classLoader = getClassLoader();
      //获取当前项目所有META-INF/spring.factories的配置信息,找出指定接口名称锁配置的所有实现类名称集合
      Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
      //反射获取names的所有对象,parameterTypes对应得构造器的参数类型
      List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
      //基于@OrderPriority注解的排序
      AnnotationAwareOrderComparator.sort(instances);
      return instances;
   }

   @SuppressWarnings("unchecked")
   private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
         ClassLoader classLoader, Object[] args, Set<String> names) {
      List<T> instances = new ArrayList<>(names.size());
      for (String name : names) {
         try {
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
         }
         catch (Throwable ex) {
            throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
         }
      }
      return instances;
   }
 

* SpringFactoriesLoader.loadSpringFactories

获取当前项目所有META-INF/spring.factories的配置信息,找出指定接口名称锁配置的所有实现类名称集合

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
   Map<String, List<String>> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   result = new HashMap<>();
   try {

//类加载器获取所有META-INF/spring.factories的路径集合
      Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();

//把URL url封装为UrlResource,获取底层的输入流InputStream,再使用Properties.load把文件内容转为Properties 对象
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);

//遍历Properties,获取指定接口类型的所有值,存储为Map<String, List<String>>
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            String[] factoryImplementationNames =
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
            for (String factoryImplementationName : factoryImplementationNames) {
               result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                     .add(factoryImplementationName.trim());
            }
         }
      }

      // Replace all lists with unmodifiable lists containing unique elements
      result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
            .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
      cache.put(classLoader, result);
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
   return result;
}

自动配置原理解析

@SpringBootApplication注解

@SpringBootApplication()

@ComponentScan(basePackages = {..})
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class,args);
    }
}

* @AliasFor:表示此注解方法继承了annotation 指定注解的方法,可进行覆盖

 如这样标注@SpringBootApplication(scanBasePackages = {"com.atguigu.springcloud"})

 那么“xxx”最终就会覆盖或指定父注解@ComponentScan中basePackages 的值

* @SpringBootConfiguration元注解为@Configuration,几乎可等同于@Configuration。说明@SpringBootApplication标注的类是一个java配置类

  所以@ComponentScan会在容器初始化时的执行bean工厂后置器时,在配置类解析类解析时会被处理

* @EnableAutoConfiguration表示启动自动配置功能,这时核心功能

* @ComponentScan:由于没有显示指定basePackage的值,所以默认的扫描路径为添加的@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 指定注解的属性,可进行覆盖
   @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 {};
   @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
   Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

   @AliasFor(annotation = Configuration.class)
   boolean proxyBeanMethods() default true;

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

   @AliasFor(annotation = Configuration.class)
   boolean proxyBeanMethods() default true;

}

@EnableAutoConfiguration启动自动配置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

   /**
    * Environment property that can be used to override when auto-configuration is
    * enabled.
    */
   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

   /**
    * 排除指定的字段配置类,不进行处理
    */
   Class<?>[] exclude() default {};
   String[] excludeName() default {};

}

@AutoConfigurationPackage

* import了AutoConfigurationPackages.Registrar

- 作用创建bean名称为AutoConfigurationPackages、类型为BasePackages的bean

- 此类的作用仅仅是保存了原始注解的包路径

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

//AutoConfigurationPackages

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

//原始注解的包路径
      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
   }
   @Override
   public Set<Object> determineImports(AnnotationMetadata metadata) {
      return Collections.singleton(new PackageImports(metadata));
   }

}

* 向容器定义表注册名称为AutoConfigurationPackages的bean定义,类型为BasePackages,简单的翻转了packageNames自动配置文件的基础包路径信息

  使用getInstanceSupplier的方法创建实例BasePackages类型

registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));

static final class BasePackagesBeanDefinition extends GenericBeanDefinition {

   private final Set<String> basePackages = new LinkedHashSet<>();

   BasePackagesBeanDefinition(String... basePackages) {
      setBeanClass(BasePackages.class);
      setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
      addBasePackages(basePackages);
   }
   @Override
   public Supplier<?> getInstanceSupplier() {
      return () -> new BasePackages(StringUtils.toStringArray(this.basePackages));
   }
   private void addBasePackages(String[] additionalBasePackages) {
      this.basePackages.addAll(Arrays.asList(additionalBasePackages));
   }

}

@Import(AutoConfigurationImportSelector.class)(核心)

* 从环境变量获取spring.boot.enableautoconfiguration的值,作为开关,默认为true

* 从META-INF/spring.factories中获取所有EnableAutoConfiguration接口配置的所有实现类

  ①获取注解设置的exclude、excludeName和spring.autoconfigure.exclude属性指定排除配置类集合

    并检查下最终过滤后的configurations存不存在指定排除的类,存在就报错

  ②从META-INF/spring.factories中获取AutoConfigurationImportFilter接口配置的所有实现类,遍历调用match对所有configurations的进行匹配过滤

  ③从META-INF/spring.factories中获取AutoConfigurationImportListener接口配置的所有实现,遍历触发onAutoConfigurationImportEvent方法

* 返回过滤后的自动配置类集合,

  由于AutoConfigurationImportSelectort 是DeferredImportSelector类型 ,所有对所有自动配置的import解析是在ConfigurationClassParser.parse的最后一步进行的

public class AutoConfigurationImportSelectort implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

   private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
   private static final String[] NO_IMPORTS = {};
   private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
   private ConfigurableListableBeanFactory beanFactory;
   private Environment environment;
   private ClassLoader beanClassLoader;
   private ResourceLoader resourceLoader;
   private ConfigurationClassFilter configurationClassFilter;

   @Override
   public String[] selectImports(AnnotationMetadata annotationMetadata) {
      //从环境变量获取spring.boot.enableautoconfiguration的值,作为开关
      if (!isEnabled(annotationMetadata)) {
         return NO_IMPORTS;
      }
      //META-INF/spring.factories中获取EnableAutoConfiguration接口配置的所有实现类
      //经过过滤处理
      AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
      //返回过滤后的自动配置类集合
      return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
   }
   
   protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
      //从环境变量获取spring.boot.enableautoconfiguration的值,作为开关
      if (!isEnabled(annotationMetadata)) {
         return EMPTY_ENTRY;
      }
      //获取EnableAutoConfiguration主键上设置的属性,是经过@AliasFor处理的
      AnnotationAttributes attributes = getAttributes(annotationMetadata);
      //META-INF/spring.factories中获取EnableAutoConfiguration接口配置的所有实现类
      //例如org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      //    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
      //    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
      List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
      //去除重复的,使用LinkedHashSet进行封装即可
      configurations = removeDuplicates(configurations);
      //获取注解设置的excludeexcludeNamespring.autoconfigure.exclude指定排除类集合
      Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        //检查下指定排除的类是不是收集到自动配置类,即在configurations存不存在,不存在就报错
      checkExcludedClasses(configurations, exclusions);
      configurations.removeAll(exclusions);
      //META-INF/spring.factories中获取AutoConfigurationImportFilter接口配置的所有实现类,遍历调用match进行匹配过滤
      configurations = getConfigurationClassFilter().filter(configurations);
      //META-INF/spring.factories中获取AutoConfigurationImportListener接口配置的所有实现,遍历触发onAutoConfigurationImportEvent方法
      fireAutoConfigurationImportEvents(configurations, exclusions);
      //封装了configurationsexclusions
      return new AutoConfigurationEntry(configurations, exclusions);
   }
   
   //

}

对@Conditional注解shouldSkip的扩展

* @ConditionalXXX

@ConditionalOnProperty (环境中是有指定key,指定value的值,才会实例化一个Bean)

@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)

@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)

@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)

@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)

@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)

@ConditionalOnNotWebApplication(不是web应用)

* havingValue和matchIfMissing

  - 如果havingValue存在指定,那么matchIfMissing就无效了

  - 如果havingValue不存在指定:

   ①matchIfMissing = true,则此@ConditionalOnXXX,一定成立

   ②matchIfMissing = false,只有name配置项在配置文件中存在才会成立

//AopAutoConfigurat是配置类。以@EnableAutoConfiguration已通过import方法注册到定义表中,在bean工厂后置处理阶段被解析
// 1、先判断配置类的是否符合@Conditiona条件,这里需要环境中存在spring.aop.auto = true matchIfMissing = true表示不存在也没关系,可以解析
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfigurationxx {

   //2、解析内部配置类,一样的流程
   //先判断配置类的是否符合@Conditiona条件,这里需要类路径下需要Advice类文件,符合就解析此配置类
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnClass(Advice.class)
   static class AspectJAutoProxyingConfiguration {

      //3、解析内部配置类,一样的流程
      //判断配置类的是否符合@Conditiona条件,这里需要环境中存在spring.aop.proxy-target-class = false,如果可以解析
      //3.1、解析@ComponentScan@ComponentScans@Import@Bean@ImportResource@PropertySource等注解
      //     这里@EnableAspectJAutoProxy里有注解@Import(AspectJAutoProxyRegistrar.class),最终向容器db表中注册了AnnotationAwareAspectJAutoProxyCreator定义
      @Configuration(proxyBeanMethods = false)
      @EnableAspectJAutoProxy(proxyTargetClass = false)
      @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
            matchIfMissing = false)
      static class JdkDynamicAutoProxyConfiguration {

      }

//同理
      @Configuration(proxyBeanMethods = false)
      @EnableAspectJAutoProxy(proxyTargetClass = true)
      @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
            matchIfMissing = true)
      static class CglibAutoProxyConfiguration {

      }

   }


   //4、这个ClassProxyingConfiguration内部类会在SpringApplication上的@ComponentScan解析扫描到,此时也会进行@Conditiona判断,只有通过了才会注册进行去db表

//因为@Configuration有元注解@Component
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnMissingClass("org.aspectj.weaver.Advice")
   @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
         matchIfMissing = true)
   static class ClassProxyingConfiguration {

      ClassProxyingConfiguration(BeanFactory beanFactory) {
         if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
         }
      }

   }

}

自动配置的顺序控制

Spring Boot给我们提供了控制顺序的注解

* @AutoConfigureBefore:表明当前配置类在某一个配置类加载前加载。

* @AutoConfigureAfter:表明当前配置类在某一个配置类加载后加载。

* @AutoConfigureOrder:表示绝对顺序(数字越小,优先顺序越高)

例如

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration { ... }

* 执行时机

  - 在ConfigurationClassPostProcessor中所有bd注册的配置类解析完成之后,最后才处理下延迟导入类,即this.deferredImportSelectorHandler.process()方法(见上说明)

@EnableAutoConfiguration中@Import()的自动配置处理类AutoConfigurationImportSelector就是实现了DeferredImportSelector

  - 在处理此延迟导入类中,获取到所有需要导入的配置类后,会进行exclude属性的排除、再进行排序,调用getInPriorityOrder进行排序

  

* 排序逻辑

 - 先通过字母排序

 - 再使用@AutoConfigureOrder排序

 - 再使用 @AutoConfigureBefore @AutoConfigureAfter进行排序


//deferredImportSelectorHandler.process()方法最终调用AutoConfigurationSorter.getInPriorityOrder()方法对自动配置类组进行排序
List<String> getInPriorityOrder(Collection<String> classNames) {
   AutoConfigurationSorter.AutoConfigurationClasses classes = new AutoConfigurationSorter.AutoConfigurationClasses(this.metadataReaderFactory,
         this.autoConfigurationMetadata, classNames);
   List<String> orderedClassNames = new ArrayList<>(classNames);
   //通过字母排序
   Collections.sort(orderedClassNames);
   //通过@AutoConfigureOrder
   orderedClassNames.sort((o1, o2) -> {
      int i1 = classes.get(o1).getOrder();
      int i2 = classes.get(o2).getOrder();
      return Integer.compare(i1, i2);
   });
   //通过 @AutoConfigureBefore @AutoConfigureAfter进行排序
   orderedClassNames = sortByAnnotation(classes, orderedClassNames);
   return orderedClassNames;
}

* @AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder这三个注解只能作用于自动配置类,而不能是自定义的@Configuration配置类。

  - 原因在于这三个注解失效的时机,这方法只会处理通过@Import()导入的类为DeferredImportSelector类型的类,而我们的

    说要所有自动配置类,即spring.factories下指定的自动配置类,会在这一步才开始导入,它的优先顺序最低(执行时机相对是最晚的);

通过扫描进来的一般都是你自己自定义的配置类,会自动配置之前载入

- 如果要对自定义的@Configuration配置类进行排序处理,只需要下面两步:

① 把自定义的@Configuration配置类挪动到Application扫描不到的包内,切记:一定且必须是扫描不到的包内

    ②当前工程里增加配置META-INF/spring.factories,内容为

org.springframework.boot.autoconfigure.EnableAutoConfiguration=自定义的@Configuration配置类

@EnableConfigurationProperties/@ConfigurationProperties属性值绑定

使用

* @EnableConfigurationProperties(xxx.class)

①就会在容器中创建xxx类型的实例,并已经绑定了属性,其他对象就可以注入此属性配置对象,通过getter获取配置值

②如果不指定@EnableConfigurationProperties(),不会加入注册表(需要通过其他方法加入),@ConfigurationProperties注解自然不会被解析

  或者属性类(如下LiteServiceConfig)通过其他方法注册bean定义到工厂中,例如在LiteServiceConfig上添加@Component注解

@Configuration
@EnableConfigurationProperties(LiteServiceConfig.class) // 激活自动配置(指定文件中的配置)
    public class LiteFlowAutoConfiguration {
}

@ConfigurationProperties(prefix = "app.service")
public class LiteServiceConfig {
   private String serviceUid;
}

* 标注在一个@Bean方法上,可把属性绑定到工厂方法返回的对象上

@Configuration
public class test {
   @Bean
   @ConfigurationProperties(prefix = "kaka")
   public User uerskaka(){
      return new User();
   }
}

* 配置文件中指定的属性值会赋值到对应bean的属性上

如果属性为对象类型,环境中存在属性对应的值,为基本类型,那么这个值将会尝试作为此对象类型的构造参数进行创建,例如

spring:

cloud:

gateway:

routes:

# =============第一个route========================

- host_example_to_httpbin=${test.uri}, Host=**.example.org

# ==============第二个route=======================

 - id: host_foo_path_headers_to_httpbin

uri: ${test.uri}

ConfigurationProperties(GatewayProperties.PREFIX)
@Validated
public class GatewayProperties {
   public static final String PREFIX = "spring.cloud.gateway";
   /**
    * List of Routes.
    */
   @NotNull
   @Valid
   private List<RouteDefinition> routes = new ArrayList<>();

}

@Validated
public class RouteDefinition {

   private String id;

   public RouteDefinition() {}

   public RouteDefinition(String text) {
      int eqIdx = text.indexOf('=');}
      setId(text.substring(0, eqIdx));

      ...

   }

原理

(1)@EnableConfigurationProperties注解

@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
   String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
   Class<?>[] value() default {};
}

  1. EnableConfigurationPropertiesRegistrar

* 添加一份bean后处理器,ConfigurationPropertiesBindingPostProcessor后置处理器,在bean初始化后解析bean上的@ConfigurationProperties注解,对属性进行绑定

  基于Binder.get(environment).bind("属性key", 对象);这样可以把属性值合适的绑定到一个对象中

* 获取EnableConfigurationProperties注解中value指定的类,直接注册到bd表中

* 因此可见,此@ConfigurationProperties注解配置的属性时没有动态刷新的功能的,可以添加@RefreshScope且在配置修改时发布RefreshEvent事件解决

class EnableConfigurationPropertiesRegistrarxxx implements ImportBeanDefinitionRegistrar {

   private static final String METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME = Conventions
         .getQualifiedAttributeName(EnableConfigurationPropertiesRegistrarxxx.class, "methodValidationExcludeFilter");

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      //注册必要的处理bean,,比如注册ConfigurationPropertiesBindingPostProcessor
      registerInfrastructureBeans(registry);
      registerMethodValidationExcludeFilter(registry);
      ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
      //获取基础配置类上所有EnableConfigurationProperties的类型的注解(包括元注解),父类上允许基础的EnableConfigurationProperties(添加@Inheric
      //遍历调用ConfigurationPropertiesBeanRegistrar.register方法进行处理:
      //收集每个配置信息类的ConfigurationProperties注解,使用prefix-type作为bean内注入定义表
      getTypes(metadata).forEach(beanRegistrar::register);
   }

//

}

更多推荐