How exactly does the hellospprinboot we wrote earlier run? For Maven project, we usually start from the pom.xml file;
pom.xml
Parent dependency
It mainly depends on a parent project, which mainly manages the resource filtering and plug-ins of the project!
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent
Click in and find another parent dependency
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.5.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent>
This is the place to really manage all dependent versions in the SpringBoot application, the SpringBoot version control center;
In the future, we will import dependency. By default, we don't need to write a version; However, if the imported package is not managed in the dependency, you need to manually configure the version;
Launcher spring boot starter
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Springboot boot starter XXX: the scenario initiator of spring boot
Spring boot starter Web: it helps us import the components that the web module depends on for normal operation;
SpringBoot extracts all functional scenarios and makes them into starters. You only need to introduce these starters into the project, and all related dependencies will be imported. We can import the scenario starters with what functions we want to use; We can also customize the starter ourselves in the future;
Main startup class
Default main startup class
//@Spring bootapplication to annotate a main program class //This is a Spring Boot application @SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { //I thought I started a method, but I didn't expect to start a service SpringApplication.run(SpringbootApplication.class, args); } }
@SpringBootApplication
Function: mark a class to indicate that this class is the main configuration class of SpringBoot. SpringBoot should run the main method of this class to start the SpringBoot application;
Enter this annotation: you can see that there are many other annotations above!
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { // ...... }
@ComponentScan
This annotation is important in Spring and corresponds to the elements in the XML configuration.
Function: automatically scan and load qualified components or beans, and load the bean definition into the IOC container
@SpringBootConfiguration
Function: the configuration class of SpringBoot is marked on a class to indicate that it is a SpringBoot configuration class;
Let's move on to this annotation
// Click in to get the following @ Component @Configuration public @interface SpringBootConfiguration {} @Component public @interface Configuration {}
The @ Configuration here indicates that this is a Configuration class, which is the xml Configuration file corresponding to Spring;
The @ Component inside shows that the startup class itself is just a Component in Spring and is responsible for starting the application!
Let's go back to the spring boot application annotation and continue.
@EnableAutoConfiguration
@EnableAutoConfiguration: enables the auto configuration function
Previously, we needed to configure things ourselves, but now SpringBoot can automatically configure them for us@ EnableAutoConfiguration tells SpringBoot to enable the auto configuration function so that the auto configuration can take effect;
Click in the annotation to continue viewing:
@AutoConfigurationPackage: autoconfiguration package
@Import({Registrar.class}) public @interface AutoConfigurationPackage { }
@Import: the Spring underlying annotation @ import imports a component into the container
Function of Registrar.class: scan the package of the main startup class and all components in all sub packages under the package to the Spring container;
This analysis is finished. Step back and continue to look
@Import({AutoConfigurationImportSelector.class}): import components to the container;
AutoConfigurationImportSelector: if the import selector is automatically configured, which component selectors will it import? Let's click this class to see the source code:
- There is one such method in this class
// Obtain candidate configurations protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //The getSpringFactoriesLoaderFactoryClass () method here //The returned annotation class is the annotation class that we first saw to start the automatic import configuration file; EnableAutoConfiguration List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; }
- This method also calls the static method of the springfactoryesloader class! We enter the loadFactoryNames() method of the springfactoryesloader class
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); //Here it calls the loadSpringFactories method again return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
- Let's continue to click to view the loadSpringFactories method
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //After obtaining the classLoader, we can see that what we get here is the class itself marked with EnableAutoConfiguration MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //Go to get a resource "META-INF/spring.factories" Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //Traverse the read resources and encapsulate them into a property while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
spring.factories
We open spring.factories according to the source and see many automatic configuration files; This is the root of automatic configuration!
WebMvcAutoConfiguration
Let's open any one of the automatic configuration classes above, such as webmvcoautoconfiguration
You can see that these are JavaConfig configuration classes one by one, and some beans are injected. You can find some classes you know and get familiar with them!
Therefore, the real implementation of automatic Configuration is to search all META-INF/spring.factories Configuration files from the classpath, and instantiate the Configuration items under the corresponding org.springframework.boot.autoconfigure. Package into the IOC container Configuration class in the form of JavaConfig marked with @ Configuration through reflection, These are then aggregated into an instance and loaded into the IOC container.
Conclusion:
-
When SpringBoot starts, get the value specified by EnableAutoConfiguration from META-INF/spring.factories in the classpath
-
Import these values into the container as automatic configuration classes, and the automatic configuration class will take effect to help us with automatic configuration;
-
The whole J2EE solution and automatic configuration are in the jar package of springboot autoconfigure;
-
It will import many automatic configuration classes (xxxAutoConfiguration) into the container, that is, import all components required for this scenario into the container and configure these components;
-
With the automatic configuration class, it eliminates the work of manually writing configuration injection function components;
SpringApplication
At first, I thought I was running a main method, but I didn't expect to start a service;
@SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
SpringApplication.run analysis
The analysis of this method is mainly divided into two parts: one is the instantiation of spring application, and the other is the execution of run method;
SpringApplication
This class mainly does the following four things:
1. Infer whether the type of application is a normal project or a Web project
2. Find and load all available initializers and set them in the initializers property
3. Find all application listeners and set them to the listeners property
4. Infer and set the definition class of the main method, and find the main class to run
View constructor:
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { // ...... this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.setInitializers(this.getSpringFactoriesInstances(); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }