【SpringBoot】SpringBoot自动配置原理

SpingBoot 自动配置原理解析

SpringBoot框架可以对Spring以及SpringMVC进行自动配置,十分方便。最近研究了一下自动配置的原理,记录一下

1、SpringBoot启动类

SpringBoot有一个主配置类,即AutoconfigApplication类。SpringBoot在启动时会加载主配置类,开启了一些自动配置功能。

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

我们跟踪进入@SpringBootApplication注解,其中会有一个@EnableAutoConfiguration注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
// 引入了一个AutoConfigurationImportSelector类
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}

@Import注解引入了一个AutoConfigurationImportSelector类,我们进入此类中,发现有如下方法:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

我们继续跟进this.getAutoConfigurationEntry()方法:

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            // 获取候选得配置
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

接下来,进入到this.getCandidateConfigurations()方法中找到如下代码:

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

我们摘出如下关键代码:

SpringFactoriesLoader.loadFactoryNames()

作用: 扫描所有jar包类路径下 META-INF/spring.factories,把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
截取部分spring.factories内容如下:

# 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,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\

到此为止便找到了我们想要得答案:
@EnableAutoConfiguration 注解会导入一个自动配置类AutoConfigurationImportSelector,而这个类又通过selectImports()方法扫描各个jar包路径下的 META-INF/spring.factories 并封装成相对应的properties对象,从中读取到EnableAutoConfiguration.class类所对应的值,即类的全限定类名,然后把他们添加进容器中,至此就完成了自动配置

自动配置案例

如果说上面的源码读的有些晦涩,可以通过下面的小案例来了解一下。

1、application.properties配置文件

在SpringBoot的配置文件中配置一下信息

student.name=张三
student.age=18
2、编写properties对应的JavaBean
@ConfigurationProperties(prefix = "student") //读取配置文件中前缀为student的配置与类中的属性相对应
public class StudentProperties {
    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "StudentConfig{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
3、编写自动配置类
@Configuration //声明这是一个配置类
@EnableConfigurationProperties(StudentProperties.class) // 读入配置对象
@ConditionalOnClass(StudentService.class) // 只有类路径存在时才会加载配置类
public class StudentAutoConfiguration {
    @Autowired
    private StudentProperties studentConfig;

    @Bean
    @ConditionalOnMissingBean(StudentService.class)
    public StudentService returnStudentService(){
        StudentService studentService = new StudentService();
        studentService.setName(studentConfig.getName());
        return  studentService;
    }
}

其中的@ConditionalOnClass注解的作用就相当与一个 if 的条件判断,作用就是当类路径存在时才会加载该配置类,如果不存在就跳过。

@ConditionalOnMissingBean(StudentService.class) 这个注解也是 SpringBoot 自动配置的核心所在,它的作用是: 判断如果 StudentService 不存在,就加载配置类,如果存在就跳过 ,这个注解就解释了为什么SpringBoot会优先使用用户自定义的配置类,如果用户没有自定义配置类,才会加载自动配置类。

4、使用自动配置类编写测试代码
@Controller
public class TestController {
    @Autowired
    StudentService studentService;

    @RequestMapping("/getStudentName")
    @ResponseBody
    public String getName(){
        return studentService.getName();
    }
}

此时的运行结果如下图所示:
在这里插入图片描述
用于模拟SpringBoot自动配置了StudentService

5、自定义的用户配置类
@Component
public class StudentService {
    private String name = "autoConfig";
    public String getName(){
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

定义自己的配置类加入容器中,并且给Name设置初值来区分,运行结果如下:
在这里插入图片描述
两个不同的结果就印证了SpringBoot会优先加载用户自定义的配置类,如果用户没有自定义配置类,才会加载自动配置类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值