SpringBoot应用--04-自动化配置机制

 1. 了解SPI机制

 详见 https://www.cnblogs.com/dduo/p/14984693.html

 2. 使用SpringBoot的Enable模块

  方式一: 使用注解+@Import(ImportSelector实现类)

  step1: 自定义Server接口以及实现类型

/**
 * @description: 定义服务器接口---Server以及服务类型Server.Type
 */
public interface Server {
    /*启动服务器*/
    void start();
    /*关闭服务器 */
    void stop();
    enum Type{
        HTTP,       //HTTP服务器
        FTP         //FTP服务器
    }
}
public class HttpServer implements Server {
    @Override
    public void start() {
        System.out.println("HTTP Server is starting");
    }
    @Override
    public void stop() {
        System.out.println("HTTP Server is stopping");
    }
}
public class FtpServer implements Server {
    @Override
    public void start() {
        System.out.println("FTP Server is starting");
    }
    @Override
    public void stop() {
        System.out.println("FTP Server is stopping");
    }
}

  step2: 自定义ImportSelector接口实现类

public class ServerImportSelector implements ImportSelector{
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //1. 读取@EnableServer中所有属性方法,当前仅有type() ,其中key为属性名称,value为属性方法的返回对象
        Map<String,Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableServer.class.getName());
        //2. 获取名为type的属性方法,并且强制转化成Server.Type类型
        Server.Type type = (Server.Type) annotationAttributes.get("type");
        //3. 导入的类名称数组
        String[] importClassNames = new String[0];
        switch (type){
            case HTTP:
                importClassNames = new String[]{HttpServer.class.getName()};
                break;
            case FTP:
                importClassNames = new String[]{FtpServer.class.getName()};
                break;
        }
        return importClassNames;
    }
}

  step3: 自定义注解使用@Import引入ImportSelector接口实现类

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServerImportSelector.class)
public @interface EnableServer {
    /*设置服务器类型 */
    Server.Type type();
}

  step4: Spring容器主启动类

/* @description: ImportSelector接口编程实现引导类 */
@EnableServer(type = Server.Type.FTP)
public class EnableServerBootstrap {
    public static void main(String[] args) throws Exception{
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(EnableServerBootstrap.class);
        ctx.refresh();
        Server server = ctx.getBean(Server.class);
        server.start();
        Thread.sleep(5000);
        server.stop();
        ctx.close();
    }
}

  step5: Spring容器主启动类测试,发现自动加载了FTPServer组件

  方式二: 使用注解+@Import(ImportBeanDefinitionRegistrar实现类)

  step1: 自定义Server接口以及实现类型

  step2: 自定义ImportSelector接口实现类  ---与方法一中一样

  step3: 自定义ImportBeanDefinitionRegistrar接口实现类

/**@description: 自定义ImportBeanDefinitionRegistrar接口实现 */
public class ServerImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        // 复用 ServerImportSelector实现
        ImportSelector importSelector = new ServerImportSelector();
        String[] selectedClassNames = importSelector.selectImports(annotationMetadata);
        // 创建Bean定义
        Stream.of(selectedClassNames)
                // 转化为BeanDefinitionBuilder对象
                .map(BeanDefinitionBuilder::genericBeanDefinition)
                // 转化为BeanDefinition对象
                .map(BeanDefinitionBuilder::getBeanDefinition)
                .forEach(beanDefition -> {
                    //  注册BeanDefinition到BeanDefinitionRegistry(Spring IOC 容器中)
                    BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefition,beanDefinitionRegistry);
                });
    }
}

  step4: 自定义注解使用@Import引入ImportBeanDefinitionRegistrar接口实现类

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServerImportBeanDefinitionRegistrar.class)
public @interface EnableServer2 {
    /* 设置服务器类型 */
    Server.Type type();
}

  step5: Spring容器主启动类

/* @description: ImportBeanDefinitionRegistrar接口编程实现引导类 */
@EnableServer2(type = Server.Type.HTTP)
public class EnableServerBootstrap {
    public static void main(String[] args) throws Exception{
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(EnableServerBootstrap.class);
        ctx.refresh();
        Server server = ctx.getBean(Server.class);
        server.start();
        Thread.sleep(1000);
        server.stop();
        ctx.close();
    }
}

  step6: Spring容器主启动类启动测试--结果与方法一一致

 3. @SpringBootApplication注解

  SpringBootApplication注解是一个组合注解,包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解

