SpringBoot自动装配原理解析

SpringBoot的一大优势就是省去了很多的配置,当SpringBoot启动的时候,在内部进行了自动装配的工作,使得开发人员无需配置或者只需要很少的配置就能直接开发业务。


SpringBoot的自动装配

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 {
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
}

这里面有三个重要的注解:

@Configuration(SpringBootConfiguration注解实质就是一个@Configuration)
@EnableAutoConfiguration
@ComponentScan

也就是说,在开发的时候,加上这三个注解,就等同于SpringBootApplication注解。

@Configuration
该注解表明这是一个配置类,相当于一个beans.xml文件。
@ComponentScan
该注解的功能是自动扫描并加载符合条件的组件或bean定义,并将其加入到Spring容器中。
@EnableAutoConfiguration
Spring中所有的@Enablexxx都是开启某一项功能的注解,比如@EnableScheduling表示开启spring定时任务。原理是借助@Import的帮助,将所有符合配置条件的bean加载到spring容器中。@EnableAutoConfiguration表示开启自动装配。

@EnableAutoConfiguration

该注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    //按类型排序不需要自动装配的类
    Class<?>[] exclude() default {};
    //按名称排除不需要自动装配的类
    String[] excludeName() default {};
}

这里最关键的是@Import(AutoConfigurationImportSelector.class)。通过自动装配导入选择器AutoConfigurationImportSelector,@EnableAutoConfiguration可以将所有符合条件的@Configuration配置都加载到当前的spring容器中。

自动装配原理

AutoConfigurationImportSelector类定义如下:

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {

	private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();

	private static final String[] NO_IMPORTS = {};

	private static final Log logger = LogFactory
			.getLog(AutoConfigurationImportSelector.class);

	private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

	private ConfigurableListableBeanFactory beanFactory;

	private Environment environment;

	private ClassLoader beanClassLoader;

	private ResourceLoader resourceLoader;

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

可以看到,该类实现了很多的xxxAware接口和DeferredImportSelector接口,所有的Aware都优先于selectImports方法执行。

selectImports执行过程如下:

(1)首先通过loadMetadata方法加载“META-INF/spring-autoconfigure-metadata.properties"文件。

(2)再通过getAutoConfigurationEntry方法获取自动装配入口,下面是源码:

protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

在getAutoConfigurationEntry方法中,先通过getAttributes方法获取注解的属性和值。
然后在getCandidateConfigurations方法中,通过工具类SpringFactoriesLoader的loadFactoryNames方法在所有的“META-INF/spring.factories”文件中查找org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,并将其封装到一个List中返回。

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories中加载配置。将@EnableAutoConfiguration的完整类名作为查找的key,获取对应的一组@Configuration类。

总结

@EnableAutoConfiguration作用就是从classpath中加载有的的META-INF/spring.factories配置文件,然后以EnableAutoConfiguration的完整类名作为key来查找对应的配置项,再通过反射将其实例化为对应的标注了@Configuration的JavaConfig形式的IOC容器配置类,最后汇总为一个并加载IOC容器。

redis自动装配

下面以redis为例来说明自动装配过程。

查找spring.factories中的redis配置项

在这里插入图片描述
从spring-boot-autoconfigure-2.1.5.RELEASE.jar的spring.factories文件中找到redis相关配置类RedisAutoConfiguration。

一般来说,一个功能配置类会负责管理多个相关的功能类,如RedisAutoConfiguration就负责JedisConnectionFactory、RedisTemplate、StringRedisTemplate这三个功能类的创建。

在这里插入图片描述
从上图可以看到,RedisAutoConfiguration配置类生效有一个条件:@CondictionOnClass({RedisOperations.class}),也就是说在classpath路径下要有RedisOperations类存在。如果在pom.xml中引入了对应的jar包,就可以匹配到这个类。

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
</dependency>

匹配成功后配置类生效,在该配置类中会自动注入默认的属性配置类@EnableConfigurationProperties(RedisProperties.class)
在这里插入图片描述
这个属性配置类会从配置文件中读取spring.redis开头的配置项。

最终,ReidsAutoConfiguration配置类会生成JedisConnectionFactory、RedisTemplate,并将加载到IOC容器中。
在这里插入图片描述

参考资料

[1]. https://www.jianshu.com/p/88eafeb3f351

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值