【SpringBoot自动装配】@ComponentScan @EnableTransactionManagement都去哪了

本文详细阐述了SpringBoot中自动装配的原理,讨论了@ComponentScan、@EnableTransactionManagement的简化,以及@Autowired、@Bean的替代。重点讲解了SpringBoot如何通过@SpringBootApplication整合配置,自动扫描和导入组件,以及条件注解在组件注册中的应用。
摘要由CSDN通过智能技术生成

一丶疑问

大家时否有这样的疑问,写spring项目时需要使用xml或者注解来

  • 扫描包扫标注了@Controller、@Service、@Repository,@Component 到spring容器
  • 开启事务,来保证sql操作的原子性

但是到了springboot我们没有写过如下的

@ComponentScan("com.jh")
@EnableTransactionManagement
@Configuration
public class Config {
	....
}
<context:component-scan base-package="com.jh"/>
<tx:annotation-driven/>

这是为什么呢?原因就在于springboot的自动装配

二丶快速加入容器注解与条件注解

快速加入容器注解:

  • 包扫描(@ComponentScan)+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
  • @Bean[导入的第三方包里面的组件,但这需要知道构造方法]
  • @Import[快速给容器中导入一个组件]
    • @Import(类.class);容器中就会自动注册这个组件,id默认是全类名
    • @Import(ImportSelector.class):返回需要导入的组件的全类名数组;
    • @Import(ImportBeanDefinitionRegistrar.class):手动注册bean到容器中

条件注解,满足条件才会加入容器:

  • @Conditional(MyCondition.class)
// 包扫描(@ComponentScan)+组件标注注解
@ComponentScan("com.jh")
// @Import[快速给容器中导入一个组件]
@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig2 {
    //bean方式加入
	@Bean("person")
	public Person person(){
		System.out.println("给容器中添加Person....");
		return new Person("张三", 25);
	}
	// 条件注解,如果满足条件,即返回true才加入容器管理
	@Conditional(MyCondition.class)
	@Bean("windows")
	public Person person02(){
		return new Person("windows", 48);
	}
}
// 条件类,判断如果是windows系统加入容器
public class MyCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		Environment environment = context.getEnvironment();
		String property = environment.getProperty("os.name");
		if(property.contains("Windows")){
			return true;
		}
		return false;
	}

}

class MyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")){
            return true;
        }
        return false;
    }

}
class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"org.hhh.Red"};
    }
}
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean def=registry.containsBeanDefinition("org.hhh.Red");
        if (def) {
            BeanDefinition beanDefinition=new RootBeanDefinition(Color.class);
            registry.registerBeanDefinition("color",beanDefinition);
        }
    }
}
class Red { }
class Blue { }
class Color { }
class Person {
    private String name;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Person() { }
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}
class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext1= new AnnotationConfigApplicationContext(MainConfig.class);
        String[] strings=applicationContext1.getBeanDefinitionNames();
        for (String str:strings){
            System.out.println(str);
        }
    }
}



结果如下:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig # 由于@Configuration
org.hhh.Blue  #@Import({Blue.class})
org.hhh.Red #@Import({MyImportSelector.class})
person # @bean
windows # @bean + @conditonal 我的是windows
color #@Import({MyImportBeanDefinitionRegistrar.class})

三丶SpringBoot自动装配

@SpringBootApplication
    ├─ @SpringBootConfiguration  #配置类
       ├─@Configuration
    ├─ @EnableAutoConfiguration # 
       ├─@AutoConfigurationPackage
         ├─@Import({Registrar.class}) # 扫描根包及其子包注册到容器
       ├─@Import({AutoConfigurationImportSelector.class}) # 将第三方自动配置类加入容器
    ├─ @ComponentScan #注解扫描

整个过程是@SpringBootApplication有着至关重要的作用

1. @ComponentScan

可以进行注解扫描

2. @Configuration

@SpringBootConfiguration 下面的@Configuration使得 启动类本身就是配置类