@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 = 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 {};
}

4. @EnableAutoConfiguration注解

@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 {};
}

4.1  @Import(AutoConfigurationImportSelector.class) 

AutoConfigurationImportSelector类是ImportSelector接口的实现类,关键方法是selectImports方法

4.2  selectImports方法解析

  @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 从注解EnableAutoConfiguration的属性中获取是否支持自动化装配
if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }

     // 加载 META-INF/spring-autoconfigure-metadata.properties文件 AutoConfigurationMetadata autoConfigurationMetadata
= AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
     // 获取EnableAutoConfiguration注解上属性信息 exclude excludeName AnnotationAttributes attributes
= getAttributes(annotationMetadata);
    // 获取所有自动配置类 List
<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
    // 剔除重复的自动配置类 configurations
= removeDuplicates(configurations);
     // 根据EnableAutoConfiguration注解上 exclude excludeName属性 剔除需要排除的自动装配类 + 校验排除的自动装配类是否是SpringBoot中spring.factories文件中定义的 Set
<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);
     // 根据spring.factories文件中的AutoConfigurationImportFilter 过滤类处理自动装配类(根据spring-autoconfigure-metadata.properties中的各个自动装配类的元数据) configurations
= filter(configurations, autoConfigurationMetadata);
     // 根据spring.factories文件中的AutoConfigurationImportListener事件监听器发布并处理监听事件 fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations); }
4.2.1  isEnabled方法详情--查看当前Spring应用是否支持自动装配
protected boolean isEnabled(AnnotationMetadata metadata) {
    if (getClass() == AutoConfigurationImportSelector.class) {
        return getEnvironment().getProperty( EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,true);
    }
    return true;
}
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
4.2.2 AutoConfigurationMetadataLoader.loadMetadata方法详情
final class AutoConfigurationMetadataLoader {
    protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";
    private AutoConfigurationMetadataLoader() {}
    public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
        return loadMetadata(classLoader, PATH);
    }

    static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
        try {
            Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path): ClassLoader.getSystemResources(path);
        // 使用一个Properties对象将文件中的配置信息读取进去 Properties properties
= new Properties(); while (urls.hasMoreElements()) { properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement()))); } return loadMetadata(properties); }catch (IOException ex) { throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex); } }   // 该方法的作用是将Properties对象数据包进PropertiesAutoConfigurationMetadata类中,该类中提供了更加方便获取数据的方法 static AutoConfigurationMetadata loadMetadata(Properties properties) { return new PropertiesAutoConfigurationMetadata(properties); }
AutoConfigurationMetadata 接口定义
public interface AutoConfigurationMetadata {
    /*Return {@code true} if the specified class name was processed by the annotation */
    boolean wasProcessed(String className);

    /* Get an {@link Integer} value from the meta-data. */
    Integer getInteger(String className, String key);

    /*Get an {@link Integer} value from the meta-data. */
    Integer getInteger(String className, String key, Integer defaultValue);

    /*Get a {@link Set} value from the meta-data.*/
    Set<String> getSet(String className, String key);

    /*Get a {@link Set} value from the meta-data.*/
    Set<String> getSet(String className, String key, Set<String> defaultValue);

    /* Get an {@link String} value from the meta-data.*/
    String get(String className, String key);

