You need not put all your @Configuration into a single class. The @Import annotation can be used to import additional configuration classes. Alternatively, you can use @ComponentScan to automatically pick up all Spring components, including @Configuration classes.
通过Java注册Bean的几种方式:
- @Configuration
- @Import
- @ComponentScan
- @ImportResource
源码:org.springframework.context.annotation.ConfigurationClassParser#processImports
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
收集Import类
比如对于以下类
/**
* Configuration to import the {@link BootstrapImportSelector} configuration.
*
* @author Spencer Gibb
*/
@Configuration
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {
}
通过getImports返回的就是BootstrapImportSelector.class
,当然实际操作层面要考虑这个类上面也可能存在非@Import
注解上也可能存在@Import
注解,比如各种@Enable
。
比如说一个很普通的Spring Boot的启动类
@SpringBootApplication
public class BeanDemoStartMain {
public static void main(String[] args) {
SpringApplication.run(BeanDemoStartMain.class);
}
}
其中包含的import如下所示:
这种情况下通过递归进行解决。因此,对于@Import
注解直接获取注解属性,而对于非@Import
注解需要遍历元数据查找是否存在包含@Import
注解(也叫注解的注解)。
另外收集之后不是直接返回这个类,而是进行了包装。包装的类型的为org.springframework.context.annotation.ConfigurationClassParser.SourceClass
。这样可以保存一些注解元数据信息,而不仅仅是类型。
/**
* Returns {@code @Import} class, considering all meta-annotations.
*/
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
递归收集一个类上面待Import的类,除了当前类上main的@Import
注解,其他的注解比如EnableConfigurationProperties
,也包含了@Import
注解,所以处理起来必须进行遍历
/**
* Recursively collect all declared {@code @Import} values. Unlike most
* meta-annotations it is valid to have several {@code @Import}s declared with
* different values; the usual process of returning values from the first
* meta-annotation on a class is not sufficient.
* <p>For example, it is common for a {@code @Configuration} class to declare direct
* {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
* annotation.
* @param sourceClass the class to search
* @param imports the imports collected so far
* @param visited used to track visited classes to prevent infinite recursion
* @throws IOException if there is any problem reading metadata from the named class
*/
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();
// 首先递归处理其他可能包含@Import的注解
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
// 获取Import注解的值,比如上面的BootstrapImportSelector
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
处理Import类
configClass:BootstrapImportSelectorConfiguration,包含@Configuration的那个类
SourceClass :BootstrapImportSelectorConfiguration,可能是configClass,也可能是内部类
importCandidates:通过以上步骤收集到的需要进行Import的类
checkForCircularImports:默认情况下为true 可能存在A import B,B import C,C import A这种情况,这里是不支持这种情况的
- 首先要考虑解决链式循环引入的问题
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
// 通过一个栈解决循环引入或者叫链式引入的问题
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 首先是添加到栈中 用于处理循环引入的问题
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 遍历处理每一个candidate
...
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
-
对于Import的类有两种类型
ImportSelector
、ImportBeanDefinitionRegistrar
,如果不是以上两种类型,按照@Configuration class
进行处理 -
ImportSelector
类型
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
// 从包装类中获取类类型
Class<?> candidateClass = candidate.loadClass();
// 实例化为一个ImportSelector类
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
// 对于ImportSelector是用于注册其他类,需要依靠一些工具类,需要在注册其他类之前首先赋值
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 如果非DeferredImportSelector 则现在就获取用户定义的需要进行import的类(selectImports)
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
ImportBeanDefinitionRegistrar
类型的处理
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());
}
区别于类型ImportSelector
(直接查找对应的Import类,然后进行各种解析添加到org.springframework.context.annotation.ConfigurationClassParser#configurationClasses
)属性当中,此处直接添加到org.springframework.context.annotation.ConfigurationClass#importBeanDefinitionRegistrars
属性当中。然后再添加到configurationClasses
属性当中。
- 按照
@Configuration class
进行处理
(这种情况是用户自定义的ImportSelector
实现类的selectImports
方法中需要import的那个类,可能不是ImportSelector
或ImportBeanDefinitionRegistrar
类型,但是包含@Configuration注解,比如BootstrapImportSelector
返回的就是这样一些BootstrapConfiguration
类)
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
// A import B -> 注册为 B —> A
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
将当前被导入的类作为一个ConfigurationClass, importedBy是那个最初引起导入的类。比如A导致B被引入,则此处B将作为一个ConfigurationClass被处理,而A作为importedBy作为参数。对于不是通过@Import
导入的配置类,这个参数则是空的。比如上面提到的BootstrapImportSelectorConfiguration
.
public ConfigurationClass asConfigClass(ConfigurationClass importedBy) {
if (this.source instanceof Class) {
return new ConfigurationClass((Class<?>) this.source, importedBy);
}
return new ConfigurationClass((MetadataReader) this.source, importedBy);
}
解决循环引入的问题
通过一个栈+map的数据结构(org.springframework.context.annotation.ConfigurationClassParser.ImportStack
)解决
private boolean isChainedImportOnStack(ConfigurationClass configClass) {
if (this.importStack.contains(configClass)) {
String configClassName = configClass.getMetadata().getClassName();
AnnotationMetadata importingClass = this.importStack.getImportingClassFor(configClassName);
while (importingClass != null) {
if (configClassName.equals(importingClass.getClassName())) {
return true;
}
importingClass = this.importStack.getImportingClassFor(importingClass.getClassName());
}
}
return false;
}
然后处理每个configClass
的时候都会先入栈,然后如果这个configClass
引入了其他的@Configuration class
,还需要注册到map当中。这里的importingClass是最初的那个configClass类(比如类BootstrapImportSelectorConfiguration
),而importedClass是那个被导入的类(比如用户selectImport方法导入的PropertySourceBootstrapConfiguration
类)。这里是将被导入的那个类作为key的。比如A(importingClass)导致B被Import(importedClass),map中保存数据为B->A,对应的栈里面保存的是A
private final MultiValueMap<String, AnnotationMetadata> imports = new LinkedMultiValueMap<>();
public void registerImport(AnnotationMetadata importingClass, String importedClass) {
this.imports.add(importedClass, importingClass);
}
SourceClass包装类
通过包装可以保存元数据信息
/**
* Simple wrapper that allows annotated source classes to be dealt with
* in a uniform manner, regardless of how they are loaded.
*/
private class SourceClass implements Ordered {
private final Object source; // Class or MetadataReader
private final AnnotationMetadata metadata;
}
public Class<?> loadClass() throws ClassNotFoundException {
if (this.source instanceof Class) {
return (Class<?>) this.source;
}
String className = ((MetadataReader) this.source).getClassMetadata().getClassName();
return ClassUtils.forName(className, resourceLoader.getClassLoader());
}
导入工具类
因为对于ImportSelector
这些类是用来真实Import其他类的,有时候需要一些容器环境信息,比如类加载器、资源加载器、bean工厂等。就比如BootstrapImportSelector
就需要Environment
信息,根据当前环境中的参数配置决定是否真实需要Import某个类。
org.springframework.context.annotation.ParserStrategyUtils#invokeAwareMethods
/**
* Invoke {@link BeanClassLoaderAware}, {@link BeanFactoryAware},
* {@link EnvironmentAware}, and {@link ResourceLoaderAware} contracts
* if implemented by the given object.
*/
public static void invokeAwareMethods(Object parserStrategyBean, Environment environment,
ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
if (parserStrategyBean instanceof Aware) {
if (parserStrategyBean instanceof BeanClassLoaderAware) {
ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
if (classLoader != null) {
((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader);
}
}
if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) {
((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry);
}
if (parserStrategyBean instanceof EnvironmentAware) {
((EnvironmentAware) parserStrategyBean).setEnvironment(environment);
}
if (parserStrategyBean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader);
}
}
}
DeferredImportSelector解决bean注入的一个顺序问题
这种类型的类通过DeferredImportSelectorHandler
特殊处理,为啥呢?因为通过Import注入的类可能是需要在一定的条件下才会进行注入的。比如BootstrapImportSelector
就是用于读取spring.factories
文件中对应key值为BootstrapConfiguration
的配置类。比如以下这个配置类
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class PropertyPlaceholderAutoConfiguration {
@Bean
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
就存在@ConditionalOnMissingBean
这样的注解,需要在全局条件下(所有待注册的Bean都注册完了的情况下才可以)
A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.
Implementations can also extend the org.springframework.core.Ordered interface or use the org.springframework.core.annotation.Order annotation to indicate a precedence against other DeferredImportSelectors.
Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.
- 会在所有的
@Configuration
beans都处理之后再进行导入,主要场景就是@Conditional
,比如一个bean要是在不存在当前类型bean的时候才导入org.springframework.boot.autoconfigure.condition.ConditionalOnBean
- 这个类的实现者可以实现
Ordered
接口或者添加Order
注解来实现优先级问题 - 进行分组以提供额外的排序或者过滤逻辑
private final DeferredImportSelectorHandler deferredImportSelectorHandler = new DeferredImportSelectorHandler();
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle
/**
* Handle the specified {@link DeferredImportSelector}. If deferred import
* selectors are being collected, this registers this instance to the list. If
* they are being processed, the {@link DeferredImportSelector} is also processed
* immediately according to its {@link DeferredImportSelector.Group}.
* @param configClass the source configuration class
* @param importSelector the selector to handle
*/
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
// 首先进行包装
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
configClass, importSelector);
// 这个deferredImportSelectors是一个包装类的列表,初始化为空的列表,但是不可能为null,这段代码不知道有啥用
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
// 添加到包装类的列表中后续再处理
this.deferredImportSelectors.add(holder);
}
}
在所有的configCandidates都进行parse
之后就会进行DeferredImportSelector
的处理了。通过以上的步骤,这些类已经添加到了deferredImportSelectors
列表当中,下面就是从列表中取出来进行处理了。
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
// 进行排序
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 注册到DeferredImportSelectorGroupingHandler中
deferredImports.forEach(handler::register);
// 处理分组结果
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
注册到DeferredImportSelectorGroupingHandler
中,一个LinkedHashMap,一个普通HashMap
private class DeferredImportSelectorGroupingHandler {
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
public void register(DeferredImportSelectorHolder deferredImport) {
// 获取分组 默认的DeferredImportSelector接口实现为null 也就是自己作为一组
Class<? extends Group> group = deferredImport.getImportSelector()
.getImportGroup();
// 默认情况下(group==null)则使用默认的分组
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 将当前对象加入到分组成员当中
grouping.add(deferredImport);
// 添加到容器中
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
}
创建组并包装为DeferredImportSelectorGrouping
对象
private Group createGroup(@Nullable Class<? extends Group> type) {
// 采用默认的分组 DefaultDeferredImportSelectorGroup
Class<? extends Group> effectiveType = (type != null ? type
: DefaultDeferredImportSelectorGroup.class);
// 进行实例化
Group group = BeanUtils.instantiateClass(effectiveType);
// 设置一些关键环境参数和工具类
ParserStrategyUtils.invokeAwareMethods(group,
ConfigurationClassParser.this.environment,
ConfigurationClassParser.this.resourceLoader,
ConfigurationClassParser.this.registry);
return group;
}
DeferredImportSelectorGrouping
对象包含组信息和组员信息
private static class DeferredImportSelectorGrouping {
// 组
private final DeferredImportSelector.Group group;
// 组员
private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
}
进行分组Imports的处理
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
public void processGroupImports() {
// 根据分组 获取Imports 一次进行处理
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
grouping.getImports().forEach(entry -> {
这里的getImports
/**
* Return the imports defined by the group.
* @return each import with its associated configuration class
*/
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
org.springframework.context.annotation.ConfigurationClassParser.DefaultDeferredImportSelectorGroup#process
此时才会真实调用到对应实现类的逻辑,可以看出前面做了很多的铺垫,最后才会真实执行自己定义的selectImports
方法。
@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
// 执行自己定义的selectImports方法并把需要import类添加到Group对象的imports列表当中
for (String importClassName : selector.selectImports(metadata)) {
this.imports.add(new Entry(metadata, importClassName));
}
}
比如BootstrapImportSelector
执行selectImports
方法之后
依次处理上面获取到的需要Import的类
// 首先获取到configurationClass
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
// 进行import 此时不检查循环依赖了 这个方法就是一开始的那个方法
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
这里DeferredImportSelector
使用的是默认的org.springframework.context.annotation.DeferredImportSelector.Group
,实现其实比较简单的,在process方法中解析元数据调用用户实现的selectImports
方法获得需要进行注册的类信息存储到imports
属性当中,而selectImports
更是简单,直接返回这个imports
属性。
其实以上的逻辑再整理一下, 无非就是通过DeferredImportSelectorGrouping
去selectImports
,获取完成之后能就processImports
(递归),但是它代理给了Group
去做这件事,这个Group
就是一个扩展点。用户在自己实现这个DeferredImportSelector
的时候可以自己去定义这个Group
实现类,然后针对特定的deferredImportSelector
更细粒度的进行扩展,而不是仅仅返回一些需要注册的类信息。
比如org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
这个类,就覆盖了DeferredImportSelector
接口中getImportGroup
方法的默认实现。
@Override
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
读取spring.factories中的AutoConfigurationImportListener
的实现类,然后发布AutoConfigurationImportEvent
事件。
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}