@SpringBootApplication
public class CartApp {
    public static void main(String[] args) {
        SpringApplication.run(CartApp.class,args);
    }
    @Bean
    ...
}

3. @AutoConfigurationPackage的@Import({Registrar.class})

你会发现这个Registrar.class与我们之前写得MyImportBeanDefinitionRegistrar一样就是往里面注入组件, 作用扫描根包及其子包批量注册到容器

// 自己写的
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean def=registry.containsBeanDefinition("org.hhh.Red");
        if (def) {
            BeanDefinition beanDefinition=new RootBeanDefinition(Color.class);
            registry.registerBeanDefinition("color",beanDefinition);
        }
    }
}
// Registrar
 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
        }
    }

扫描根包及其子包批量注册到容器,因为AutoConfigurationPackages.register就是拿到 引导类所在的根包,从而扫描工程下根包及其子包的所有组件,可以看到下面断点程序拿到了根包目录
在这里插入图片描述

4. @Import({AutoConfigurationImportSelector.class})

作用第三方依赖的自动配置加入容器

AutoConfigurationImportSelector 也是与MyImportBeanDefinitionRegistrar 一致作用,进入下面断点的getAutoConfigurationEntry方法在这里插入图片描述
发现了下面的127个自动配置类,这是如何获取的呢?
在这里插入图片描述
其实是由下面步骤依次得到,进去getCandidateConfigurations ,loadFactoryNames ,getOrDefault,META-INF/spring.factories 如下图
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
最后我们发现由类加载去META-INF/spring.factories下面找资源
我们先拿springboot的autoconfiguration看看,里面都是全类限定名
在这里插入图片描述
但是并不会添加所有的,这就是conditional作用,比如redis和mq的自动配置类,必须满足规则才会引入这个自动配置类,由于redis和mq的conditional没有其他的严格,所以没有配置数据源等等系统能正常启动,但是一旦遇到redis操作和mq操作就会报错
在这里插入图片描述

在这里插入图片描述
但是像druid这种严格,一旦数据源没有配置,就会无法启动
在这里插入图片描述

在这里插入图片描述

四丶结论

@ComponentScan @EnableTransactionManagement都去哪了?

都在@SpringBootApplication里面,所以之前spring工程需要写很多的配置类,也会被springboot看是否符合需要自动加载,不需要人为介入

springboot帮了我们很多,默认大于配置

五丶进阶

ConfigurationClassPostProcessor
在这里插入图片描述

1. 判断之前加入的bd是否被某些注解修饰了,有注释的添加到configCandidates

for (String beanName : candidateNames) {
	// 获取指定名称的BeanDefinition对象
	BeanDefinition beanDef = registry.getBeanDefinition(beanName);
	// 如果beanDefinition中的configurationClass属性不等于空,那么意味着已经处理过,输出日志信息
	if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
		if (logger.isDebugEnabled()) {
			logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
		}
	}
	// 判断当前BeanDefinition是否是一个配置类,并为BeanDefinition设置属性为lite或者full,此处设置属性值是为了后续进行调用
	// 如果Configuration配置proxyBeanMethods代理为true则为full
	// 如果加了@Bean、@Component、@ComponentScan、@Import、@ImportResource注解,则设置为lite
	// 如果配置类上被@Order注解标注,则设置BeanDefinition的order属性值
	else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
		// 添加到对应的集合对象中
		configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
	}
}

分类
checkConfigurationClassCandidate 获取注解元数据信息
metadataReader.getAnnotationMetadata()
检测 Configuration
检测
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
检测
metadata.hasAnnotatedMethods(Bean.class.getName());

2. 生成名字

if (registry instanceof SingletonBeanRegistry) {
	sbr = (SingletonBeanRegistry) registry;
	if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
		BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
		this.componentScanBeanNameGenerator = generator;
		this.importBeanNameGenerator = generator;
	}
}

3. 配置解析类

ConfigurationClassParser parser = new ConfigurationClassParser(
	this.metadataReaderFactory, this.problemReporter, this.environment,
	this.resourceLoader, this.componentScanBeanNameGenerator, registry);

