SpringBoot源码解析-@SpringBootApplication注解

本文详细解析了Spring Boot中关键的@SpringBootApplication注解,包括它的组件扫描、自动配置等功能。@SpringBootConfiguration等价于@Configuration,@EnableAutoConfiguration则涉及到自动配置包管理和组件导入。特别地,AutoConfigurationImportSelector作为自动配置导入选择器,负责组件的装配,通过SpringFactoriesLoader加载工厂名称来确定自动配置项。
摘要由CSDN通过智能技术生成

对一个spring boot项目,其启动类上会加上这个注解,那这个注解是干嘛的呢?

1.@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

@ComponentScan是定义扫描包,@SpringBootConfiguration定义一个配置类,等价于@Configuration,然后就是@EnableAutoConfiguration注解

2.@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 {};
}

这里加了@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)两个注解

其中@AutoConfigurationPackage注解的作用是将 添加该注解的类所在的package 作为 自动配置package 进行管理

@Import是向容器中添加对应组件

3.AutoConfigurationImportSelector

直接翻译就是自动配置导入选择器,就是进行组件装配的,我们看一下类定义

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

该类实现了DeferredImportSelector接口

public interface DeferredImportSelector extends ImportSelector 

DeferredImportSelector接口继承自ImportSelector接口,所以其selectImports方法会被实现,我们看一下其具体内容

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

首先是校验,然后获取AutoConfigurationEntry最后转换成字符串数组,这里重点就是如何获取AutoConfigurationEntry下面看一下getAutoConfigurationEntry方法的实现

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//获取注解里设置的属性,在@SpringBootApplication设置的exclude,excludeName属性值,
		//其实就是设置@EnableAutoConfiguration的这两个属性值
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//获取所有jar包里面META-INF/spring.factories加载配置类的名称,
		// 打开这个文件发现里面包含了springboot框架提供的所有配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		//去重操作
		configurations = removeDuplicates(configurations);
		//获取自己配置不需要生成bean的class
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		//校验被exclude的类是否都是springboot自动化配置里的类,如果存在抛出异常
		checkExcludedClasses(configurations, exclusions);
		//删除被exclude掉的类
		configurations.removeAll(exclusions);
		//过滤刷选,满足OnClassCondition的类
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		//封装返回
		return new AutoConfigurationEntry(configurations, exclusions);
	}

重点就是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;
	}

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

这里调用了SpringFactoriesLoader.loadFactoryNames方法,我们继续看吧

	/**
	 * 加载资源
	 */
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		//获取factoryType的全类名
		String factoryTypeName = factoryType.getName();
		
		//加载SpringFactories并获取factoryTypeName对应资源
		//getOrDefault保证若未获取到对应资源则返回一个空集合
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

然后就是loadSpringFactories方法

	/**
	 * 加载SpringFactories,也就是要加载META-INF/spring.factories文件中的内容
	 * @param classLoader 类加载器
	 */
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		//先检查cache缓存,避免重复加载
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			//获取所有的META-INF/spring.factories文件路径
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			
			//初始化result为一个LinkedMultiValueMap
			result = new LinkedMultiValueMap<>();
			
			//遍历所有urls  关于Enumeration接口实现可参考CompoundEnumeration
			while (urls.hasMoreElements()) {
				//获取一个url
				URL url = urls.nextElement();
				//将url包装成一个UrlResource
				UrlResource resource = new UrlResource(url);
				//加载UrlResource并转换成Properties
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				//遍历properties的所有节点
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					//获取key
					String factoryTypeName = ((String) entry.getKey()).trim();
					//遍历结果集,添加到result中
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			//加入缓存中
			cache.put(classLoader, result);
			return result; //返回结果
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

FACTORIES_RESOURCE_LOCATION路径定义

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

到此结束

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值