我们在使用SpringBoot开发相关项目时,经常会使用到@ConfigurationProperties注解,这个注解配合application.properties(或yml文件)使用,可以快速的为我们的Bean的成员变量进行赋值,常规用法如下:
/ 创建bean @ConfigurationProperties(prefix="person") @Data public class Person { private int id; private String name; private String addr; } // 在application.properties文件中添加 person.name=jack person.id=11 person.addr=china
这样就可以为Person实例自动注入属性
那么SpringBoot是如何为bean实例注入属性值的呢?
1.关于自动注入属性值的功能猜想
SpringBoot会为每一个在配置文件中有匹配项的bean注入属性值,根据之前的 BeanPostProcessor功能可知,我们的Spring容器中应该有一个Bean实现BeanPostProcessor接口,然后在返回每一个bean之前,获取配置文件内容,找到对应属性,然后进行赋值操作
SpringBoot中确实有这么一个Bean,ConfigurationPropertiesBindingPostProcessor,实现了相关赋值功能,那么这个Bean是如何被注入到容器中的呢,下面亲随笔者一层层分析查找
2.SpringBoot项目启动之@SpringBootApplication
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
重要的注解就是@EnableAutoConfiguration,下面看下这个注解
@SuppressWarnings("deprecation") @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
主要功能就是把EnableAutoConfigurationImportSelector注入到容器中,下面看下这个类
public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector { @Override protected boolean isEnabled(AnnotationMetadata metadata) { if (getClass().equals(EnableAutoConfigurationImportSelector.class)) { return getEnvironment().getProperty( EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true); } return true; } } // AutoConfigurationImportSelector public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}
AutoConfigurationImportSelector这个类实现了DeferredImportSelector接口,可知这个类应该在其selectImport方法中,应该获取了一个bean的列表,然后被这个bean列表的所有bean会被注入到容器中,下面重点分析下这个selectImport方法
对于@Import ImportSelector等接口注入bean到容器的方式不太明白的同学可以参考下这篇文章 https://blog.csdn.net/qq_26323323/article/details/81201717
3.AutoConfigurationImportSelector.selectImport(AnnotationMetadata annotationMetadata)
该方法源码如下:
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } try { // 1.获取META-INF/spring-autoconfigure-metadata.properties 路径下所有bean URL AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); // 2.重要操作在这里 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); configurations = sort(configurations, autoConfigurationMetadata); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); // 3.经过中间的一系列操作返回configurations return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } } // getCandidateConfigurations(annotationMetadata,attributes) protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 主要就是为了获取META-INF/spring.factories 文件下EnableAutoConfiguration对应的value值,并返回该值 List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), 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; }
通过以上分析可知:AutoConfigurationImportSelector类的主要功能就是为了加载META-INF/spring.factories文件下EnableAutoConfiguration对应的value值,这些value值对应一个个bean,Spring会将这些bean加载到容器中
我们看下是哪些bean(在spring-boot-autoconfigure-xxx.jar的META-INF/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.CloudAutoConfiguration, org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration, org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration, org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration, org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration, org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration, ... org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration, org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration, org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration, ... org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration, org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration, org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration, org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration, org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration, org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration, ... org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
以上展示了部分类,可知,这些类都会被注入到Spring中,下面我们重点关注ConfigurationPropertiesAutoConfiguration类
4.ConfigurationPropertiesAutoConfiguration
1)ConfigurationPropertiesAutoConfiguration源码如下:
@Configuration @EnableConfigurationProperties public class ConfigurationPropertiesAutoConfiguration { } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({EnableConfigurationPropertiesImportSelector.class}) public @interface EnableConfigurationProperties { Class<?>[] value() default {}; }
该类没有其他方法,由其注解@EnableConfigurationProperties可知,整个类主要是为了注入EnableConfigurationPropertiesImportSelector这个类,
2)EnableConfigurationPropertiesImportSelector源码如下:
class EnableConfigurationPropertiesImportSelector implements ImportSelector { public String[] selectImports(AnnotationMetadata metadata) { MultiValueMap attributes = metadata.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false); Object[] type = attributes == null ? null : (Object[]) ((Object[]) attributes.getFirst("value")); return type != null && type.length != 0 ? new String[]{ConfigurationPropertiesBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()} : new String[]{ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()}; } }
可知其会返回ConfigurationPropertiesBindingPostProcessorRegistrar类到容器中
3)ConfigurationPropertiesBindingPostProcessorRegistrar源码如下
public class ConfigurationPropertiesBindingPostProcessorRegistrar implements ImportBeanDefinitionRegistrar { public static final String BINDER_BEAN_NAME = ConfigurationPropertiesBindingPostProcessor.class.getName(); private static final String METADATA_BEAN_NAME; // 主要的注入方法 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(BINDER_BEAN_NAME)) { BeanDefinitionBuilder meta = BeanDefinitionBuilder .genericBeanDefinition(ConfigurationBeanFactoryMetaData.class); BeanDefinitionBuilder bean = BeanDefinitionBuilder .genericBeanDefinition(ConfigurationPropertiesBindingPostProcessor.class); bean.addPropertyReference("beanMetaDataStore", METADATA_BEAN_NAME); // 真正的注入功能就是这句话,将ConfigurationPropertiesBindingPostProcessor类注册进来 registry.registerBeanDefinition(BINDER_BEAN_NAME, bean.getBeanDefinition()); registry.registerBeanDefinition(METADATA_BEAN_NAME, meta.getBeanDefinition()); } } static { METADATA_BEAN_NAME = BINDER_BEAN_NAME + ".store"; } }
总结:通过以上的一层层分析,我们终于到了ConfigurationPropertiesBindingPostProcessor类,也成功将该类注入到容器中,下面我们就分析下ConfigurationPropertiesBindingPostProcessor是如何将参数注入到bean中
具体分析过程可参考:https://blog.csdn.net/qq_26000415/article/details/78942494 ,笔者不再分析