4. 要处理的处理过的集合

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());

5. 解析

parser.parse(candidates);

processConfigurationClass

6. 解析细节 6.1~6.3前置,6.4 核心处理

6.1 根据条件注解查看是否跳过解析

if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
	return;
}

shouldSkip 遍历条件是否符合,根据matches方法

6.2 防止多次处理,进行合并等处理

ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
	if (configClass.isImported()) {
		if (existingClass.isImported()) {
			existingClass.mergeImportedBy(configClass);
		}
		// Otherwise ignore new imported config class; existing non-imported class overrides it.
		return;
	}
	else {
		// Explicit bean definition found, probably replacing an import.
		// Let's remove the old one and go with the new one.
		this.configurationClasses.remove(configClass);
		for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
			if (configClass.equals(it.next())) {
				it.remove();
			}
		}
	}
}

6.3 递归处理父类,变成sourceClass解析…

SourceClass sourceClass = asSourceClass(configClass);

6.4 核心处理 doProcessConfigurationClass

6.4.1 递归处理内部类

processMemberClasses(configClass, sourceClass)

有的话回到解析过程第五步5. 解析processConfigurationClass,递归

6.4.2 解析@PropertySource

@PropertySource({“classPath:db.properties”})
@Value("#{jdbc.url}")
String url

最终会把这些属性加载到beanFactory 的MutablePropertySource

for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
		sourceClass.getMetadata(), PropertySources.class,
		org.springframework.context.annotation.PropertySource.class)) {
	if (this.environment instanceof ConfigurableEnvironment) {
		processPropertySource(propertySource);
	}
	else {
		logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
				"]. Reason: Environment must implement ConfigurableEnvironment");
	}
}

6.4.3 解析@ComponentScan

最终会把这些属性加载到bd,这里也会递归处理parse
核心的是findCandidateComponents,找出来后进行解析处理

Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
		sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
		!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
	for (AnnotationAttributes componentScan : componentScans) {
		// The config class is annotated with @ComponentScan -> perform the scan immediately
		Set<BeanDefinitionHolder> scannedBeanDefinitions =
				this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
		// Check the set of scanned definitions for any further config classes and parse recursively if needed
		for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
			if (ConfigurationClassUtils.checkConfigurationClassCandidate(
					holder.getBeanDefinition(), this.metadataReaderFactory)) {
				parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
			}
		}
	}
}

6.4.4 解析 @Import

processImports 下面仍然会有递归processImports

参数getImports也是递归,递归收集所有的生命的{@code @Import} values

// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);

6.4.5 解析 @ImportResource

加载某些spring的配置文件

// Process any @ImportResource annotations
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
	AnnotationAttributes importResource =
			AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
	String[] resources = importResource.getStringArray("locations");
	Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
	for (String resource : resources) {
		String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
		configClass.addImportedResource(resolvedResource, readerClass);
	}
}

6.4.6 解析 @Bean

// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
	configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

6.4.7 解析接口@Bean

jdk8接口可以由默认实现,可以在接口方法加@Bean

6.4.8 解析父类

// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
	String superclass = sourceClass.getMetadata().getSuperClassName();
	if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
		this.knownSuperclasses.put(superclass, configClass);
		// Superclass found, return its annotation metadata and recurse
		return sourceClass.getSuperClass();
	}
}

六丶进阶理解

总体:执行BFPP有个核心的ccpp解析注解解析,包含@…等等的解析,解析@Import,会从启动类开始解析,会发现识别到@ACIS,有个延迟加载的实现,getImports的方法可以获取autoConfigurationEntry对象,获取entry对象会调用getCandidateConfiguration方法可以把配置文件中对应属性值加载来完成我们的自动加载回来
springboot主要就是两个Import,也就是解析@Import

用这个方法找出所有的Import,下面是主要代码

