【Spring Boot】自动配置原理

【Spring Boot】自动配置原理


Spring Boot的出现,得益于“习惯优于配置”的理念,没有繁琐的配置、难以集成的内容(大多数流行第三方技术都被集成),这是基于Spring 4.x提供的 按条件配置Bean的能力。

Spring Boot的配置文件

Spring Boot有一个全局配置文件:application.properties或application.yml。各种属性都可以在这个文件中进行配置,最常配置的比如:server.port、logging.level.* 等等。然而实际用到的往往只是很少的一部分,那么这些属性是否有据可依呢?答案当然是肯定的,这些属性都可以在官方文档中查找到:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-properties

img

这些配置是如何在Spring Boot项目中生效的呢?

工作原理剖析

Spring Boot关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar中:

image-20220327201554375

@EnableAutoConfiguration

image-20220327201740851

Spring Boot的启动类上有一个@SpringBootApplication注解,@SpringBootApplication是一个复合注解或派生注解,在@SpringBootApplication中有一个注解@EnableAutoConfiguration,就是开启自动配置,其定义如下:

image-20220327201849416

而这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。

image-20220327202111626

spring.factories文件是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔。

这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。

@Import注解使用方式

使用须知:

  • @Import只能用在类上,@Import通过快速导入的方式实现把实例加入spring的IOC容器中
  • @Import注解可以用于导入第三方包
  • @Import注解有三种用法
直接填class数组

直接填对应的class数组,class数组可以有0到多个。

@Import({ 类名.class , 类名.class... })
public class TestDemo {

}

对应的import的bean都将加入到spring容器中,这些在容器中bean名称是该类的全类名

ImportSelector方式

这种方式的前提就是一个类要实现ImportSelector接口,具体如下:创建Myclass类并实现ImportSelector接口

public class Myclass implements ImportSelector {
   //既然是接口肯定要实现这个接口的方法
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[0];
    }
}

返回值:就是实际上要导入到容器中的组件全类名,需要注意的是selectImports方法可以返回空数组但是不能返回null,否则会报空指针异常
参数:AnnotationMetadata表示当前被@Import注解给标注的所有注解信息

具体demo:

创建Myclass类并实现ImportSelector接口,用于演示添加一个全类名给其返回值:

public class Myclass implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.yzc.Test.TestDemo3"};
    }
}

编写TestDemo类,并标注上使用ImportSelector方式的Myclass类

@Import({TestDemo2.class,Myclass.class})
public class TestDemo {
        @Bean
        public AccountDao2 accountDao2(){
            return new AccountDao2();
        }
}

编写打印容器中的组件测试类

/**
 * 打印容器中的组件测试
 */
public class AnnotationTestDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestDemo.class);  
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){
            System.out.println(name);
        }
    }
}

输出

在这里插入图片描述

ImportBeanDefinitionRegistrar方式

也是一个接口,类似于第二种ImportSelector用法,只不过这种用法比较自定义化注册,具体如下:

创建Myclass2类并实现ImportBeanDefinitionRegistrar接口

public class Myclass2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //指定bean定义信息(包括bean的类型、作用域...)
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDemo4.class);
        //注册一个bean指定bean名字(id)
        beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition);
    }
}

编写TestDemo 类,并标注上使用ImportBeanDefinitionRegistrar方式的Myclass2类

@Import({TestDemo2.class,Myclass.class,Myclass2.class})
public class TestDemo {
        @Bean
        public AccountDao2 accountDao222(){
            return new AccountDao2();
        }

}

输出

在这里插入图片描述

以上三种用法方式皆可混合在一个@Import中使用,注意第一种和第二种都是以全类名的方式注册,而第三中可自定义名称注册。其中的第二种用法ImportSelector方式在springboot中使用的特别多

自动配置生效

每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:

@ConditionalOnBean:当容器里有指定的bean的条件下。

@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。

@ConditionalOnClass:当类路径下有指定类的条件下。

@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。

@ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。

以ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何生效。

image-20220327204232733

在ServletWebServerFactoryAutoConfiguration类上,有一个@EnableConfigurationProperties注解:开启配置属性,而它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。

image-20220327204328160

在这个类上,看到了一个非常熟悉的注解:@ConfigurationProperties,它的作用就是从配置文件中绑定属性到对应的bean上,而**@EnableConfigurationProperties负责导入这个已经绑定了属性的bean到spring容器中。那么所有和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties**类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。

至此,大致可以了解。在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上,封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。诸多的XxxxAutoConfiguration自动配置类,作用就是为Spring容器导入bean,而所有导入的bean所需要的属性都通过xxxxProperties的bean来获得。

面试的时候,其实远远不需要回答的这么具体,只需要这样回答:

Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中,取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

总结

XxxxProperties类的含义是:封装配置文件中相关属性。

XxxxAutoConfiguration类的含义是:自动配置类,目的是给容器中添加组件。

参考

Spring Boot面试杀手锏————自动配置原理

spring注解之@Import注解的三种使用方式

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值