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 {

我们发现主要由3个注解组成 @SpringBootConfiguration@ComponentScan@EnableAutoConfiguration

1.@SpringBootConfiguration

源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

发现其实就是一个@Configuration注解,作用就是将配置了这个注解的类加载到ioc容器。主要是为了替换掉xml配置文件。

2.@ComponentScan

这个注解是大家接触得最多的了,相当于xml配置文件中的<context:component-scan>。它的主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到spring的Ioc容器中。

标识需要装配的类的形式主要是:@Component、@Repository、@Service、@Controller这类的注解标识的类。ComponentScan默认会扫描当前package下的的所有加了相关注解标识的类到IoC容器中

3.@EnableAutoConfiguration

仍然是在spring3.1版本中,提供了一系列的@Enable开头的注解,Enable主机应该是在JavaConfig框架上更进一步的完善,是的用户在使用spring相关的框架是,避免配置大量的代码从而降低使用的难度比如常见的一些Enable注解:@EnableWebMvc,(这个注解引入了MVC框架在Spring 应用中需要用到的所有bean);比如@EnableScheduling,开启计划任务的支持
在这里插入图片描述

找到EnableAutoConfiguration,我们可以看到每一个涉及到Enable开头的注解,都会带有一个@Import的注解。

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@Import

这个注解相当于xml 格式下的<import resource/>
import就是把多个配置合并在一个配置中。在JavaConfig中所表达的意义是一样的。

我们用Import注解将bean进行注入
1基于普通bean或者带有@Configuration的bean进行注入
比如这个时候@Import(AutoConfigurationImportSelector.class) AutoConfigurationImportSelector 就会被ico管理。
2.实现ImportSelector接口进行动态注入

public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

}

返回值是一个字符串的数组,这个数组会将里面的字符串加载到ioc容器当中,这个字符串必须就是类的的全限定名

3.实现ImportBeanDefinitionRegistrar接口进行动态注入

这个上面的类似,感兴趣的可以去了解下。

我们回来过来继续看AutoConfigurationImportSelector 中有个selectImports 方法。
我们不是将那些需要自动装备的类的名称找到并返回不就好了吗?

public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		try {
			AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
					.loadMetadata(this.beanClassLoader);
			AnnotationAttributes attributes = getAttributes(annotationMetadata);
			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);
			return StringUtils.toStringArray(configurations);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

这个方法返回了configurations,这个configurations 我们猜想就是那些需要自动装备的类,我们跟下configurations 是怎么来的

List<String> configurations = getCandidateConfigurations(annotationMetadata,
					attributes);

点进去

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		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;
	}

我们发现是由一个SpringFactoriesLoader的类加载到的,继续点进去,最终发下了一行这样带代码

 try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

它会去"META-INF/spring.factories" 查找,我们去找找看,我们发现在 autoconfigere 包下确实有这个文件。

在这里插入图片描述

我们发现了个大秘密,这不就是那些自动装配的配置类吗?
在这里插入图片描述
为什么我们导入这个redis 的包就可以直接使用 RedisTemplate操作redis(还是要在配置文件中配置端口的。)?

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

我们发现在spring.factories 的文件中有一个类
在这里插入图片描述
这个类会被spring 管理,我们点进去

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean(StringRedisTemplate.class)
	public StringRedisTemplate stringRedisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
}

为什么可以使用RedisTemplate?因为这个类帮我们创建了RedisTemplate

tip:
@ConditionalOnClass(RedisOperations.class)
这个注解的意思是RedisOperations.class这个类存在时,会加载这个类。而RedisOperations是spring-boot-starter-data-redis包中的一个类,所以需要导包才能使用。

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值