SpringBoot的自动装配原理

在使用 springboot的时候,我们体会到了它的舒服之处,容易上手,开箱即用。撇去了繁杂的xml配置,使用注解和 yml的方式统一配置和管理。使我们的开发变得非常的优雅。

用了这么久的springboot  天天在那看@SpringBootApplication 那么 springboot的 自动装配原理是什么呢?

 

接下来认认真真来看一次 springboot 装配原理。

自动配置原理

@SpringBootApplication
public class DemoTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoTestApplication.class, args);
    }

}

点开 @SpringBootApplication 可以看到

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

删去一些不重要的注解,其中最为重要的两个注解为

@SpringBootConfiguration 和 @EnableAutoConfiguration

 

@SpringBootConfiguration

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

经由分析,可以简单看到 @SpringBootConfiguration 里边值得我们注意的一个 注解是 @Configuration,这表明这是一个 spring的配置类,我们将它交给 IOC容器来管理。

@EnableAutoConfiguration

见名知意,开启自动自动配置。看来这就是我们要找的主要类了,那我们来看看它到底做了什么

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包
@Import({AutoConfigurationImportSelector.class}) //自动配置导入选择器
public @interface EnableAutoConfiguration {

我们可以看到,这个类最最重要的一条配置就是 @Import 导入了一个 自动配置导入选择器,那么这个类到底干嘛的呢?

AutoConfigurationImportSelector.class

    // 我们来看看它的 selectImports 放
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry 
                //这个方法就是我们要着重分析的方法了,getAutoConfigurationEntry 获取自动配置条目
                = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            /*
              所有的配置都存放在configurations中,
              而这些配置都从getCandidateConfiguration中获取,
              这个方法是用来获取候选的配置。
            */
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            ...
        }
    }

点进 getCandidateConfigurations(annotationMetadata, attributes)方法,我们知道了这个方法可以用来获取所有候选的配置,那么这些候选的配置又是从哪来的呢?

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }

可以看到源码中有一个 loadFactoryNames 中传入了一个 getSpringFactoriesLoaderFactoryClass,通过这个方法我看到了熟悉的 EnableAutoConfiguratioin 注解,标注了这个类的包不就是 @SpringBootApplication 吗?

所以我们可以得出结论:它兜兜转转绕了这么多地方,就是为了将启动类所需的所有资源导入。

然后看到 springboot 给我们的提示  Assert.notEmpty("No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");

这个断言的意思是,configurations必须非空,否则就打印一段话,No auto configuration classes found in META-INF/spring.factories,我们把这个逻辑反过来想想。如果这个集合不为空,是不是就代表找到了这个spring.factories并且会去加载这个文件中的内容呢?

我们点进去这个 文件会发现里边包含了很多自动配置属性

大致浏览一下spring.factories 会发现,大部分的文件名都是 xxxAutoConfiguration结尾

 

那么 loadFactoryNames 这个方法干嘛的?经过几次跳转我们发现代码中最重要的一行代码

Properties properties = PropertiesLoaderUtils.loadProperties(resource);

它将我们传入的文件内容包封装成了 Properties 对象

也就是 这个 EnableAutoConfiguration值,然后把他们添加到容器中。

(图截不下...一直到最后)

然后每一个自动配置类会进行自动配置功能

 

以 HttpAutoConfiguration 为例

我们点进去HttpEncodingAutoConfiguration 看看,然后分析一下他是怎么从 yml里获取我们的配置的。

@Configuration// 表明当前是一个配置类
@EnableConfigurationProperties({ServerProperties.class})// 启用指定类的@ConfigurationProperties功能
//这里是启动 ServerProperties 的@ConfigurationProperties
@ConditionalOnWebApplication // 如果当前是 Web 项目则生效
@ConditionalOnClass({CharacterEncodingFilter.class}) // 项目组存在 CharacterEncodingFilter 这个类则生效
@ConditionalOnProperty(
    prefix = "server.servlet.encoding",
    value = {"enabled"},
    matchIfMissing = true
)// 默认这个字段 server.servlet.encoding = true
public class HttpEncodingAutoConfiguration {
    
    private final Encoding properties;

    //当类中 只有一个 有参构造器的时候,这个时候的参数 会从 IOC容器里去找
    //所以这里,我们就将与 yml 对应的 Properties 文件与 AutoConfigutation这个类 绑定了
    public HttpEncodingAutoConfiguration(ServerProperties properties) {
        this.properties = properties.getServlet().getEncoding();
    }

    @Bean
    @ConditionalOnMissingBean//如果当前类不存在,就执行这个方法。
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        //从 properties/yml获取值 
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
        return filter;
    }
}

关于 ServerProperties

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
) //看到这里我们非常的熟悉,我们不也常用这个 @ConfigurationProperties 来写自己的配置类吗
public class ServerProperties {

小总结:

springboot 启动会加载大量的配置类,如果我们需要的功能有默认写好的配置类,那么我们直接用就行了,还可以看看这些自动配置类装载了哪些组件啊,对应的 Properties 文件里有哪些可以配置的属性啊。

如果没有的话,那我们可就要自己来配置了,相信看完本文后大家也都知道,对应的基本套路了。

XxxxAutoConfig

XxxxProperties

然后在自己项目类路径下 也就是  src/resources/META-INF/spring.factories(自己创建的) 里注册配置一下就行了

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
xxx.xxx.xxx包.XxxxAutoConfig,

转载:https://www.yuque.com/zzuli-tree/my6a8r/lkk3a0

 

 

 

原文地址:https://www.cnblogs.com/shadoll/p/14422633.html