Note: The source code analysis corresponds to SpringBoot version 2.1.0.release
1 introduction
ConditionalOnXXX ConditionalOnXXX ConditionalOnXXX
ConditionalOnXxx (ConditionalOnXxx) ConditionalOnXxx (ConditionalOnXxx)
- All SpringBoot
@ConditionalOnXxx
The conditions of the classOnXxxCondition
It’s all inherited fromSpringBootCondition
The base class, andSpringBootCondition
And to achieve theCondition
Interface. SpringBootCondition
The base class is mainly used to print the logs of some condition evaluation reports. The condition evaluation information all comes from its subclass annotation condition classOnXxxCondition
, so it also abstracts a template methodgetMatchOutcome
It is left to subclasses to implement to evaluate whether their conditional annotations meet the conditions.- There is also an important point that we have not covered in the previous article, and that is related to filtering auto-configuration class logic
AutoConfigurationImportFilter
Interface, this article we are going to fill this hole.
In front of us is closely related with SpringBoot automatic configuration is analyzed conditions of built-in annotations @ ConditionalOnXxx, now we start to lu SpringBoot automatic configuration of the relevant source code.
2 @ SpringBootApplication annotation
Before we start, let’s think about how SpringBoot can automatically configure a large number of Starter classes by executing a simple run method with the @SpringBootApplication annotation. SpringBoot automatic configuration with @springBootApplication annotation, let’s take a look at the source of this annotation:
@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 {
/ /... Omit non-critical code
}
Copy the code
@SpringBootApplication has a lot of annotations, and we can see that one of them is @enableAutoConfiguration, so, Certainly SpringBoot automatic configuration is definitely is closely related with @ EnableAutoConfiguration (including @ ComponentScan annotation also has a class AutoConfigurationExcludeFil excludeFilters attribute Ter, this class also has something to do with auto-configuration, but it’s not our focus. Now let’s open the @enableAutoConfiguration annotation source:
@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 {};
}
Copy the code
See @ @ AutoConfigurationPackage EnableAutoConfiguration annotations and marked and @ Import two annotations (AutoConfigurationImportSelector. Class), as the name implies, The @autoConfigurationPackage annotation must be associated with automatically configured packages, The AutoConfigurationImportSelector is associated with automatic configuration choice import SpringBoot (ImportSelector in Spring is used to import the configuration of the class, The decision to import a configuration class is usually based on @conditionalonxxxx).
Therefore, it can be seen that AutoConfigurationImportSelector class is our focal point of this because SpringBoot automatic configuration must have a configuration class, While this configuration class import need accomplished by AutoConfigurationImportSelector this elder brothers.
Finished next we focus to see AutoConfigurationImportSelector this class, we will have a simple analysis of @ AutoConfigurationPackage this annotation logic.
How to find SpringBoot automatic configuration implementation logic entry method?
Certainly SpringBoot automatic configuration of logic must be associated with AutoConfigurationImportSelector this class, so how do we go to find SpringBoot logical entry method to realize automatic configuration?
Looking for realization of automatic configuration SpringBoot logic before entry method, we first look at AutoConfigurationImportSelector related class diagram, well have a whole understanding. See below:
Can see AutoConfigurationImportSelector key is to realize the DeferredImportSelector interfaces and various Aware, And then the DeferredImportSelector interface inherits the ImportSelector interface.
Naturally, we will pay attention to implementation method of selectImports AutoConfigurationImportSelector autotype DeferredImportSelector interface, because selectImports method associated with imported automatic configuration class, This method is often the entry method for program execution. SelectImports (selectImports) : selectImports (selectImports) : selectImports (selectImports) : selectImports (selectImports) : selectImports (selectImports) : selectImports
At this point the plot development seems to be not very logical, how do we find the automatic configuration logic of the entry method?
The simplest method is in AutoConfigurationImportSelector breakpoint on each of the methods of a class, then see which methods to perform to debug. But instead of doing this, let’s recall that when we customize a Starter we do it in the Spring. factories configuration file
EnableAutoConfiguration=XxxAutoConfiguration
Copy the code
Can therefore be concluded that SpringBoot automatic configuration principle must be followed the spring. The factories in the configuration file loaded automatically configure class, then combining AutoConfigurationImportSelector annotation methods, We found the getAutoConfigurationEntry method. So we create a breakpoint in this method, call the stack frame to see where the upper-level entry method is, and then we start with the upper-level entry method related to auto-configuration.
With automatic configuration by figure 1. We can see that are logically related entry method in DeferredImportSelectorGrouping getImports method of a class, So let’s start from DeferredImportSelectorGrouping class getImports method to analyze SpringBoot automatic configuration of the source code.
4 Analyze the principle of SpringBoot automatic configuration
Since found ConfigurationClassParser. GetImports () method is an automatic configuration related entry method, so let’s to really analyze SpringBoot automatic configuration of the source.
Take a look at the getImports method code:
// ConfigurationClassParser.java
public Iterable<Group.Entry> getImports() {
/ / traverse DeferredImportSelectorHolder object collection deferredImports, deferredImports collection with various ImportSelector, Of course, here is AutoConfigurationImportSelector
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// [1], use the AutoConfigurationGroup process method to handle the auto-configurationgroup logic, and determine which configuration classes to import.
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// [2], and then select which configuration classes to import
return this.group.selectImports();
}
Copy the code
The code in label [1] is the most important part of our analysis. Most of the logic related to automatic configuration is all here. The main logic of automatic configuration will be analyzed in depth in 4.1. So this. Group. The process (deferredImport getConfigurationClass (.) for getMetadata (), deferredImport. GetImportSelector ()); Mainly do is in this. Group AutoConfigurationGroup object process method, namely the incoming AutoConfigurationImportSelector object to choose some qualified automatic configuration, Filtering out some of the auto-configuration classes that don’t meet the criteria, that’s all there is to it.
Note:
AutoConfigurationGroup
Is:AutoConfigurationImportSelector
The inner class, mainly used to handle auto-configuration related logic, ownsprocess
andselectImports
Method, and then haveentries
andautoConfigurationEntries
Collection properties, which store the qualified auto-configuration classes that are processed, respectively, and that’s all we need to know;AutoConfigurationImportSelector
: Undertake most of the logic of automatic configuration, and be responsible for selecting some qualified automatic configuration classes;metadata
: marked on the SpringBoot boot class@SpringBootApplication
Annotation metadata
The this.group.selectimports method in standard [2] is mainly used to selectively import the auto-configuration class after the previous process method, which will be further analyzed in 4.2 Selectively importing auto-configuration classes.
4.1 Analyzing the logic of automatic configuration
Here we continue to explore how the this.group.process method in the section [1] of analyzing the principle of SpringBoot automatic configuration handles the logic related to automatic configuration.
// AutoConfigurationImportSelector$AutoConfigurationGroup.java
// This is used to deal with auto-configuration classes, such as filtering out auto-configuration classes that do not match the criteria
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()));
/ / 【 1 】, call getAutoConfigurationEntry method for automatic configuration in autoConfigurationEntry object class
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(),
annotationMetadata);
// [2], and put the autoConfigurationEntry object encapsulated in the autoConfigurationEntries collection
this.autoConfigurationEntries.add(autoConfigurationEntry);
// [3], traverses the auto-configuration class just obtained
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
AnnotationMetadata is inserted into the entries collection as a value
this.entries.putIfAbsent(importClassName, annotationMetadata); }}Copy the code
Code above in the way we look at standard [1] getAutoConfigurationEntry, this method is mainly used to retrieve automatically configure class, bear the automatic configuration of main logic. Directly on the code:
// AutoConfigurationImportSelector.java
// Get the auto-configuration classes that meet the conditions to avoid memory waste by loading unnecessary auto-configuration classes
protected AutoConfigurationEntry getAutoConfigurationEntry( AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
/ / get if there is a spring configuration. The boot. Enableautoconfiguration properties, return true by default
if(! isEnabled(annotationMetadata)) {return EMPTY_ENTRY;
}
// The Configuration class annotated by @Congiguration is the annotated data of the introspectedClass.
/ / such as: @ SpringBootApplication (exclude = FreeMarkerAutoConfiguration. Class)
/ / will get to exclude = FreeMarkerAutoConfiguration. The class and excludeName = "" annotation data
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// [1] get all the auto-configuration classes configured in the Spring. factories file
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// Use LinkedHashSet to remove duplicate configuration classes
configurations = removeDuplicates(configurations);
// Get the auto-configuration class to exclude, such as the configuration class for the annotation attribute exclude
/ / such as: @ SpringBootApplication (exclude = FreeMarkerAutoConfiguration. Class)
/ / will get to exclude = FreeMarkerAutoConfiguration. The class of annotation data
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// Check the configuration classes to be excluded, as some are not auto-configuration classes, so throw an exception
checkExcludedClasses(configurations, exclusions);
// [2] Remove the configuration class to be excluded
configurations.removeAll(exclusions);
// [3] Because there are so many auto-configuration classes from the spring.factories file, if some unnecessary auto-configuration classes are loaded into memory, it will cause memory waste
// Note that there will be calls AutoConfigurationImportFilter match method to determine compliance with @ ConditionalOnBean, @ ConditionalOnClass or @ ConditionalOnWebApplication, We'll focus on that later
configurations = filter(configurations, autoConfigurationMetadata);
/ / [4] to obtain the qualified automatic configuration after class, trigger AutoConfigurationImportEvent events at this time,
/ / the purpose is to tell ConditionEvaluationReport condition evaluation report, to record the eligible automatic configuration object class
// When will the event be triggered? -- > in a container called when invokeBeanFactoryPostProcessors rear trigger when the processor
fireAutoConfigurationImportEvents(configurations, exclusions);
// [5] Encapsulates qualified and excluded auto-configuration classes into an AutoConfigurationEntry object and returns it
return new AutoConfigurationEntry(configurations, exclusions);
}
Copy the code
The main thing that the AutoConfigurationEntry method does is to get the auto-configuration classes that meet the criteria and avoid wasting memory by loading unnecessary auto-configuration classes. Here’s a summary of what the AutoConfigurationEntry method does:
[1] Load the EnableAutoConfiguration class from the Spring. factories configuration file (note that it is in the cache at this point) and the resulting autoconfiguration class is shown in Figure 3. Here we just need to know what the method does, and there will be another article detailing the principle of Spring.Factories;
[2] If @enableAutoConfiguration is marked with an autoconfiguration class to exclude, exclude this autoconfiguration class.
[3] After the automatic configuration classes to exclude are excluded, the filter method is called for further filtering, and some automatic configuration classes that do not meet the conditions are excluded again. More on this later.
After heavy filtering [4], and the extra trigger AutoConfigurationImportEvent event, tell ConditionEvaluationReport condition assessment object, to record the eligible automatic configuration classes; (the detailed analysis in this section 6 AutoConfigurationImportListener.)
[5] Finally, the automatic configuration classes that meet the conditions are returned.
AutoConfigurationEntry methods are summarized after the main logic, we again to scrutinize the AutoConfigurationImportSelector filter method:
// AutoConfigurationImportSelector.java
private List<String> filter(List
configurations, AutoConfigurationMetadata autoConfigurationMetadata)
{
long startTime = System.nanoTime();
// Roll out an array of strings from the auto-configuration classes obtained from spring.factories
String[] candidates = StringUtils.toStringArray(configurations);
// Define the skip array, whether to skip. Note the order of the SKIP array and the candidates array
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
/ / getAutoConfigurationImportFilters method: to obtain a OnBeanCondition OnClassCondition and OnWebApplicationCondition
// Then iterate through the three conditional classes to filter the large number of configuration classes loaded from Spring.factories
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
/ / call aware methods, will beanClassLoader, such as the beanFactory injected into the filter object,
/ / the filter object namely OnBeanCondition here, OnClassCondition or OnWebApplicationCondition
invokeAwareMethods(filter);
// Check the various filters for each candidate
/ / @ ConditionalOnClass, @ ConditionalOnBean and @ ConditionalOnWebApplication annotations) matches,
// Note that the candidates array corresponds to the match array
/ * * * * * * * * * * * * * * * * * * * * * * ", focus on "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
// Walk through the match array and note that the match order corresponds to the automatic configuration class of the candidates
for (int i = 0; i < match.length; i++) {
// If there is a mismatch
if(! match[i]) {// Mismatches will be recorded in the SKIP array, marking skip[I] as true and corresponding to the candidates' array
skip[i] = true;
// Empty the corresponding auto-configuration class because of a mismatch
candidates[i] = null;
To be skipped or deflected
skipped = true; }}}/ / here said if all automatic configuration class after OnBeanCondition, OnClassCondition and OnWebApplicationCondition filtered, all match, all the same to return
if(! skipped) {return configurations;
}
// Create the result set to hold the matching autoconfiguration class
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
// If skip[I] is false, it is an automatic configuration class that meets the conditions and is added to the result set
if (!skip[i]) {
result.add(candidates[i]);
}
}
// Prints logs
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
+ " ms");
}
// Finally return the auto-configuration class that meets the criteria
return new ArrayList<>(result);
}
Copy the code
AutoConfigurationImportSelector filter method mainly do is call interface AutoConfigurationImportFilter match method to determine each the conditions of the automatic configuration class comments (if any) @ Conditi OnalOnClass, @ ConditionalOnBean or @ ConditionalOnWebApplication whether meet the conditions, if met, it returns true, specification matching, if not satisfied, return false description does not match.
We now know what did the AutoConfigurationImportSelector filter method is mainly to go, now don’t have to study too deep, As for AutoConfigurationImportFilter interface match method will analyze it in detail in this section 5 AutoConfigurationImportFilter, fill our notes left in the source analysis the condition of the previous hole.
Note: we adhere to the principle of main line first, other branches of the code here do not delve into, so as not to lose the main line ha.
4.2 Importing automatic configuration Classes selectively
Here we continue to explore how the this.group.selectImports method in the previous section [2] selectively imports the automatic configuration class. Look directly at the code:
// AutoConfigurationImportSelector$AutoConfigurationGroup.java
public Iterable<Entry> selectImports(a) {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
// Here is a set of all the auto-configuration classes to exclude
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions)
.flatMap(Collection::stream).collect(Collectors.toSet());
// Here is a set of all the auto-configuration classes that meet the criteria after filtering
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations)
.flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
// Remove the auto-configuration class to exclude
processedConfigurations.removeAll(allExclusions);
// Sort the autoconfiguration classes annotated with @order,
return sortAutoConfigurations(processedConfigurations,
getAutoConfigurationMetadata())
.stream()
.map((importClassName) -> new Entry(
this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
Copy the code
As you can see, selectImports method mainly for after eliminated the exclude and be filtered AutoConfigurationImportFilter interface meet the conditions of the automatic configuration class rule out further exclude the automatic configuration of classes, and then sort. The logic is simple and I won’t go into detail.
I’ve already excluded you once, so why exclude you again?
5 AutoConfigurationImportFilter
Continue to delve into the front section 4.1 AutoConfigurationImportSelector here. The filter method of automatic filter configuration class of Boolean [] match = filter. The match (candidates, autoConfigurationMetadata); This code.
So we continue to analyze AutoConfigurationImportFilter interface, analyzes the match method, is also a source analysis of the previous @ ConditionalOnXxx left in the hole to fill.
AutoConfigurationImportFilter interface is only one match method is used to filter is not in conformity with the conditions of the automatic configuration classes.
@FunctionalInterface
public interface AutoConfigurationImportFilter {
boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata);
}
Copy the code
Also, based on the analysis of AutoConfigurationImportFilter interface before the match method, we first look at the class diagram:
As you can see, AutoConfigurationImportFilter interface have a concrete implementation class FilteringSpringBootCondition, FilteringSpringBootCondition have three specific subclass: OnClassCondition OnBeanCondtition and OnWebApplicationCondition.
So what’s the relationship between these classes?
FilteringSpringBootCondition implements the interface AutoConfigurationImportFilter match method, Then in FilteringSpringBootCondition match method call getOutcomes this abstract template matching method returns the automatic configuration class or not. At the same time, The most important thing is the three subclasses OnClassCondition FilteringSpringBootCondition, OnBeanCondtition and OnWebApplicationCondition will copy the template matching method realizes own judgment logic.
Ok, AutoConfigurationImportFilter interface overall relationship has been clear, now we enter the specific implementation class FilteringSpringBootCondition match method to see how is the filter automatically configure class according to conditions.
// FilteringSpringBootCondition.java
@Override
public boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
// Create an evaluation report
ConditionEvaluationReport report = ConditionEvaluationReport
.find(this.beanFactory);
// Note that getOutcomes is the template method that passes in all the auto-configuration classes loaded in the Spring.factories file
/ / subclass (here refers to OnClassCondition, OnBeanCondition and OnWebApplicationCondition class) to filter
// Note that the outcomes array stores mismatched results, corresponding to the autoConfigurationClasses array
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ", focus on "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
// iterate over outcomes, where null equals a match and non-null equals a mismatch
for (int i = 0; i < outcomes.length; i++) {
ConditionOutcome outcome = outcomes[i];
match[i] = (outcome == null || outcome.isMatch());
if(! match[i] && outcomes[i] ! =null) {
// If one of the classes does not match, the parent class SpringBootCondition's logOutcome method is called to print the log
logOutcome(autoConfigurationClasses[i], outcomes[i]);
// Record the mismatches in the report
if(report ! =null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]); }}}return match;
}
Copy the code
FilteringSpringBootCondition methods do match or getOutcomes call abstract template method according to the condition to filter configuration class automatically, and facsimile getOutcomes template method has three children, here no longer one by one analysis, Only the getOutcomes method of the OnClassCondition copy is selected for analysis.
5.1 OnClassCondition
The code for the getOutcomes method is copied directly on OnClassCondition:
// OnClassCondition.java
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
// Split the work and perform half in a background thread. Using a single
// additional thread seems to offer the best performance. More threads make
// things worse
// If the thread is larger than two, the performance will be worse
int split = autoConfigurationClasses.length / 2;
// [1] Start a new thread to scan for half of the loaded autoconfiguration classes
OutcomesResolver firstHalfResolver = createOutcomesResolver(
autoConfigurationClasses, 0, split, autoConfigurationMetadata);
// [2] Here we use the main thread to scan for half of the loaded autoconfiguration classes
OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(
autoConfigurationClasses, split, autoConfigurationClasses.length,
autoConfigurationMetadata, getBeanClassLoader());
// [3] automatically configure whether the class matches the condition
ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
// [4] The other half of the auto-configuration class is resolved with the newly opened thread fetching
// Note that to prevent the main thread from terminating too quickly, resolveOutcomes calls thread.join()
// Let the main thread wait for the new thread to finish, since the results of the two threads will be merged later
ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
// Create a ConditionOutcome array to store the filter results of the auto-configuration class
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
// Copy the filtering results of the first two threads into the outcomes array, respectively
System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length);
System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length);
// Returns the filter result of the auto-configuration class
return outcomes;
}
Copy the code
As you can see, the getOutcomes method of OnClassCondition resolves whether the auto-configured class matches the condition specified by @conditionAlonClass in the auto-configured class and returns a match if it does. If it does not exist, a mismatch is returned.
First configuration resolver and secondHalfResolver are created by first thalfresolver and secondHalfResolver respectively. Each of these two resolvers corresponds to a thread to see if the loaded autoconfiguration class meets the condition. Finally, the matching results of the parsing auto-configuration classes of the two threads are merged and returned.
What about the parsing process to determine whether an auto-configuration class meets the criteria? Now let’s look at the points [1], [2], [3] and [4] indicated in the code comments above.
5.1.1 createOutcomesResolver
# # # # # # # # # # # # # # # # # # # # # # # # # # # ; Method:
// OnClassCondition.java
private OutcomesResolver createOutcomesResolver(String[] autoConfigurationClasses,
int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
// Create a StandardOutcomesResolver object
OutcomesResolver outcomesResolver = new StandardOutcomesResolver(
autoConfigurationClasses, start, end, autoConfigurationMetadata,
getBeanClassLoader());
try {
// New a ThreadedOutcomesResolver object and pass in an outcomesResolver object of type StandardOutcomesResolver as the constructor argument
return new ThreadedOutcomesResolver(outcomesResolver);
}
// Return the StandardOutcomesResolver object if the thread opened above throws an AccessControlException
catch (AccessControlException ex) {
returnoutcomesResolver; }}Copy the code
You can see that the createOutcomesResolver method creates a ThreadedOutcomesResolver resolution object that encapsulates the StandardOutcomesResolver class. So let’s take a look at ThreadedOutcomesResolver what’s the purpose of this thread resolution class that encapsulates StandardOutcomesResolver? We continue to follow the code:
// OnClassCondtion.java
private ThreadedOutcomesResolver(OutcomesResolver outcomesResolver) {
// start a new thread using the resolveOutcomes method of StandardOutcomesResolver
// Parse the auto-configuration classes to see if they match
this.thread = new Thread(
() -> this.outcomes = outcomesResolver.resolveOutcomes());
// Start the thread
this.thread.start();
}
Copy the code
As you can see, when constructing the ThreadedOutcomesResolver object, you start a thread that actually calls the resolveOutcomes method of the StandardOutcomesResolver object that was just passed in to resolve the auto-configuration class. How to parse it? Later we on the analysis of [3] code secondHalfResolver. ResolveOutcomes (); I’ll dig deeper.
5.1.2 new StandardOutcomesResolver
OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(… ; The StandardOutcomesResolver object is created to parse the auto-configuration class and is used by a new thread to parse the auto-configuration class.
5.1.3 StandardOutcomesResolver resolveOutcomes method
Here in front of the corresponding code section 5.1 of [3] ConditionOutcome [] secondHalf = secondHalfResolver. ResolveOutcomes (); .
Here StandardOutcomesResolver. ResolveOutcomes method for parsing automatic matching all logical configuration, that we should focus on the analysis of the method, The resolveOutcomes method ultimately assigns the results of the parsed auto-configuration class to the secondHalf array. So how does it resolve whether an auto-configuration class matches a condition?
// OnClassCondition$StandardOutcomesResolver.java
public ConditionOutcome[] resolveOutcomes() {
// Call the getOutcomes method to resolve
return getOutcomes(this.autoConfigurationClasses, this.start, this.end,
this.autoConfigurationMetadata);
}
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { / / as long as autoConfigurationMetadata no store related configuration class automatically, then the outcome the default is null, then match
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
// Walk through each auto-configuration class
for (int i = start; i < end; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
/ / TODO for autoConfigurationMetadata have a doubt: Why some kinds of automatic configuration conditions annotations that can be loaded into the autoConfigurationMetadata, and some can't, Such as their definition of an automatic configuration class HelloWorldEnableAutoConfiguration it hadn't been put into the autoConfigurationMetadata
if(autoConfigurationClass ! =null) {
ConditionalOnClass = @conditionalonClass // ConditionalOnClass = @conditionalonClass
/ / for chestnuts, see the following KafkaStreamsAnnotationDrivenConfiguration this automatic configuration class
/ * * *@ConditionalOnClass(StreamsBuilder. Class) * class KafkaStreamsAnnotationDrivenConfiguration} {* / / omit irrelevant code * * /
/ / then remove are StreamsBuilder fully qualified name of a class of candidates = org. Apache. Kafka. Streams. StreamsBuilder
String candidates = autoConfigurationMetadata
.get(autoConfigurationClass, "ConditionalOnClass"); ConditionalOnClass is ConditionalOnClass. ConditionalOnClass is ConditionalOnClass
// If the auto-configuration class is ConditionalOnClass and has a value, getOutcome is called to determine whether it is in the classpath
if(candidates ! =null) {
ConditionalOnClass () returns null if the class is in the classpath. ConditionalOnClass () returns null if the class is in the classpath
/******************* [mainline, focus] ******************/outcomes[i - start] = getOutcome(candidates); }}}return outcomes;
}
Copy the code
Can see StandardOutcomesResolver. ResolveOutcomes method call getOutcomes method again, Mainly from autoConfigurationMetadata annotations on the object gets to automatically configure class @ ConditionalOnClass designated as the fully qualified class name, and then passed as a parameter to getOutcome method is used for loading in the class to go to the class path, ConditionalOnClass (@conditionalonClass) ConditionalOnClass (ConditionalOnClass)
But remember, this is just past the @conditionalonClass annotation, and if the auto-configuration class has another annotation like @conditionalonBean, then the @conditionalonbean annotation will not match. This is a little bit of a digression, but let’s go back to the logic of OnClassCondtion, and go on to the getOutcome method and see how it determines whether @conditionalonclass is full or not.
// OnClassCondition$StandardOutcomesResolver.java
// The outcome returned records mismatches. If the outcome is not null, it indicates a mismatch. If the value is null, it indicates a match
private ConditionOutcome getOutcome(String candidates) {
// Candidates for the form of "org. Springframework. Boot. Autoconfigure. Aop. AopAutoConfiguration. ConditionalOnClass = org. Aspectj. Lang. Annot Ation. The Aspect, org. Aspectj. Lang. Reflect. Advice,. Org. Aspectj weaver. AnnotatedElement"
try {// If the @conditionalonClass value is only one, the getOutcome method is called to determine the match
if(! candidates.contains(",")) {
ClassNameFilter.MISSING: ClassNameFilter.MISSING: ClassNameFilter.MISSING: ClassNameFilter
/****************** [mainline, focus] ********************/
return getOutcome(candidates, ClassNameFilter.MISSING,
this.beanClassLoader);
}
ConditionalOnClass = @conditionalonClass = @conditionalonClass = @conditionalonClass = @conditionalonClass
for (String candidate : StringUtils
.commaDelimitedListToStringArray(candidates)) {
ConditionOutcome outcome = getOutcome(candidate,
ClassNameFilter.MISSING, this.beanClassLoader);
// If there is only one mismatch, the result will be returned
if(outcome ! =null) {
returnoutcome; }}}catch (Exception ex) {
// We'll get another chance later
}
return null;
}
Copy the code
As you can see, the getOutcome method calls the overloaded getOutcome method to further determine whether the class specified by @conditionalonClass exists in the classpath.
// OnClassCondition$StandardOutcomesResolver.java
private ConditionOutcome getOutcome(String className, ClassNameFilter classNameFilter, ClassLoader classLoader) {
// Call the matches method of classNameFilter to check whether the class specified by @conditionalonClass exists in the classpath
/****************** [mainline, focus] ********************/
if (classNameFilter.matches(className, classLoader)) { // Call classNameFilter to check whether className exists in the class path. ClassNameFilter can be divided into PRESENT and MISSING. At present, we only see the calls where ClassNameFilter is MISSING, so if the default is true, the mismatch information will be recorded. If you pass ClassNameFilter PRESENT, you probably have to write an else branch
return ConditionOutcome.noMatch(ConditionMessage
.forCondition(ConditionalOnClass.class)
.didNotFind("required class").items(Style.QUOTE, className));
}
return null;
}
Copy the code
We peel layer by layer, finally peel to the bottom, this really need enough patience, no way, the source can only bit by bit gnaw, hey hey. The matches method of ClassNameFilter is called to check whether @conditionalonClass exists in the classpath. If it does not, it returns a mismatch.
We continue to follow the source of ClassNameFilter:
// FilteringSpringBootCondition.java
protected enum ClassNameFilter {
// If the specified class exists on the classpath, return true
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
returnisPresent(className, classLoader); }},// If the specified class does not exist in the classpath, return true
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return! isPresent(className, classLoader);Return true if className does not exist in the classpath}};// This is another abstract method, implemented by the PRESENT and MISSING enumeration classes, respectively
public abstract boolean matches(String className, ClassLoader classLoader);
// Check whether the specified class exists in the classpath
public static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
// Use the class loader to load the corresponding class. If no exception is thrown, the class exists in the classpath. In this case, return true
try {
forName(className, classLoader);
return true;
}// If it does not exist in the classpath, the exception will be caught and false will be returned.
catch (Throwable ex) {
return false; }}// Use the class loader to load the specified class
private staticClass<? > forName(String className, ClassLoader classLoader)throws ClassNotFoundException {
if(classLoader ! =null) {
return classLoader.loadClass(className);
}
returnClass.forName(className); }}Copy the code
Can see ClassNameFilter turned out to be FilteringSpringBootCondition an internal enumeration class, the realization of the specified class exists in the logic of the classpath, this class is very simple, here no longer expatiatory.
5.1.4 ensuring ThreadedOutcomesResolver resolveOutcomes method
Here corresponds to the front of section 5.1 of [4] code ConditionOutcome [] firstHalf. = firstHalfResolver resolveOutcomes ().
Previous analysis 5.1.3 StandardOutcomesResolver. ResolveOutcomes methods have been explored in bottom, deeper into details, Now we need to jump out to continue to see the front of [4] code ConditionOutcome [] firstHalf. = firstHalfResolver resolveOutcomes () method.
Here is to use the new open a thread to call StandardOutcomesResolver. ResolveOutcomes method resolution half automatic configuration class matches, because is a new thread, there is such a situation may arise: After the main thread has parsed its own half of the auto-configuration class, it continues running for so long that it does not wait for a new child thread to open.
Therefore, in order for the main thread to finish parsing, we need to have the main thread continue to wait for the child thread being parsed until the child thread finishes. So we continue to follow up the code area. See ThreadedOutcomesResolver resolveOutcomes method is how to realize the main thread to wait for the child thread:
// OnClassCondition$ThreadedOutcomesResolver.java
public ConditionOutcome[] resolveOutcomes() {
try {
// Call the child thread's Join method and let the main thread wait
this.thread.join();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
// If the child thread ends, the parsing result of the child thread is returned
return this.outcomes;
}
Copy the code
As you can see, the thread.join () method is used to make the main Thread wait for the child Thread that is parsing the auto-configured class. CountDownLatch should also be used to make the main Thread wait for the child Thread to finish. Finally, the result of child thread parsing is assigned to the firstHalf array.
5.2 OnBeanCondition and OnWebApplicationCondition
In the previous section 5.1 OnClassCondition, we analyzed in depth how OnClassCondition filters the auto-configuration classes, so the auto-configuration classes need to be filtered by OnClassCondition. Pass OnBeanCondition and OnWebApplicationCondition conditions of the two classes of filtering, here no longer expatiatory, interested friends can analysis.
6 AutoConfigurationImportListener
Here we continue with the previous section 4.1 AutoConfigurationImportSelector. GetAutoConfigurationEntry method automatic configuration class filter after the event to trigger fireAutoConfigurationImportEvents (configuratio ns, exclusions); This code.
We look at how the trigger point directly into the fireAutoConfigurationImportEvents method of events:
// AutoConfigurationImportSelector.java
private void fireAutoConfigurationImportEvents(List
configurations, Set
exclusions)
{
/ / in the spring. The factories always get to AutoConfigurationImportListener ConditionEvaluationReportAutoConfigurationImportListener namely
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if(! listeners.isEmpty()) {/ / create a new AutoConfigurationImportEvent events
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
configurations, exclusions);
/ / traverse AutoConfigurationImportListener just to get to
for (AutoConfigurationImportListener listener : listeners) {
// Various Aware methods are called here to assign values before triggering events, such as factory,environment, etc
invokeAwareMethods(listener);
/ / real trigger AutoConfigurationImportEvent onXXXEveent callback listener method. This is used to record the evaluation information for the auto-configuration classlistener.onAutoConfigurationImportEvent(event); }}}Copy the code
As above, fireAutoConfigurationImportEvents method to do the following two things:
- call
getAutoConfigurationImportListeners
Methods fromspring.factoris
Configuration file acquisition implementationAutoConfigurationImportListener
Event listeners for interfaces; As you can see in the figure below, getConditionEvaluationReportAutoConfigurationImportListener
:
- Iterate through the acquired event listeners, and then call the listeners
Aware
Method, which in turn calls back to the event listener’sonAutoConfigurationImportEvent
Method to perform the logic to listen for events.
At this time we’ll look at ConditionEvaluationReportAutoConfigurationImportListener listener listens to the incident, what I’d done it onAutoConfigurationImportEvent method:
// ConditionEvaluationReportAutoConfigurationImportListener.java
public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
if (this.beanFactory ! =null) {
// Get the condition evaluation reporter object
ConditionEvaluationReport report = ConditionEvaluationReport
.get(this.beanFactory);
// Add the qualified auto-configuration classes to the unconditionalClasses collection
report.recordEvaluationCandidates(event.getCandidateConfigurations());
// Record the automatic configuration class of exclude into the Exclusions collectionreport.recordExclusions(event.getExclusions()); }}Copy the code
As you can see, ConditionEvaluationReportAutoConfigurationImportListener listener after listening to the event, do is very simple, just record the qualified and exclude automatic configuration class.
7 AutoConfigurationPackages
AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage: SpringBoot AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
Copy the code
As you can see, the @autoconfigurationpackage annotation is associated with the package where SpringBoot is automatically configured. The package to which the annotation was added is managed as an AutoConfigurationPackage.
Next we’ll look at AutoConfigurationPackages. The Registrar class do, see the source code directly:
//AutoConfigurationPackages.Registrar.java
static class Registrar implements ImportBeanDefinitionRegistrar.DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(newPackageImport(metadata)); }}Copy the code
You can see the Registrar class is a static inner class of AutoConfigurationPackages, implements the ImportBeanDefinitionRegistrar and DeterminableImports two interfaces. Now let’s focus on the Registrar implementation’s registerBeanDefinitions method, which as the name suggests is the way to registerBeanDefinitions. See it again call AutoConfigurationPackages register method, continue to follow up the source code:
// AutoConfigurationPackages.java
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition
.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0,
addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(BEAN, beanDefinition); }}Copy the code
As you can see above, the Register method registers a packageNames bean associated with the package name where the auto-configuration class annotation @enableAutoConfiguration resides. So what is the purpose of registering this bean? The purpose of registering the beans associated with the autoconfiguration package name is to be referenced in other places, such as the JPA Entity Scanner.
8 subtotal
Well, SpringBoot automatic configuration of the source code analysis here, relatively long, some places are also very in-depth details, reading requires a certain amount of patience.
Finally, we summarize the principle of SpringBoot automatic configuration, mainly do the following things:
- Load the auto-configuration classes from the Spring.Factories configuration file;
- The loaded autoconfiguration class is removed
@EnableAutoConfiguration
annotationsexclude
Property to specify the auto-configuration class; - And then use
AutoConfigurationImportFilter
Does the interface filter auto-configuration class conform to its annotations (if any)@ConditionalOnClass
.@ConditionalOnBean
and@ConditionalOnWebApplication
If all the conditions are met, the matching results will be returned; - Then the trigger
AutoConfigurationImportEvent
Event, tellConditionEvaluationReport
The condition evaluation reporter object to record the conditions and respectivelyexclude
Automatic configuration class of. - Finally, Spring imports the filtered auto-configuration classes into the IOC container
Finally leave a question of their own, but also hope to know the answer of the big guy, here to express thanks:
In order to avoid unnecessary automatic loading configuration class cause waste of memory, FilteringSpringBootCondition filters spring. The factories of the automatic configuration file class, And why only OnOnBeanCondition FilteringSpringBootCondition OnClassCondition and onWebApplicationCondition classes are used to filter the three conditions, Why is there no onPropertyCondtion, onResourceCondition conditional class to filter the auto-configuration class?
Next: What is the startup process of SpringBoot? –SpringBoot source code (5)
Original is not easy, help point a praise!
Due to the author’s limited level, if there are mistakes in the article, please point out, thank you.
Reference:
1, @autoConfigurationPackage annotation