SpringBoot自动装配源码解析

7 篇文章 0 订阅

本文的代码demo全部在springboot-configuration-test.zip中,免积分下载,下载后直接运行。

1. SpringBoot自动装配示例

参考附件代码中com.lan.version0包

在resource下创建META-INF目录,然后在该目录下创建spring.factories文件,然后添加以下内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lan.version0.instance.InstanceA,\
com.lan.version0.instance.InstanceB

其中org.springframework.boot.autoconfigure.EnableAutoConfiguration是Spring的一个注解类,com.lan.version0.instance.InstanceA和com.lan.version0.instance.InstanceB是自定义的类,这两个类都没有加@Component注解。

InstanceA.java:

package com.lan.version0.instance;

public class InstanceA {

	public InstanceA() {
		System.out.println("version0.InstanceA 实例化");
	}
	
}

InstanceB.java:

package com.lan.version0.instance;

public class InstanceB {

	public InstanceB() {
		System.out.println("version0.InstanceB 实例化");
	}
	
}

上面的spring.factories配置中,SpringBoot启动后,会自动把com.lan.version0.instance.InstanceA和com.lan.version0.instance.InstanceB实例化成spring中的Bean。

这就是自动装配,自动把spring.factories中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的类实例化成Spring中的bean。为什么是org.springframework.boot.autoconfigure.EnableAutoConfiguration呢?这就需要了解自动装配的原理了。

2. 为什么需要自动装配

默认情况下,SpringBoot启动时,只会扫描到启动类下的包及其子包中的组件。像Apollo client、mybatis等第三方插件,其包名并不会和应用启动类下的包名一样,所以没法扫描到第三方插件包中的组件。这个时候,就需要一个装配机制,应用只需要引入第三方插件包,就能够自动扫描到第三方插件的组件并加载到spring中。以下通过渐进的方式讲解SpringBoot的自动装配机制。

3. BeanDefinition

Spring中的每个bean都会被封装成BeanDefinition对象,然后存放在一个BeanDefinitionMap中。BeanDefinition对象包含了对应bean的class对象、bean的名称、是否懒加载、scope、dependsOn等等属性。

4. @Import注解的作用

@Import注解在自动装配中起着关键作用,先了解一下该注解的几个作用。

4.1 注入bean

参考附件代码中com.lan.version1包

AppConfig.java

package com.lan.version1.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.lan.version1.instance.InstanceA;
import com.lan.version1.instance.InstanceB;

/**
 * InstanceA和InstanceB没有加@Component,通过Import注解将其实例化成spring容器中的一个bean
 * @author lan
 *
 */
@Configuration
@Import({InstanceA.class, InstanceB.class})
public class AppConfig {

}

4.2 导入bean注册器ImportBeanDefinitionRegistrar

参考附件代码中的com.lan.version2包

AppConfig.java:

package com.lan.version2.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({AppImportBeanDefinitionRegistrar.class})
public class AppConfig {

}

AppImportBeanDefinitionRegistrar.java

package com.lan.version2.config;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import com.lan.version2.instance.InstanceA;
import com.lan.version2.instance.InstanceB;

public class AppImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // RootBeanDefinition是BeanDefinition的实现类
		RootBeanDefinition beanDefinitionA = new RootBeanDefinition(InstanceA.class);
		registry.registerBeanDefinition("instanceA", beanDefinitionA);
		RootBeanDefinition beanDefinitionB = new RootBeanDefinition(InstanceB.class);
		registry.registerBeanDefinition("instanceB", beanDefinitionB);
	}
	
}

4.3 导入选择器ImportSelector

参考附件中的com.lan.version3包

AppConfig.java

package com.lan.version3.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({AppImportSelector.class})
public class AppConfig {

}

AppImportSelector.java

package com.lan.version3.config;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class AppImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		// 指定需要实例化的类
		return new String[]{"com.lan.version3.instance.InstanceA", "com.lan.version3.instance.InstanceB"};
	}

}

5. 自定义注解+@Import实现装配

参考附件中的com.lan.version4包

改进一下上面的例子:

将com.lan.version3.config.AppImportSelector的selectImports方法返回的类集合,改成从spring.factories中读取:

package com.lan.version4.config;

import java.util.List;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;

public class AppImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		// 从当前classpath查找所有的spring.factories文件,并查找key是com.lan.version4.config.EnableInstance的value列表
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableInstance.class, AppImportSelector.class.getClassLoader());
		return StringUtils.toStringArray(configurations);
	}

}

自定义@EnableInstance,通过@Import注解导入ImportSelector:

package com.lan.version4.config;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.Import;

/**
 * 定义该注解,用于导入AppImportSelector
 * @author lan
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AppImportSelector.class})
public @interface EnableInstance {

}

在启动类中激活@EnableInstance注解,从而导入ImportSelector:

package com.lan.version4;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.lan.version4.config.EnableInstance;

@SpringBootApplication
@EnableInstance // 激活该注解,从而使得导入ImportSelector
public class App {
	public static void main(String[] args) {
		new SpringApplication(App.class).run(args);
	}
}

到这里,再结合org.springframework.boot.autoconfigure.EnableAutoConfiguration注解,自动装配的原理就已经清晰了。

6. @EnableAutoConfiguration注解激活自动装配

查看@SpringBootApplication注解,可以看到,里面包含了@EnableAutoConfiguration注解。

SpringBootApplication.java:

@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注解,该注解使用@Import,导入了AutoConfigurationImportSelector。

EnableAutoConfiguration.java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // 省略...
}

查看AutoConfigurationImportSelector,该类实现了ImportSelector接口,其中覆盖的selectImports方法,就是获取所有当前classloader中的所有META-INF/spring.factories文件,并读取该文件的org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的类的集合作为返回值。

AutoConfigurationImportSelector.java:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    
    // 省略...
    
    @Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) { // 检查AutoConfiguration是否被关闭
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata); // 获取所有spring.factories中的org.springframework.boot.autoconfigure.EnableAutoConfiguration有效的配置类
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

    // 省略...

    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 获取所有spring.factories中的org.springframework.boot.autoconfigure.EnableAutoConfiguration配置类
		configurations = removeDuplicates(configurations); // 去重
		Set<String> exclusions = getExclusions(annotationMetadata, attributes); // 读取EnableAutoConfiguration注解的exclude属性
		checkExcludedClasses(configurations, exclusions); 去掉classloader中没有的类
		configurations.removeAll(exclusions); // 去掉exclude的类
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 读取所有spring.factories文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration值
		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;
	}

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

}

7. 总结

SpringBoot自动装配,实际上是通过@SpringBootApplication继承的@EnableAutoConfiguration+@Import导入ImportSelector,ImportSelector中读取了所有的spring.factories得到了需要实例化的bean的类集合,并将类集合实例化成bean。其中spring.factories配置的key就是org.springframework.boot.autoconfigure.EnableAutoConfiguration注解类。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值