Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
SpringBoot is a framework we often use, so can you do a detailed introduction to SpringBoot automatic configuration. If possible, can you draw a flow chart to implement automatic configuration? What are the key classes involved, and what are the key points.
Let’s take a look!
Preface:
After reading this article:
- You can learn how SpringBoot automatically configures itself when it starts
- You can see the automatic configuration process when SpringBoot starts
- And some common annotations for SpringBoot
Debug from shallow to deep step by step.
Note: The SpringBoot version of this article is 2.5.2
First, the startup class
Preamble what, do not say, everyone will use, we directly start with the SpringBoot boot class.
@SpringBootApplication
public class Hello {
public static void main(String[] args) { SpringApplication.run(Hello.class); }}Copy the code
The @SpringBootApplication annotation indicates on a class that this class is the main configuration class for SpringBoot, and SpringBoot should run the class’s main method to start the SpringBoot application; Is the focus of our research!! It is essentially a composite annotation that we click on to see how it is written on Javadoc, from shallow to deep, from rough to detailed.
Let’s click inside to see:
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
Copy the code
Javadoc says so
Represents a Configuration class that declares one or more @Bean methods and triggers auto-Configuration and Component Scanning. This is a handy annotation that declares @Configuration, @EnableAutoConfiguration, and @ComponentScan.
— Why can it integrate so many annotation functions?
It’s the @Inherited annotation on it, which means Inherited annotation types automatically.
The two most important annotations here are @SpringBootConfiguration and @EnableAutoConfiguration.
1.1, @ SpringBootConfiguration
Let’s start with the @springBootConfiguration annotation:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interfaceSpringBootConfiguration {}.Copy the code
1.2, @ EnableAutoConfiguration
Look at the @ EnableAutoConfiguration.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
Copy the code
1.3, @ ComponentScan
@ComponentScan: Configures the component scanning directive for the Configuration class. Support for parallelism with Spring XML’s
element. BasePackageClasses or basePackages (or its alias value) can define a specific package to scan. If no specific package is defined, the scan starts with the package of the class declaring the annotation.
As understanding, not the focus of this article.
1.4. Research direction
The main line in the middle of the diagram will be explored, and the rest will only be explained a little bit.
Second, @ SpringBootConfiguration
We’ve just taken a quick look at @SpringBootConfiguration.
@Configuration
@Indexed
public @interface SpringBootConfiguration {}
Copy the code
It is a SpringBoot configuration class, marked on a class to indicate that this is a SpringBoot configuration class.
Here we see @Configuration, an annotation we’ve seen in Spring, which means to mark a class as a Spring Configuration class, the equivalent of an XML file in Spring, that can inject components into the container.
Not the point of inquiry.
Third, @ EnableAutoConfiguration
Let’s take a look at this. It literally means: automatic configuration import.
@Inherited
@AutoConfigurationPackage //// Automatic package guide
@Import(AutoConfigurationImportSelector.class) //// Automatic configuration import option
public @interface EnableAutoConfiguration {}
Copy the code
You can guess from the name that this must be related to automatic configuration.
We then look at the two annotations on this @ AutoConfigurationPackage and @ Import (AutoConfigurationImportSelector. Class), the two is the focus of our research.
3.1, @ AutoConfigurationPackage
Click in and have a look:
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}
Copy the code
@ Import for spring annotations, Import a configuration file, in springboot for container Import a component, and the imported components by AutoConfigurationPackages. The Registrar. The class execution logic.
Look at 👇 : Registrar
static class Registrar implements ImportBeanDefinitionRegistrar.DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(newPackageImports(metadata)); }}Copy the code
Here we can break to see what value new PackageImports(metadata).getPackAgenames ().toArray(new String[0]) is.
New PackageImports(metadata).getPackAgenames ().toarray (new String[0]) : com.crush. Hello , the package in which the current startup class resides.
Further down the line is the Spring registry, and XDM can debug further.
Here we can draw a small conclusion:
The @AutoConfigurationPackage annotation itself is designed to scan into the Spring container all components under the package that contains the main configuration class (the class annotated by @SpringBootApplication).
If you put a Controller outside of com.crush. Hello it will not be scanned and an error will be reported.
3.2, @ Import (AutoConfigurationImportSelector. Class)
AutoConfigurationImportSelector open automatic configuration guide package of class selector which component selector (import)
We point into AutoConfigurationImportSelector class to see, what are the key of knowledge, this class of methods can help us to get all of the configuration
public class AutoConfigurationImportSelector implements DeferredImportSelector.BeanClassLoaderAware.ResourceLoaderAware.BeanFactoryAware.EnvironmentAware.Ordered {
/** Select the components to import,*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if(! isEnabled(annotationMetadata)) {return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
/ / according to import @ the Configuration class AnnotationMetadata returns AutoConfigurationImportSelector. AutoConfigurationEntry.
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if(! isEnabled(annotationMetadata)) {return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// You can make a breakpoint here to see the data returned
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// Delete duplicates
configurations = removeDuplicates(configurations);
// Remove dependencies
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
/ / check
checkExcludedClasses(configurations, exclusions);
// Remove dependencies that need to be excluded
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return newAutoConfigurationEntry(configurations, exclusions); }}Copy the code
If we look at the breakpoint, the configurations array is 131 in length and the file name extension is **AutoConfiguration
The idea here is to return all the components that need to be imported with full class names and add them to the container, and you end up importing a very large number of automatic configuration classes (xxxAutoConfiguration) into the container, importing all the components needed for this scenario, and configuring them. With automatic configuration, we don’t need to write by hand.
3.2.1, getCandidateConfigurations ()
We also need to think about it, these configurations are obtained from the getCandidateConfigurations method, this method can be used to retrieve all candidate configuration, then the candidate configuration is come from?
Step by step:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
/ / there's a loadFactoryNames method execution time and the two parameters, one is BeanClassLoader, another is getSpringFactoriesLoaderFactoryClass () we see together
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;
}
Copy the code
Look at the getSpringFactoriesLoaderFactoryClass () method, is passed here
protectedClass<? > getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;
}
Copy the code
Does this EnableAutoConfiguration look familiar? (Does our starting point, @enableAutoConfiguration, feel like you’re getting closer?)
Let’s see what the loadFactoryNames() method does with it:
First will EnableAutoConfiguration. Class passed factoryType, then the getName (), so for EnableAutoConfiguration factoryTypeName values.
3.2.2 loadSpringFactories ()
Then you start calling the loadSpringFactories method again
Here the FACTORIES_RESOURCE_LOCATION is defined above:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
Let’s go back togetCandidateConfigurations
Methods.
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.");
Copy the code
No auto-configuration class was found in meta-INF/Spring.Factories. If you use custom wrappers, make sure the file is correct. “
Where is this meta-INF/spring.Factories?
What it says:
There’s basically a configuration class that we use every day.
For example, webmvc,
Let’s click on the WebMvcProperties class to see:
So what the hell is going on here?
The idea here is to take the urls of the file and iterate through each of those urls, and finally make the file into a Properties object, the loadProperties method
We then get the values we need from the Properties object, load those values into the result we’re going to return, which is a map set, and return it to the loadFactoryNames method.
Then we go back to loadSpringFactories(classLoaderToUse).getorDefault (factoryTypeName, collections.emptyList ()); At the call of.
The factoryTypeName value is EnableAutoConfiguration
For loadFactoryNames method carry to come over to the first parameter is EnableAutoConfiguration. The class, so factoryType value for EnableAutoConfiguration. Class, Then factoryTypeName values EnableAutoConfiguration.
So what does getOrDefault mean in the map set? EnableAutoConfiguration = EnableAutoConfiguration = EnableAutoConfiguration = EnableAutoConfiguration = EnableAutoConfiguration = EnableAutoConfiguration
Look at the picture below, isn’t that what it is?
Jar/meta-inf /spring.factories: < EnableAutoConfiguration > Each xxxAutoConfiguration class is a component of the container and is added to the container. The purpose of adding them to the container is to use them for auto-configuration, which is where Springboot auto-configuration begins.
Only after these auto-configuration classes are in the container does the next auto-configuration class start
Do you have so many configurations in spring.factories that you load them all every time you start them?
Is it all loaded? Impossible ha, this everyone knows ha, all load to start a project do not know how long to go. It’s selective.
Let’s click on any class and make this @conditionalonxxx comment
@Conditional is actually a spring underlying annotation, which means to judge different conditions according to different conditions. If the specified conditions are met, the configuration in the whole configuration class will take effect.
So instead of loading the spring.Factories configuration, you load the annotation only if all the classes in the annotation exist.
This is the automatic configuration of SpringBoot.
Four, summary
A simple summary is:
The startup class has an @SpringBootApplication annotation, which includes @SpringBootConfiguration, @EnableAutoConfiguration, The @enableAutoConfiguration command is used to enable auto-assembly. The annotations go to the spring-boot-autoconfigure project and look for the meta-INF /spring.factories file. This file lists all the classes that can be auto-assembled, and then automatically reads the auto-assembly configuration class list inside. Annotations, because there are @ ConditionalOn conditions satisfy certain conditions configuration will only take effect, otherwise don’t come into effect. For this configuration to take effect, @conditionalonClass must include some related classes in the project. Therefore, when some corresponding classes are introduced into our dependencies and the conditions of auto-assembly are met, auto-assembly will be triggered.
Five, talk to yourself
The paper come zhongjue shallow, and must know this to practice.
If you can, you can debug again and draw a flow chart. 🛌 (lying flat)
Hello, THIS is blogger Ning Zaichun: homepage
I hope you found this article useful!!
Wish us: by the time we meet on another day, we have achieved something.
If you have any doubts, please leave a comment.
If there is any deficiency, please point it out, thank you very much 👨💻.