【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
这些配置是如何在Spring Boot项目中生效的呢?
工作原理剖析
Spring Boot关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar中:
@EnableAutoConfiguration
Spring Boot的启动类上有一个@SpringBootApplication注解,@SpringBootApplication是一个复合注解或派生注解,在@SpringBootApplication中有一个注解@EnableAutoConfiguration,就是开启自动配置,其定义如下:
而这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
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配置类为例,解释一下全局配置文件中的属性如何生效。
在ServletWebServerFactoryAutoConfiguration类上,有一个@EnableConfigurationProperties注解:开启配置属性,而它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。
在这个类上,看到了一个非常熟悉的注解:@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类的含义是:自动配置类,目的是给容器中添加组件。
参考