@[toc] Although we use Spring Boot a lot in our daily development, it is considered a standard configuration in the field of Java development, but friends, think about your interview experience carefully, what are the interview questions related to Spring Boot? Personally, I feel that there should be relatively few. Spring Boot is essentially the same as SSM, but the configuration is simplified through various starters. The others are exactly the same, so many interview questions in Spring Boot still have to return to Spring to answer ! Of course, this is not to say that there is nothing to ask in Spring Boot. There is actually a very classic interview question in Spring Boot, that is, how is automatic configuration in Spring Boot implemented? Brother Song is here today to chat with you guys about this issue.
In fact, Brother Song talked about related issues with his friends before, but they were all scattered and not systematically sorted out. He also led his friends to customize a starter before. I believe that all of you have a certain understanding of the principle of the starter, so I won’t go into too much detail in today’s article, you can read the previous article.
1. @SpringBootApplication
To talk about the automatic configuration of Spring Boot, we must @SpringBootApplication
start . This is the starting point of the entire Spring Boot universe. Let's look at this annotation first:
@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 {
}
As you can see, @SpringBootApplication
annotations combine the functions of multiple common annotations, among which:
- The first four are meta-annotations, which we won't discuss here.
- The fifth
@SpringBootConfiguration
is an annotation that supports configuration classes, which we will not discuss here. @EnableAutoConfiguration
The sixth annotation indicates that automatic configuration is turned on, which is the focus of our talk today.- The seventh
@ComponentScan
is a package scanning comment, why will be automatically scanned as long asSpring Boot
it is placed in the right position in the project , it is related to this comment.Bean
Don't look at the many annotations here. In fact, there are only two annotations Spring Boot
provided , namely and@SpringBootConfiguration
. The other annotations have existed for many years before the appearance of .@EnableAutoConfiguration
Spring Boot
2. @EnableAutoConfiguration
Next, let's take a look @EnableAutoConfiguration
at how to implement automatic configuration.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
This annotation plays a key role in two things:
@AutoConfigurationPackage
: This means to automatically scan various third-party annotations. In the previous article, Brother Song has already talked with you about the function of this annotation. Portal: What is the difference between @AutoConfigurationPackage and @ComponentScan?@Import
It is importingAutoConfigurationImportSelector
the configuration class, which is used to load various automatic configuration classes.
3. AutoConfigurationImportSelector
AutoConfigurationImportSelector
There are many methods in the class, and the entry point is the process method, so we will start from the process method here:
@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()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
As can be seen from the class name, objects related to automatic configuration are AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
loaded .
Of course, getAutoConfigurationEntry
the method is actually the method provided by the current class, let's take a look at the method:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<string> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<string> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
The method naming of the source code here is well done, basically you can see the name and know the meaning, and you should follow this naming idea in your daily development. Next, let's take a look at the key methods here one by one.
3.1 isEnabled
First call the isEnabled method to determine whether the automatic configuration is enabled. This is mainly because after we introduced spring-boot-starter-xxx in the project in time, we can also disable all automatic configurations spring.boot.enableautoconfiguration=false
by
The relevant source code is as follows:
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
3.2 getCandidateConfigurations
Next, call getCandidateConfigurations
the method to obtain all candidate automation configuration classes. These candidate automation configuration classes mainly come from two places:
starter
Song Ge talked with you in the previous custom , we need toclaspath\:META-INF/spring.factories
define all the automatic configuration classes in , this is the first source.Spring Boot
The built-in automatic configuration class, this has been talked about many times with my friends in the previous vhr video, and theSpring Boot
built-in automatic configuration class is located inspring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
the file .
The relevant source code is as follows:
protected List<string> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<string> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
The full path of the automatic configuration class loaded here is stored in configurations
the object , which has two places to obtain:
- Call
SpringFactoriesLoader.loadFactoryNames
the method to get it. I will not show you the details of this method. It is relatively simple. In essence, it is to loadMETA-INF/spring.factories
the file. This file defines the full path of a large number of automatic configuration classes. - Call
ImportCandidates.load
the method to load, this is to loadspring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
the automation configuration class in the file.
If neither place is loaded into any automation configuration class, then an exception will be thrown.
3.3 removeDuplicates
removeDuplicates
The method means to remove the duplicated classes in the candidate automation configuration class. The idea of removal is also very interesting, just use a LinkedHashSet
transfer . The source code is as follows:
protected final <t> List<t> removeDuplicates(List<t> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
It can be seen that sometimes some solutions in these source codes are also very interesting.
3.4 getExclusions
getExclusions
The method indicates that all excluded automation configuration classes need to be obtained. These excluded automation configuration classes can be obtained from three places:
exclude
The property of the current annotation .excludeName
The property of the current annotation .application.properties
spring.autoconfigure.exclude
property in the configuration file .
Take a look at the relevant source code:
protected Set<string> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<string> excluded = new LinkedHashSet<>();
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(asList(attributes, "excludeName"));
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
It corresponds to the three points explained above.
3.5 checkExcludedClasses
This method is to check all the excluded automation configuration classes. Since the automation configuration classes Spring Boot
in can be customized, it is not necessary to uniformly implement a certain interface or uniformly inherit a certain class, so when writing an exclusion class, if the compilation is wrong It cannot be verified, like the following:
@SpringBootApplication(exclude = HelloController.class)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Since HelloController
is not an automatic configuration class, an error will be reported when the project is started, as follows:
Where does this exception come from? In fact, it comes from checkExcludedClasses
the method , let's take a look at the method:
private void checkExcludedClasses(List<string> configurations, Set<string> exclusions) {
List<string> invalidExcludes = new ArrayList<>(exclusions.size());
for (String exclusion : exclusions) {
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
protected void handleInvalidExcludes(List<string> invalidExcludes) {
StringBuilder message = new StringBuilder();
for (String exclude : invalidExcludes) {
message.append("\t- ").append(exclude).append(String.format("%n"));
}
throw new IllegalStateException(String.format(
"The following classes could not be excluded because they are not auto-configuration classes:%n%s",
message));
}
It can be seen that in checkExcludedClasses
the method , configurations
all the excluded automation configuration classes that are located in the current classpath but not included in will be found first. Since configurations
there are all automation configuration classes in , these do not exist configurations
in Classes are all problematic, and they are not automatic configuration classes. Collect these problematic classes, store them in invalidExcludes
variables , and then perform additional processing.
The so-called extra processing is to throw an exception in handleInvalidExcludes
the method , which is where the exception in the previous screenshot comes from.
3.6 removeAll
This method is just one task, which is to remove those excluded automation configuration classes from configurations
it . configurations
It is List
a collection , exclusions
and it is a Set
collection , so it can be removed directly here.
3.7 filter
Now we have loaded all the automatic configuration classes, but not all of these configuration classes will take effect. Whether they will take effect or not depends on whether your project uses specific dependencies.
For example, the automatic configuration loaded now contains RedisAutoConfiguration, which automatically configures Redis, but since Redis is not used in my project, this automatic configuration class will not take effect. This process is done getConfigurationClassFilter().filter(configurations);
by .
Let me talk about a preliminary knowledge first:
Since there are so many automated configuration classes in our project, each automated configuration class will depend on other classes. When other classes exist, this automated configuration class will take effect. This bunch of dependencies between each other exists in spring-boot-autoconfigure-3.0.6.jar!/META-INF/spring-autoconfigure-metadata.properties
the file Among them, I randomly cite a configuration in this file:
org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.annotation.EnableRabbit
Indicates that a prerequisite for the RabbitAnnotationDrivenConfiguration class to take effect is that it exists in the current project classpathorg.springframework.amqp.rabbit.annotation.EnableRabbit
.
Let's take a look at the annotations of the RabbitAnnotationDrivenConfiguration class:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(EnableRabbit.class)
class RabbitAnnotationDrivenConfiguration {
}
This class is consistent with the content in the configuration file.
If you understand this preliminary knowledge, the next content will be easy to understand.
First look at the getConfigurationClassFilter method, which is to get all the filters, as follows:
private ConfigurationClassFilter getConfigurationClassFilter() {
if (this.configurationClassFilter == null) {
List<autoconfigurationimportfilter> filters = getAutoConfigurationImportFilters();
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
It can be seen that the filters obtained here are all of the AutoConfigurationImportFilter type, and there are only three instances of this type of filter, as shown in the following figure:
From the names of these three instances, you can basically see their respective functions:
- OnClassCondition: This is the judgment condition
@ConditionalOnClass
of . It is used to judge whether a certain class exists under the current classpath by looking at the name. - OnWebApplicationCondition: This is the judgment condition
ConditionalOnWebApplication
of , which is used to judge whether the current system environment is a Web environment. - OnBeanCondition: This is the judgment condition
@ConditionalOnBean
of , which is to judge whether a Bean exists in the current system.
The three AutoConfigurationImportFilter filters obtained here are actually the above three. Next, execute the filter method, as follows:
List<string> filter(List<string> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<string> result = new ArrayList<>(candidates.length);
for (String candidate : candidates) {
if (candidate != null) {
result.add(candidate);
}
}
return result;
}
Here is to traverse these three filters, and then call their respective match methods to match with 144 automatic configuration classes. If the conditions required by these automatic configuration classes are met, the corresponding position of the match array will be true, otherwise it will be false.
Then traverse the match array, set the automatic configuration classes that do not meet the conditions to null, and finally remove these nulls.
In this way, the classes we need to automate configuration are obtained.
The last sentence fireAutoConfigurationImportEvents triggers the automatic configuration class import event, which is nothing to say~
After these automatic configuration classes are loaded, the next step is various conditional annotations to determine whether these configuration classes are effective. These are relatively simple. I have talked to my friends many times in vhr before, so I won’t stop here Long winded~
Well, after the above combing, I believe that the friends have a general understanding of the loading of Spring Boot automatic configuration classes~</string></string></string></autoconfigurationimportfilter></string></ string></string></string></string></string></t></t></t></string></string></string></string>