目录
前言
springboot提供了自动装配的功能,简化了配置。自动装配应该包含配置类的导入和属性的赋值两个过程,本文基于源码分析这两个过程。
一、自动装配类的加载过程
首先从SpringBootApplocation注解进行分析,包含了EnableAutoConfiguration注解,在该注解中引入了AutoConfigurationImportSelector类,该类实现了DeferredImportSelector接口,主要逻辑就位于这个类中。
@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 {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
首先在spring加载bean定义的过程中,会调用到deferedImport的group的process方法,selectImports方法,下面依次进行分析。
this.deferredImportSelectorHandler.process();
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
在process方法中,比较重要的方法是getAutoConfigurationMetadata和getAutoConfigurationEntry方法,分别实现了配置元数据和配置类的加载。
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
//加载自动配置的类
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
getAutoConfigurationMetadata方法,主要就是从META-INF/spring-autoconfigure-metadata.properties文件中读取配置元数据。
//获取META-INF/spring-autoconfigure-metadata.properties中的数据
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
if (this.autoConfigurationMetadata == null) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
}
return this.autoConfigurationMetadata;
}
getAutoConfigurationEntry方法首先从spring.factories中读取需要装配的全类名,然后使用元数据进行过滤,将过滤后的类构建AutoConfigurationEntry。
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//读取所有的spring.factories中的全类名
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//使用spring-autoconfigure-metadata.properties文件中的数据过滤
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
在selectImports方法中主要是将过滤之后的类返回,并加载到容器中。
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
//返回文件中加载的类
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
二、自动加载属性
本文以RabbitAutoConfiguration为例,在rabbitConnectionFactory的创建过程中依赖RabbitProperties,同时,RabbitProperties类中的属性也是配置文件中需要配置的属性,可以自动装配到rabbitConnectionFactory中,下面主要分析配置的属性是如何与RabbitProperties关联的。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ConnectionFactory.class)
protected static class RabbitConnectionFactoryCreator {
@Bean
public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties properties,
ObjectProvider<ConnectionNameStrategy> connectionNameStrategy) throws Exception {
PropertyMapper map = PropertyMapper.get();
CachingConnectionFactory factory = new CachingConnectionFactory(
getRabbitConnectionFactoryBean(properties).getObject());
map.from(properties::determineAddresses).to(factory::setAddresses);
map.from(properties::isPublisherReturns).to(factory::setPublisherReturns);
map.from(properties::getPublisherConfirmType).whenNonNull().to(factory::setPublisherConfirmType);
RabbitProperties.Cache.Channel channel = properties.getCache().getChannel();
map.from(channel::getSize).whenNonNull().to(factory::setChannelCacheSize);
map.from(channel::getCheckoutTimeout).whenNonNull().as(Duration::toMillis)
.to(factory::setChannelCheckoutTimeout);
RabbitProperties.Cache.Connection connection = properties.getCache().getConnection();
map.from(connection::getMode).whenNonNull().to(factory::setCacheMode);
map.from(connection::getSize).whenNonNull().to(factory::setConnectionCacheSize);
map.from(connectionNameStrategy::getIfUnique).whenNonNull().to(factory::setConnectionNameStrategy);
return factory;
}
}
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {
/**
* RabbitMQ host.
*/
private String host = "localhost";
/**
* RabbitMQ port.
*/
private int port = 5672;
/**
* Login user to authenticate to the broker.
*/
private String username = "guest";
}
RabbitAutoConfiguration上有一个注解EnableConfigurationProperties,在该注解中引入了EnableConfigurationPropertiesRegistrar类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
/**
* The bean name of the configuration properties validator.
* @since 2.2.0
*/
String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
/**
* Convenient way to quickly register
* {@link ConfigurationProperties @ConfigurationProperties} annotated beans with
* Spring. Standard Spring Beans will also be scanned regardless of this value.
* @return {@code @ConfigurationProperties} annotated beans to register
*/
Class<?>[] value() default {};
}
EnableConfigurationPropertiesRegistrar将ConfigurationPropertiesBindingPostProcessor和@EnableConfigurationProperties注解中的类加载到容器中。
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//注册ConfigurationPropertiesBindingPostProcessor到容器中
registerInfrastructureBeans(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
//获取注解@EnableConfigurationProperties指定的被@ConfigurationProperties注解标注的bean实例对象
//forEach循环调用注册器方法将@ConfigurationProperties标注的bean注册到IOC容器之中
getTypes(metadata).forEach(beanRegistrar::register);
}
}
ConfigurationPropertiesBindingPostProcessor实现了BeanPostProcessor接口,在postProcessBeforeInitialization方法中,会给含有ConfigurationProperties注解的类中的属性赋值,在环境中查找对应属性,赋值时是在bean创建的过程中,此时相关属性已经加载到容器中。将环境中的属性加载到RabbitProperties对象中,可以在创建CachingConnectionFactory时将属性装配。
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//绑定有ConfigurationProperties注解的类的属性,给属性赋值
bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
return bean;
}
在一个bean中有大量的属性需要注入可以使用configurationProperties,注入个别属性还是使用value注解.
总结
本文对springboot的自动装配原理进行了分析,EnableAutoConfiguration注解引入了AutoConfigurationImportSelector类,在该类中,会加载spring.factory文件中的类,并使用pring-autoconfigure-metadata.properties文件中的元数据进行过滤。EnableConfigurationProperties注解引入了ConfigurationPropertiesRegistrar,在其中注入了EnableConfigurationProperties注解中的类和ConfigurationPropertiesBindingPostProcessor,该类会在postProcessBeforeInitialization方法中对带有ConfigurationProperties注解的类进行属性赋值,在自动装配类中将相关属性配置进去。