【SpringBoot】SpringBoot自动装配源码分析

SpringBoot如何实现的自动装配

1. 基本描述

SpringBoot的最主要的功能就是自动装配功能。通过自动配置,SpringBoot大大减少了使用java框架的复杂程度,并且可以统一的将配置文件写入application.yml中。并且,SpringBoot为常见的应用提供了一套默认的配置,使得使用者无需编写重复的配置代码,可以专心于业务的实现。

第三方框架和其他的Spring组件只要使用Spring提供的SPI规范,就可以通过编写starter的方式被通过SpringBoot的方式组合到SpringBoot中。

SpringBoot首先会从配置文件中读取所有的自动配置类,这些配置类我们称为待选配置类。然后通过筛选,仅注入项目所需要的配置类,这些配置类被注册后就会生效,为我们的项目提供自动配置功能。

2. 原理和实现

2.1 配置的读取和筛选

SpringBoot实现自动装配的主要实现是依靠@EnableAutoConfiguration注解实现的,这个注解被包含在了Spring的入口注解@SpringBootApplication中

@SpringBootApplication

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}

SpringBootApplication注解包括三个重要的子注解

  1. @SpringBootConfiguration 是一个对 @Configuration 的包装,其加入了一个 @Index 注解作为对SpringBoot应用加速的注解。
  2. @ComponentScan注解是SpringBoot的组件扫描注解。
  3. 主要实现自动配置的注解是 @EnableAutoConfiguration注解

@EnableAutoConfiguration注解的实现如下:

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

可以看到,在这里SpringBoot引用了AutoConfigurationImportSelector类,这个类继承了ImportSelector接口,这个接口提供一个方法,可以在获取到IOC容器中的Bean的元信息之后,来判断是否将Bean注入IOC容器。这个方法就是selectImports方法。参数为AnnotationMeataInfo注解元信息。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

显然的,上述方法中最主要的方法是getAutoConfigurationEntry方法,这个方法,打开这个方法的实现,可以看到他通过getCandidateConfiguration方法获取了所有的待选配置的全限定名并组成了一个list。

    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        ...
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        ...
    }

深入这个方法,就可以找到最终调取自动配置类的方法loadFactoryName,loadSpringFactories是一个Map>数据结构。这个数据结构首先加载了一个配置文件META-INF/spring.fatories,然后,这个数据结构从配置文件中读取所有的待选配置的全限定名,然后根据配置文件中的分组将他们放置到不同的key对应的List中。

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        return configurations;
    }

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ...
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");

spring.factories中的格式大致如下:可以看到其中不仅定义了许多的自动配置类,还定义了初始化类,监听器,前置处理器等。

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# 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

# 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,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\

至此,我们弄明白了待选类是如何被读取的。

  1. 首先,SpringBoot把常用框架的自动配置类的全限定名放置在了spring.factories文件中;
  2. 之后,SpringBoot通过@EnableAutoConfiguration注解引入了配置类AutoConfigurationImportSelector,这个类实现了ImportSelector的SelectImport方法,这个方法支持通过全限定名往Spring中进行IOC注入;
  3. 然后,在这个方法中,通过调用getAutoConfigurationEntry方法,从Spring.factories中读取预先设定的spring.factories中的自动配置类全限定名。将这些全限定名交给ImportSelector即可实现注入。

那么,我们是否每次都注入了所有的配置呢?显然我们应该按需注入,这里就必须提到SpringBoot提供的注解

@ConditionalOn....(一些条件))

当一个配置类被这个注解标识,那么仅在这个注解标志的条件满足的时候,这个配置类才会生效。以Redis为例,如果SpringBoot在当前的项目文件中检测不到存在RedisOperations这种Redis的核心类,那么Redis就没有被用到,也就不需要对Redis进行自动配置了。于是,通过对配置类使用@Condition系列注解,就使得组件被按需注入。

2.2 自动配置实现

我们在使用自动配置的时候,可以发现自动配置其实是由两个方面组成的。一是SpringBoot提供了一些默认的配置,二是我们可以通过编写application.yml来自定义配置。

Configuration类

SpringBoot为我们编写好了许多的配置类,这些配置类已经实现了常见的配置,帮我们注入了常用的Bean,并配置好了一些默认的属性。同样以Redis为例,可以看到Redis的配置类如下:SpringBoot为我们创建了SpringRedisTemplate和RedosTemplate类,我们就无需自己注入这两个类了。

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

从上面的代码我们也可以看到SpringBoot的一个设计准则,如果我们没有配置,他就帮我们进行默认配置。如果我们有自定义的配置,就遵循我们的配置。

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)

可以看到,如果我们自定义了一个redisTemplate的bean,SpringBoot就不会再注入这个Bean了。

Properties类

接下来解决我们的自定义问题,我们可以在application.yml中指定redis客户端的链接地址,数据库密码等各种属性。SpringBoot通过@ConfigurationProperties,从配置文件中读取配置信息。

@ConfigurationProperties(
    prefix = "spring.redis"
)
public class RedisProperties {
    private int database = 0;
    private String url;
    private String host = "localhost";
    private String username;
    private String password;
    private int port = 6379;
    private boolean ssl;
    private Duration timeout;
    private Duration connectTimeout;
    private String clientName;
    private ClientType clientType;
    private Sentinel sentinel;
    private Cluster cluster;
    .....
}

从上面代码中可以看到,配置的前缀是spring.redis,在application.yml中修改spring.redis后的属性即可修改这个类,然后对redis进行自定义了。

综上所述,自动配置主要由一个或多个配置类和Properties类组成。一个提供Bean的注入和属性的设置,另一个从配置文件中读属性值。SpringBoot实现了第三方框架和其他Spring组件的自动配置类,使得他们可以被轻松的集成和自定义。

你可能感兴趣的:(SpringBoot,spring,boot,spring,java)