Article Directory
foreword
I use automatic configuration as the first chapter of SpringBoot, because from SpringMvc to SpringBoot, it achieves zero configuration, and there are many default configurations. When understanding the source code later, there may be some places that I don’t understand, and the idea is not clear. So first understand the part of automatic configuration clearly, and know how one of its configurations is loaded, and the subsequent learning should be smoother.
SpringBoot's automatic configuration @EnableAutoConfiguration
is enabled by annotations, so let's start with this annotation to see how it implements automatic configuration and conditional filtering.
principle
External jar packages or third-party packages are loaded in the same way. To achieve automatic configuration, you need to follow the spring specification, usually configuration META-INFO/spring.factories
and META-INF/spring-autoconfigure-metadata.properties
two files, so as to realize automatic configuration.
In the Spring chapter spring source code (6) configuration class parsing process , I learned how Spring injects beans through configuration classes, as follows.
Spring judges it as a configuration class, and there are several annotations @Configuration、@Component、@ComponentScan、@Import、@ImportResource
:@Bean
All, Spring’s conventional ways of injecting beans are (Spring judges from beanDefinition, all premise is a beanDefinition):
-
@Component/@Configuration
-
@Component/@Configuration + @Bean
-
@Component/@Configuration + @CompnentScan
-
@Component/@Configuration + @Import
-
@Component/@Configuration + @ImportResource
Then you can probably guess the way to introduce other external classes. You can use @ComponentScan
the scanning method to scan in the external package, and this method is to apply the active scanning. This active method is not flexible for the framework of component integration. , For example, if you open an interface to a third party, and then you cannot call the interface, you have to add this interface in your configuration, does it feel very inconsistent, like, I develop an idea Plug-in, I have done it according to the specification of idea, released it, and I also use it to notify the official of idea, let them enable the use permission of my plug-in, is it outrageous, no one cares about you.
So what we should pay attention to should be passively accepting components, and then scanning, just like assembling a car, with spring providing the framework, main control chip, and engine, we will assemble the rest.
In Tomcat, Tomcat initializes the application by META-INF/services/javax.servlet.ServletContainerInitializer
reading the application implementation class, and spring is the same, it META-INFO/spring.factories
loads the configuration by reading this file.
For example, the dependency package of mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
When imported, it relies on a
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
It can be seen that there are two configuration classes configured in it, and these two are the cores that mybatis can automatically configure
Then let's look at the automatic configuration of SpringBoot. Spring will introduce the following automatic configuration dependency. There are many automatic configuration classes in it. Basically all the automatic configurations of spring-boot are here. If you look carefully, you will see to aop, jpa, MongoDB, kafka, servlet, etc.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
@EnableAutoConfiguration
Parsing Import
So how does SpringBoot load external packages, or third-party packages?
When starting the SpringBoot application, annotations will be added @SpringBootApplication
, and this annotation is the key
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
SpringApplication.run(App.class);
This is to start the web container, the spring container, and then the Spring set.
The definition we looked at @SpringBootApplication
, there is one in it @EnableAutoConfiguration
, this is the annotation for automatic configuration loading
@EnableAutoConfiguration
There is such a note in the note@Import({AutoConfigurationImportSelector.class})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
We can find out through annotations that its value can be set to four types of classes:
- Annotated
@Configuration
configuration classes - class that implements
ImportSelector
the interface - implemented
ImportBeanDefinitionRegistrar
class - common
component
class
When Spring starts, it will create an ioc container, and add our configuration class ( @SpringBootApplication
marked startup class) as a configuration class, which is equivalent to one Configuration
and the first configuration class, through which other configuration classes or beans are scanned; When parsing this configuration class, it will imports
be parsed regardless of whether it is present or not
The location of the parsing configuration class: org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
Analysis importer location: org.springframework.context.annotation.ConfigurationClassParser#processImports
The function of this step is to find out the ImportSelector class in imports, and also find out the beanDefinition register in the Import annotation, just find out, and do nothing.
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 当配置类没有impots时不再进行
if (importCandidates.isEmpty()) {
return;
}
// 判断是否循环引入
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 标记正在import的配置类
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// 判断import的类是否是ImportSelector子类
// @SpringBootApplication里的import注解走的就是这里
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
// 排除选项
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
// ImportSelector还有另一个子类接口DeferredImportSelector,
// 这里只做了一个动作:添加importHandler
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 普通的imports
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 判断import的类是否是ImportBeanDefinitionRegistrar子类
// 这个子类从命名上看就是一个beanDefinition注册器,spring中的所有bean都是先通过beanDefinition注册后,再根据beanDefinition实例化的,所有这里的注册器后面也要添加到已经载入的注册器中
// 可以查看:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
// 它在parse(配置类)后有这么一句:this.reader.loadBeanDefinitions(configClasses);
// 这句就是将这里找出的注册器都添加的到已经加入的spring容器里的BeanDefinitionReader
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// import的类不是ImportBeanDefinitionRegistrar,ImportSelector,那么就作为配置类进行载入
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
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();
}
}
}
Take a look at: ParserStrategyUtils.instantiateClass(...)
, its instantiation is not a simple instantiation, but also initializes the instantiated bean.
org.springframework.context.annotation.ParserStrategyUtils#instantiateClass
static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo, Environment environment,
ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
// 二次校验
Assert.notNull(clazz, "Class must not be null");
Assert.isAssignable(assignableTo, clazz);
if (clazz.isInterface()) {
// import 的类不能是接口
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
// 反射实例化,这里AutoConfigurationImportSelector是无参构造
T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader);
// 这一步就是为这个实例化的AutoConfigurationImportSelector进行遍历初始化,
ParserStrategyUtils.invokeAwareMethods(instance, environment, resourceLoader, registry, classLoader);
return instance;
}
It can be seen from this step that @Imports
Spring gives it the greatest support for the imported classes:BeanClassLoaderAware、BeanFactoryAware、EnvironmentAware、ResourceLoaderAware
private static void invokeAwareMethods(Object parserStrategyBean, Environment environment,
ResourceLoader resourceLoader, BeanDefinitionRegistry registry, @Nullable ClassLoader classLoader) {
if (parserStrategyBean instanceof Aware) {
if (parserStrategyBean instanceof BeanClassLoaderAware && 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);
}
}
}
After finding the class that needs to be imported above, execute ImportSelectorHandle
位置:org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
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);
}
}
// importSelectorHandler执行深度import
this.deferredImportSelectorHandler.process();
}
**Summary:** The above steps are to import
parse the annotations, and then parse the annotated classes, including recursive parsing.
There are three cases of import:
- If the interface is implemented
ImportSelector
, then this type of interface needs to be imported by executing the interface method, so it will be added to thedeferredImportSelectorHandler
ImportBeanDefinitionRegistrar
The interface is implemented. This interface belongs to the registration interface of beanDefinition. The creation of beans in spring is based on beanDefinition, so it should be based on unified steps, and in the process of parsing configuration classes, it has not yet been created . The steps of beans are still in the preparation stage, so they will be instantiated together with other bean definitions later, so they are all put in here, and after theconfigurationClasses
configuration class is parsed later, it will be taken out and merged- In other cases, ordinary configuration classes, such as
Configuration component
etc., are also put in as configurationconfigurationClasses
classes
Then the next step ( this.deferredImportSelectorHandler.process();
) is to execute the class @Import
obtained through the annotation , which can be said to be a deep import. This is our description of its method function, which may not be accurate.ImportSelector
Execute import class handler
Here's deferredImportSelectorHandler.process
how to do it, but here's deferredImportSelectorHandler.handle
a way to mention it.
位置:org.springframework.context.annotation.ConfigurationClassParser#processImports
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
It has a null judgment, but the class is new when it is initialized, and there is no null situation.
What I want to say is that the entire null situation is after the selector scans, and he will execute the process method ( this.deferredImportSelectorHandler.process();
), in the process method, it loops deferredImportSelectors
and then executes, and deferredImportSelectors
it is not thread-safe, and parsing the configuration class is also a cycle, so This process cannot guarantee that add will occur during execution, so the process will be empty first, so that when handling, you know that the current handle set is already being processed and cannot be added, then execute directly Bar;
The meaning of renewing a list in the process is the same, to ensure that the current cycle is not destroyed.
// deferredImportSelectorHandler.handle
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
// 当要添加时,发现为null,表明这个集合已经在循环处理了,这里可以直接执行
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
this.deferredImportSelectors.add(holder);
}
}
// deferredImportSelectorHandler.process
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
// 这里执行必然不会是null,这里new List()是为了避免在循环过程中,handle方法又进行添加对象
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
}
Then look at the process method
位置:org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process
deferredImports.forEach(handler::register);
Adding group to process AutoConfigurationImportSelector
is returned directly by the methodAutoConfigurationGroup.class
So two things are initialized here:
- group -> AutoConfigurationGroup.class automatically configures the group, and the group is aimed at two scenarios:
- Current jar package - "DefaultDeferredImportSelectorGroup.class This article does not analyze
- External jar package - "AutoConfigurationGroup.class ( the focus of this analysis )
- deferredImports -> DeferredImportSelector, a subclass of DeferredImportSelector in @Import.value, it needs
public void register(DeferredImportSelectorHolder deferredImport) {
// 这里其实是有值的,AutoConfigurationImportSelector它重写了这个方法,并返回了AutoConfigurationGroup.class
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 把handle添加到grouping
// 实际执行是:this.deferredImports.add(deferredImport);
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// 这个getImports实际上是通过上一步添加的group.processs执行的
grouping.getImports().forEach(entry -> {
// 遍历得到的configurationEntry
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);
}
});
}
}
grouping.getImports()
The method actually executed:
Here group refers to AutoConfigurationImportSelector.AutoConfigurationGroup, which is fixed, while deferredImport is obtained from annotations, and is not fixed. Group.process can be regarded as an executor, and deferredImport is implemented as a third-party interface
// grouping.getImports()
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 这里通过AutoConfigurationImportSelector.AutoConfigurationGroup#process执行
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 这里将导入的类返回
return this.group.selectImports();
}
// group.process
/**
* @param annotationMetadata 配置类的注解信息
* @param deferredImportSelector 配置类上@Import.value里的类
*/
@Override
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()));
// 这里它会去获取自动配置的entry
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
Here is the construction of the class (List) read from the spring.fatories file AutoConfigurationEntry
, which can be regarded as a configuration class object, followed by parsing
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);
// 加载配置过滤
// 读取spring.factories中key=org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的3个条件处理类,作为导入配置类的过滤处理器(import filter)
// 然后读取META-INF/spring-autoconfigure-metadata.properties下的键值(配置类的条件,格式className.condition)
// 这里这样处理,是因为在这一步并没有加载class,还知识className,通过spring-autoconfigure-metadata.properties配置,将条件写进到里面,然后进行过滤,下面还会提到,这里只是说明一下
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
Here is List<String> configurations
the logic it gets, except for the part that reads the file. From this paragraph, it can be seen that it loads the value SpringFactoriesLoader.loadFactoryNames
according to the full class name. The bottom layer of this method is to read directlyEnableAutoConfiguration
META-INF/spring.factories
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// getSpringFactoriesLoaderFactoryClass() = () -> return EnableAutoConfiguration.class;
// 所以这里它是通过 EnableAutoConfiguration 全类名获取对应 META-INF/spring.factories 下的value
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;
}
// 上面一步的:SpringFactoriesLoader.loadFactoryNames方法实现
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
So SpringBoot's automatic configuration is to read the value of this key
**Summary:** This step is DeferredImportSelector
the class to be executed. @Import
If there are subclasses introduced in it, it will be executed here. @EnableAutoConfiguration
What is in the annotation is AutoConfigurationImportSelector
, so getAutoConfigurationEntry
the method will be executed to read
META-INF/spring.factories
The key below org.springframework.boot.autoconfigure.EnableAutoConfiguration
is the key value, which is loaded at this time List<String>
, and then read the configuration class conditional filter processor to filter, then build it into an entry and put it in , and then traverse the recursion autoConfigurationEntries
obtained from spring.factories to parse the import start.autoConfigurationEntries
processImports
SpringBootCondition
In automatic configuration, there is another important type of annotation Conditional
, which is also a function of automatic configuration, which can inject beans according to environmental conditions.
for example:
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnClass
In SpringBoot, it is the basis of all conditional processing classes, and the rest can be judged SpringBootCondition
by implementing its interface.getMatchOutcome
For example @ConditionalOnBean
, if it is also marked on it @Conditional(OnBeanCondition.class)
, then its processing class isOnBeanCondition
Here I also want to mention one point, because I did not elaborate above;
The above @Import
annotation will read META-INF/spring.factories
the configuration class below, and will read the key values of two keys in total:
EnableAutoConfiguration -> This is the class of import
AutoConfigurationImportFilter -> This is the implementation class of the Condition condition processing class, which is used to process the import class
At this time, the imports are all of String type, and the class has not been read yet. Because there are annotations like this, there ConditionalOnClass
will be a filter before loading the class, and the filter condition will be configured and saved in META-INF/spring-autoconfigure-metadata.properties
; its key=full name of the class. The name of the Condition annotation indicates which condition annotations are marked in the configuration class. You can find them in the packages of spring and mybatis.
judgement
org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)
This class is a verification condition verification class that will be called before parsing the configuration class. It is called to judge whether the class is loaded.shouldSkip
位置:org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)
ConfigurationPhase has two values:
- PARSE_CONFIGURATION -> indicates that the parsing configuration is in the parsing configuration stage at this time, the shouldSkip judgment fails, and the class will not be loaded
- REGISTER_BEAN -> indicates that the time is in the bean registration stage, shouldSkipt fails to judge, and the bean will not be registered
/**
* @param metadata 注解元数据信息
* @param phase 执行方法的阶段,时机
*/
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
// 是否有条件注解
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
if (phase == null) {
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
List<Condition> conditions = new ArrayList<>();
// 从条件注解中获取到条件处理类,如@ConditionalOnMissingBean,拿到的就是这个注解上面的@Conditional(OnBeanCondition.class)的OnBeanCondition
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
// 这里就是条件判断,看matches
// 同时这还有一个判断,判断当前配置类上的条件注解需要在什么进行校验的判断
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}
For the following judgment, the conditional verification of the configuration class is all implemented ConfigurationCondition
, but for different configurations, the timing of parsing and loading is different, and there will be differences.
// 这里就是条件判断,看matches
// 同时这还有一个判断,判断当前配置类上的条件注解需要在什么进行校验的判断
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
Take OnBeanCondition
it as an example, it needs REGISTER_BEAN
to be judged in stages, because the condition of onBeanConditional is for bean objects, then he needs to prepare the class, and when a configuration class is scanned, it can be taken out directly when it is in missingBean or onBean.
OnBeanCondition
Actual matching method:
@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
// 定义结果数组
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
for (int i = 0; i < outcomes.length; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
// 这里是获取当前配置类,在条件ConditionalOnBean下所需要的处理类
// 这里get的是spring-autoconfigure-metadata.properties读取到的key value
Set<String> onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean");
// 查找当前需要的bean类型,返回返回结果信息
outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);
if (outcomes[i] == null) {
// 这里也是一样,查找需要的类型,返回结果信息
Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,
"ConditionalOnSingleCandidate");
outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);
}
}
}
return outcomes;
}
/**
* @param requiredBeanTypes 需要查找的bean的类全名
* @param annotation 条件注解,这里因为是ConditionalOnBean代码,所以这里是ConditionalOnBean
*/
private ConditionOutcome getOutcome(Set<String> requiredBeanTypes, Class<? extends Annotation> annotation) {
// 这里是通过需要的类型,去Class.forName反射加载,能加载就将当前的类名添加到missing里
List<String> missing = filter(requiredBeanTypes, ClassNameFilter.MISSING, getBeanClassLoader());
if (!missing.isEmpty()) {
// 构建条件检索结果
ConditionMessage message = ConditionMessage.forCondition(annotation)
.didNotFind("required type", "required types").items(Style.QUOTE, missing);
return ConditionOutcome.noMatch(message);
}
return null;
}
Here ClassNameFilter.MISSING
is a method
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
// 里面其实是通过反射`Class.forName`加载,加载失败那就是没有,返回false
return !isPresent(className, classLoader);
}
};
then returnorg.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#match
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
// 这里就是上面的执行方法,查找需要的类,返回返回结果
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
// 日志输出
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
// 日志记录
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
主方法是:org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)
It has many subclasses, and their implementations are different, but the matching logic is similar.
Summarize
@Import
import support
- Annotated
@Configuration
configuration classes - class that implements
ImportSelector
the interface - implemented
ImportBeanDefinitionRegistrar
class - common
component
class
Used in SpringBoot automatic configuration , it reads the key value of the key @Import(AutoConfigurationImportSelector.class)
through AutoConfigurationImportSelector , loads it into the configuration class list, and then reads the conditional filter processor of the key, and the configuration class conditional configuration of the file, filters and obtains the configuration The entry of the class, and then traverse the obtained entry, and then go through the import analysis process (recursive).META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
META-INF/spring-autoconfigure-metadata.properties
So if we want to implement our custom spring-boot-starter, we need to write one META-INF/spring.factories
, just like mybatis
This process is carried out when the configuration class is parsed, and after the configuration class is parsed, the parsed configuration class will be registered in and ConfigurationClassBeanDefinitionReader
instantiated later.
We can also define the configuration class condition processor by ourselves, just inherit it SpringBootCondition
, and then customize an annotation. I’m lazy here, and I didn’t write a demo. I have time to write one in the future.