Although we use Spring Boot a lot in daily development, and it is considered a standard in the current Java development field, but friends, think carefully about your interview experience, what are the interview questions related to Spring Boot? Personally, there should be relatively few. Spring Boot is essentially the same as SSM. It just simplifies the configuration through various starters. Everything else is exactly the same, so many interview questions in Spring Boot still need to be answered in Spring. ! Of course, this does not mean that there is nothing to ask in Spring Boot. There is actually a very classic interview question in Spring Boot, that is, how is the automated configuration in Spring Boot implemented? Today Brother Song is here to talk to all of you about this issue.
In fact, Brother Song has talked about related issues with his friends before, but they were all scattered and not systematically sorted out. He also led the friends to customize a starter before. I believe that all of you have a certain understanding of the principles of starters, so I won’t go into too much detail in today’s article. You can read the previous articles.
Contents
1. @SpringBootApplication
To talk about the automated configuration of Spring Boot, we must start with the startup class of the project @SpringBootApplication
. This is the starting point of the entire Spring Boot universe. Let’s first look at 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 { }
As you can see, @SpringBootApplication
annotations combine the functions of multiple common annotations, including:
- The first four are meta-annotations, which we will not discuss here.
- The fifth
@SpringBootConfiguration
is an annotation that supports configuration classes, which we will not discuss here. - The sixth
@EnableAutoConfiguration
annotation means turning on automated configuration, which is the focus of our discussion today. - The seventh one
@ComponentScan
is a package scanning annotation. Why everythingSpring Boot
in the projectBean
will be automatically scanned as long as it is placed in the right position is related to this annotation.
Don’t look at the many annotations here. In fact, Spring Boot
there are only two annotations provided by , namely @SpringBootConfiguration
and @EnableAutoConfiguration
. The other annotations Spring Boot
have existed for many years before the appearance of .
2. @EnableAutoConfiguration
Next, let’s take a look @EnableAutoConfiguration
at how to implement automated 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 automatically scanning various third-party annotations. In the previous article, Brother Song has already talked to everyone about the role 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 automated configuration classes.
3. AutoConfigurationImportSelector
AutoConfigurationImportSelector
There are many methods in the class, and the entry point is the process method, so we will start with 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 automated configuration are AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
loaded by.
Of course, the method here getAutoConfigurationEntry
is actually the method provided by the current class. Let’s take a look at this 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 in the source code here is done well, and basically everyone can understand the meaning after seeing the name. Friends should follow this naming idea in their 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 automated configuration is enabled. This is mainly because after we introduced spring-boot-starter-xxx in the project in time, we can also spring.boot.enableautoconfiguration=false
turn off all automated configuration by configuring in application.properties.
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 automated configuration classes. These candidate automated configuration classes mainly come from two places:
- In the previous customization
starter
Zhongsong brother talked to everyone, we need toclaspath\:META-INF/spring.factories
define all the automated configuration classes in , this is the first source. Spring Boot
The built-in automated configuration class has been talked about many times with friends in previous vhr videos.Spring Boot
The built-in automated configuration class is locatedspring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
in 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 automated 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. It is essentially to loadMETA-INF/spring.factories
the file. This file defines the full paths of a large number of automated configuration classes. - Call
ImportCandidates.load
the method to load, which is to loadspring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
the automated configuration class in the file.
If no automated configuration classes are loaded in either place, an exception will be thrown.
3.3 removeDuplicates
removeDuplicates
The method means to remove duplicate classes in the candidate automation configuration classes. 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)); }
You can see that sometimes some solution ideas in these source codes are also very interesting.
3.4 getExclusions
getExclusions
The method indicates that all excluded automated configuration classes need to be obtained. These excluded automated configuration classes can be obtained from three places:
- Properties of the current annotation
exclude
. - Properties of the current annotation
excludeName
. application.properties
spring.autoconfigure.exclude
properties in the configuration file .
Let’s 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 exactly to the three points explained above.
3.5 checkExcludedClasses
This method is to check all excluded automated configuration classes. Since Spring Boot
the automated configuration classes in can be customized, there is no need to uniformly implement a certain interface or uniformly inherit a certain class. Therefore, when writing excluded classes, if you write the wrong compile 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 automated configuration class, an error will be reported when the project is started, as follows:
Where does this anomaly 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 the method, all excluded automated configuration classes that checkExcludedClasses
are located on the current classpath but are not included in will be found first. Since those in are all automated configuration classes, these do not exist in All classes are problematic, and none of them are automatically configured. Collect these problematic classes, store them in variables, and then perform additional processing.configurations
configurations
configurations
invalidExcludes
The so-called additional processing is handleInvalidExcludes
to throw an exception in the method. The exception in the previous screenshot comes from here.
3.6 removeAll
This method has one task, which is to remove those excluded automated configuration classes configurations
from . It is a collection configurations
itself , so it can be removed directly here.List
exclusions
Set
3.7 filter
Now we have loaded all the automated configuration classes, but not all of these configuration classes will take effect. Whether they will take effect depends on whether your project uses specific dependencies.
For example, the automated configuration loaded now contains RedisAutoConfiguration, which automatically configures Redis. However, since Redis is not used in my project, this automated configuration class will not take effect. This process is completed getConfigurationClassFilter().filter(configurations);
by .
Let’s talk about some preliminary knowledge first:
Since there are so many automated configuration classes in our project, each automated configuration class will depend on other classes. This automated configuration class will only take effect when other classes exist. This bunch of mutual dependencies exist in spring-boot-autoconfigure-3.0.6.jar!/META-INF/spring-autoconfigure-metadata.properties
files Among them, I just give 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 must exist in the current project class pathorg.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.
Once you understand this preparatory knowledge, the following content will be easier to understand.
Let’s first look at the getConfigurationClassFilter method. This is to get all 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; }
As you can see, the filters obtained here are all of the AutoConfigurationImportFilter type. There are only three instances of this type of filter, as shown below:
From the names of these three instances, you can basically see their respective functions:
- OnClassCondition: This is
@ConditionalOnClass
the judgment condition of the conditional annotation. You can tell from the name that it is used to judge whether a certain class exists under the current classpath. - OnWebApplicationCondition: This is
ConditionalOnWebApplication
the judgment condition of the conditional annotation, which is used to judge whether the current system environment is a Web environment. - OnBeanCondition: This is
@ConditionalOnBean
the judgment condition of the conditional annotation, which is to judge whether a certain Bean exists in the current system.
The three AutoConfigurationImportFilter filters obtained here are actually the three above. 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 and 144 automated configuration classes to match. If the conditions required by these automated configuration classes are met, the corresponding position in the match array will be true, otherwise it will be false.
Then iterate through the match array, set the automated configuration classes that do not meet the conditions to null, and finally remove these nulls.
In this way, we get the classes we need to configure automatically.
The last sentence fireAutoConfigurationImportEvents triggers the automatic configuration class import event. There is nothing to say about this ~
After these automated configuration classes are loaded in, the next step is various conditional annotations to determine whether these configuration classes are effective. These are relatively simple. I have talked about it many times with my friends in vhr before, so I won’t do it here. So long-winded~
Okay, after the above review, I believe you all have a general understanding of the loading of Spring Boot automated configuration classes~