源代码分析 spring.factories 的使用原理和实现自动装配

本文深入探讨了SpringBoot中spring.factories文件的作用及其实现自动装配的机制。通过SpringFactoriesLoader类,系统能读取并实例化配置在META-INF/spring.factories中的接口实现,从而简化大型项目中自定义依赖包的bean注入过程。

源代码分析 spring.factories 的使用原理和实现自动装配

一、Spring Boot是如何使用spring.factories文件的呢?

0.Spring Boot仿照Java中的SPI扩展机制实现了自己的扩展机制。它在META-INF/spring.factories文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。这种自定义的SPI机制是Spring Boot Starter实现的基础。

1.SpringFactoriesLoader类
spring-core包里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。在这个类中定义了两个对外的方法:
loadFactories() 和 loadFactoryNames() ,loadFactories()方法中也是调用了loadFactoryNames()。

2.源代码


	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
		
		    /**
		      * public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
		      */
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
					
					
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}


从代码中我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。也就是说我们可以在自己的jar中配置spring.factories文件,不会影响到其它地方的配置,也不会被别人的配置覆盖。

二.使用spring.factories来实现自动装配。

0.对于我们添加的依赖,我们可以手动来配置这些jar包所需要的bean;同时呢,我们也可以spring.factories来实现自动装配。相比于手写的方式,这样做的好处就是在构建大型项目的时候,有时你可能会自己写一个外部依赖包(类似spring-boot-starter-xxx)。主项目添加这个外部依赖包后,这个外部依赖包里面的bean如果需要自动加入主项目的spring容器中(而不必手动添加@Bean实例化)。

1.如何使用spring.factories来实现自动装配的呢?

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

    在源码里,@EnableAutoConfiguration 这个注解是关键。我们知道一般@Enable…开头的注解多是开关,含义是开启… 这里的含义就是开启自动配置。


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

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}


    在源码里,可以看到它引入了AutoConfigurationImportSelector类。


	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);
	}


	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(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader())
    因此我们在使用spring.factories来实现自动装配时,可以在spring.factories文件中添加这样的键值对.


org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.portal.web.autoconfigure.DevelopmentDomainAutoConfigure

    key 是 EnableAutoConfiguration 的全路径名,value是我们自己定义文件的路径名。
这样,我们就可以实现jar包中bean的自动装配。

### Spring Boot 中 `spring.factories` 文件的作用 `spring.factories` 是 Spring Boot 的核心配置文件之一,主要用于实现自动配置机制框架扩展功能[^1]。此文件位于 JAR 包内的 `META-INF/spring.factories` 路径下。 #### 自动化配置支持 通过 `spring.factories` 文件,开发者能够定义一系列与应用程序启动运行有关的关键类。当 Spring Boot 应用程序初始化时会读取此类目下的条目,并依据所设定的内容完成相应的组件注册服务激活操作[^3]。 例如,在默认情况下,Spring Boot 使用该文件来加载各种内置特性: ```properties org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,... ``` 上述代码片段展示了如何利用逗号分隔的形式列举多个待启用的自动化配置类名称列表。 #### 扩展能力增强 除了辅助构建过程外,`spring.factories` 还允许第三方库轻松集成至现有项目之中而不必修改源码结构或引入额外依赖项。只要遵循既定格式向其中添加必要的元数据信息即可达成目的。 对于希望被识别为条件判断处理器(即实现了 Condition 接口的对象),则可以通过如下方式声明: ```properties org.springframework.context.annotation.Condition=com.example.MyCustomCondition ``` 这表明每当遇到带有特定属性标记(@ConditionalOnProperty, @ConditionalOnClass 等)的方法调用时都会触发自定义逻辑执行流程验证是否满足预期前提条件[^2]。 ### 配置实例展示 假设有一个名为 my-library.jar 的外部模块想要将自己的服务纳入到主工程的服务发现体系当中,则可以在其内部创建一个 `META-INF/spring.factories` 文本资源文件并将下面内容写入进去: ```properties # 定义要自动装配的 Bean 类型 org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.mycompany.library.LibraryServiceConfig # 注册新的事件监听器类型 org.springframework.context.ApplicationListener=com.mycompany.library.EventLoggingListener ``` 这样做的好处在于无需改动任何地方就能使新加入的功能模块无缝衔接进来并正常工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值