    /*Get an {@link String} value from the meta-data.*/
    String get(String className, String key, String defaultValue);
}
spring-autoconfigure-metadata.properties文件数据  定义了要自动加载的类的一些元数据信息(例如加载条件Conditional,绝对加载顺序Order,相对加载顺序Before|After等)
#Thu Apr 05 11:37:28 UTC 2018
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,org.springframework.data.cassandra.core.ReactiveCassandraTemplate,reactor.core.publisher.Flux
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration=
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.jms.artemis.ArtemisXAConnectionFactoryConfiguration=
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration=
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.ConditionalOnClass=com.couchbase.client.java.CouchbaseBucket,com.couchbase.client.java.Cluster
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.core.RabbitTemplate,com.rabbitmq.client.Channel
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureOrder=-2147483648
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration=
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureOrder=-2147483638
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration.ConditionalOnClass=com.couchbase.client.java.Bucket,org.springframework.data.couchbase.repository.ReactiveCouchbaseRepository,reactor.core.publisher.Flux
org.springframework.boot.autoconfigure.batch.BatchConfigurerConfiguration.Configuration=
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.jdbc.core.JdbcTemplate
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration=
4.2.3 getAttributes方法追踪 --获取@EnableAutoConfiguration注解上exclude excludeName 需要排除的配置类
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
        String name = getAnnotationClass().getName();
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
        Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
                + " annotated with " + ClassUtils.getShortName(name) + "?");
        return attributes;
    }
4.2.4 getCandidateConfigurations方法追踪
  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;
    }
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

SpringFactoriesLoader类中方法: SpringBoot SPI机制读取数据的加载类,加载META-INF/spring.factories文件中key为EnableAutoConfiguration的所有自动装配类

   public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) { return result; }
        try {
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                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());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        } catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

4.2.5 removeDuplicates 方法追踪--将读取的自动配置类进行去重操作-使用LinkedHashSet
  protected final <T> List<T> removeDuplicates(List<T> list) {
        return new ArrayList<>(new LinkedHashSet<>(list));
    }
4.2.6 getExclusions方法追踪-从EnableAutoConfiguration注解上属性信息 exclude excludeName 需要排除的自动装配类
    protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        Set<String> excluded = new LinkedHashSet<>();
        excluded.addAll(asList(attributes, "exclude"));
        excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
        excluded.addAll(getExcludeAutoConfigurationsProperty());
        return excluded;
    }
4.2.7 checkExcludedClasses方法追踪-检查是否存在 当前类路径下存在但是自动配置类集合中不存在的排除类,如果有抛出异常,言外之意排除的自动装配类只能是SpringBoot中定义的
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
        List<String> invalidExcludes = new ArrayList<>(exclusions.size());
        for (String exclusion : exclusions) {
            if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
                invalidExcludes.add(exclusion);
            }
        }
        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));
    }
4.2.8 filter方法追踪-根据spring.factories文件中的AutoConfigurationImportFilter 过滤类处理自动装配类(根据spring-autoconfigure-metadata.properties中的各个自动装配类的元数据)
    private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
        long startTime = System.nanoTime();
        String[] candidates = StringUtils.toStringArray(configurations);
        boolean[] skip = new boolean[candidates.length];
        boolean skipped = false;
        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;
                    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]);
            }
        }
        if (logger.isTraceEnabled()) {
            int numberFiltered = configurations.size() - result.size();
            logger.trace("Filtered " + numberFiltered + " auto configuration class in "+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
        }
        return new ArrayList<>(result);
    }
    // 获取spring.factories文件中的AutoConfigurationImportFilter 过滤类
    protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        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);
            }
        }
    }
4.2.9 fireAutoConfigurationImportEvents方法追踪-根据spring.factories文件中的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);
            }
        }
    }
    protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
    }
    
    
    # Auto Configuration Import Listeners
    org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
    org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
    class ConditionEvaluationReportAutoConfigurationImportListener implements AutoConfigurationImportListener, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
        if (this.beanFactory != null) {
            ConditionEvaluationReport report = ConditionEvaluationReport.get(this.beanFactory);
            report.recordEvaluationCandidates(event.getCandidateConfigurations());
            report.recordExclusions(event.getExclusions());
        }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory)? (ConfigurableListableBeanFactory) beanFactory : null;
    }
4.2.10 StringUtils.toStringArray(configurations)

  返回经过过滤之后的自动装配类的全类名信息列表

4.2.11 AutoConfigurationImportSelector初始化与Spring交互

 参照此位仁兄博文 : https://blog.csdn.net/shiyan719902675/article/details/106378107

posted @ 2021-07-09 17:31  521pingguo1314  阅读(136)  评论(0编辑  收藏  举报