processImports(configClass, sourceClass, getImports(sourceClass), true);

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
	Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
	Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
	collectImports(sourceClass, imports, visited);
	return imports;
}

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
		throws IOException {

	if (visited.add(sourceClass)) {
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
				collectImports(annotation, imports, visited);
			}
		}
		imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}

collectImport会将SpringBootApplication上的所有注解进行递归遍历,发现有两个Registrar.class和AutoConfigurationImportSelector.class

@SpringBootApplication
    ├─ @SpringBootConfiguration  #配置类
       ├─@Configuration
    ├─ @EnableAutoConfiguration # 
       ├─@AutoConfigurationPackage
         ├─@Import({Registrar.class}) # 扫描根包及其子包注册到容器
       ├─@Import({AutoConfigurationImportSelector.class}) # 将第三方自动配置类加入容器
    ├─ @ComponentScan #注解扫描

processImports 导入Selector额外的配置类同时完成具体类的实例化工作
就像我们上面说的三种导入 ImportSelector,ImportBeanDefinitionRegistrar,直接导入

  • ImportSelector:继承DeferredImportSelector这个接口可以延迟处理,暂先加入deferredImportSelectors,不然直接处理。处理是递归的
  • ImportBeanDefinitionRegistrar:实例化导入类
this.importStack.push(configClass);
try {
	for (SourceClass candidate : importCandidates) {
		if (candidate.isAssignable(ImportSelector.class)) {
			// Candidate class is an ImportSelector -> delegate to it to determine imports
			Class<?> candidateClass = candidate.loadClass();
			ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
			ParserStrategyUtils.invokeAwareMethods(
					selector, this.environment, this.resourceLoader, this.registry);
			if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
				this.deferredImportSelectors.add(
						new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
			}
			else {
				String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
				Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
				processImports(configClass, currentSourceClass, importSourceClasses, false);
			}
		}
		else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
			// Candidate class is an ImportBeanDefinitionRegistrar ->
			// delegate to it to register additional bean definitions
			Class<?> candidateClass = candidate.loadClass();
			ImportBeanDefinitionRegistrar registrar =
					BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
			ParserStrategyUtils.invokeAwareMethods(
					registrar, this.environment, this.resourceLoader, this.registry);
			configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
		}
		else {
			// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
			// process it as an @Configuration class
			this.importStack.registerImport(
					currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
			processConfigurationClass(candidate.asConfigClass(configClass));
		}
	}
}

deferredImportSelectors这个会在parse的最后执行 this.deferredImportSelectorHandler.process();

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}

		this.deferredImportSelectorHandler.process();
	}
public void processGroupImports() {
	for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
		Predicate<String> exclusionFilter = grouping.getCandidateFilter();
		grouping.getImports().forEach(entry -> {
			ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
			try {
				processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
						Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
						exclusionFilter, false);
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
								configurationClass.getMetadata().getClassName() + "]", ex);
			}
		});
	}
}
public Iterable<Group.Entry> getImports() {
	for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
		this.group.process(deferredImport.getConfigurationClass().getMetadata(),
				deferredImport.getImportSelector());
	}
	return this.group.selectImports();
}
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
	Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
			() -> String.format("Only %s implementations are supported, got %s",
					AutoConfigurationImportSelector.class.getSimpleName(),
					deferredImportSelector.getClass().getName()));
	AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
			.getAutoConfigurationEntry(annotationMetadata);
	this.autoConfigurationEntries.add(autoConfigurationEntry);
	for (String importClassName : autoConfigurationEntry.getConfigurations()) {
		this.entries.putIfAbsent(importClassName, annotationMetadata);
	}
}

protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
	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;
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	Map<String, List<String>> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	result = new HashMap<>();
	try {
		Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				String factoryTypeName = ((String) entry.getKey()).trim();
				String[] factoryImplementationNames =
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
				for (String factoryImplementationName : factoryImplementationNames) {
					result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
							.add(factoryImplementationName.trim());
				}
			}
		}

		// Replace all lists with unmodifiable lists containing unique elements
		result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
				.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
		cache.put(classLoader, result);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
	return result;
}

待完善补充

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yilyil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值