Java architecture Express -- @ SpringApplication automatic assembly principle

Article directory

At present, microservices are the standard configuration of enterprises. In view of the fact that many small partners are only aware of it, they don't know why. Here is a summary of the principle of automatic assembly of SpringBoot. Some of the following contents are written directly by the teacher based on the principle of automatic assembly of SpringBoot. If you understand the principle of automatic assembly of SpringBoot, then Face to face learning will be easier.

Principle analysis of SpringBoot startup process

First of all, for a spring boot project, the most obvious sign is @ SpringBootApplication, which marks that this is a spring boot project, so today's spring boot auto assembly principle starts from it.

First of all, let's take a look at the mystery behind @ SpringBootApplication annotation. Press Ctrl + left mouse button, and click it gently. At this time, witness the miracle moment

We see elegant code like this

@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 {   
    // Ten thousand words are omitted here
    ...
}

There are two things that are easy to attract our attention, one is the @ SpringBootConfiguration annotation, the other is the @ EnableAutoConfiguration annotation. The reason why these two annotations attract our attention is not because they grow up to be good-looking, but because other annotations are too ugly (mainly because we are familiar with other annotations, even though I don't know what they do. It doesn't matter that they assemble more automatically. Then we stretched out our evil little hand, opened the familiar operation, pressed Ctrl + left mouse button, stared at Mimi's small eyes, pupil magnified a hundred times and waited for the miracle to appear Wipe... Wipe... Wipe...

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

Nothing
What's the use of you? How can such a top-level world-class open source project make a useless guy exist? So we used hundreds of millions of brain cells. After complicated calculation, we came to an unreliable conclusion that it might be used to mark the configuration of a spring boot project. Because spring boot configuration translates to the configuration of spring boot, tens of thousands of alpacas are flying in the desert.

After calming down, adhering to the belief that "failure is the mother of success", he skillfully pressed the Ctrl + Table key with his left hand and went back to the original place. Stare at @ enable autoconfiguration, look around, enter Google translation in the address bar, and the result shows that auto assembly. I'm looking for you. I'm looking for him in the crowd, but he's in the dark. Skillfully press Ctrl + left key, can't wait to enter; recite the classic poem of peach blossom source in my heart:

Forest water, you will get a mountain, mountain mouth, as if there is light. He gave up his boat and entered from the mouth. At the beginning, it was very narrow that people were connected. Take dozens of steps again, and you will be enlightened

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // 100 words are omitted here
    ...
}

At this moment, we are in a happy mood. After the previous experience, we have calmed down a lot in the face of the new world. At this time, the brain is running at a high speed, and there is no more trouble. It enters the AutoConfigurationImportSelector.class class class, because Google translation tells us that this is the automatic configuration import selector. So we discovered a new world.

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {


    @Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		// Get auto configured entities
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
				autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

    // Specific methods for loading auto configuration classes
	protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// Get the candidate configuration classes. Even if the harem is three thousand, they should be filtered
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
		// Depending on the situation, configure the required configuration classes and unnecessary configurations automatically
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		// Return to the final required configuration
		return new AutoConfigurationEntry(configurations, exclusions);
	}
}

There are two properties in the AutoConfigurationEntry, configurations and exclusions.

protected static class AutoConfigurationEntry {
    	// Used to store required configuration items
		private final List<String> configurations; 
   	 	// Configuration items used to store exclusions
		private final Set<String> exclusions;      
		
		private AutoConfigurationEntry() {
       	 	this.configurations = Collections.emptyList();
   			this.exclusions = Collections.emptySet();
        }
}

Later, you can see that the getAutoConfigurationEntry() method returns an object return new AutoConfigurationEntry(configurations, exclusions). Here we have all the required configurations.

How does he get the candidate configuration class? Let's take a look at this method to get candidate configuration classes

List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);

After entering the method, we can see the following method to obtain the method content of the candidate configuration class

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		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;
	}

Here we follow the breakpoint. First, enter the getspringfactoryesloaderfactoryclass() method

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
	// Returned is EnableAutoConfiguration bytecode object
    return EnableAutoConfiguration.class;
}

Then we enter the getBeanClassLoader() method, which is a classloader

protected ClassLoader getBeanClassLoader() {
		return this.beanClassLoader;
}

Finally, we enter the loadFactoryNames() method, which is to find the candidate configuration class according to the bytecode file and class loader just now. Bytecode passed in

public static List<String> loadFactoryNames(Class<?> 				factoryClass, @Nullable ClassLoader classLoader) {
	// Get the permission name of EnableAutoConfiguration.class / / org.springframework.boot.autoconfigure.EnableAutoConfiguration
    String factoryClassName = factoryClass.getName();
		return                          					 		  		           loadSpringFactories(classLoader).getOrDefault(factoryClassNa		me, Collections.emptyList());
	}

The following picture:

Finally, get all configuration classes through loadspringfactors()

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	// Cache loaded configuration class,	
    MultiValueMap<String, String> result = 			 	 	 	 						cache.get(classLoader);
	if (result != null) {
		return result;
	}
	try {
		Enumeration<URL> urls = (classLoader != null ?		//	Go to the resource directory to find classloader.getresources (facts "resource" location):
//	Go to the system resource directory to find classloader.getsystemresources (facts "resource" location));
	result = new LinkedMultiValueMap<>();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			Properties properties = 	          					 	PropertiesLoaderUtils.loadProperties(resource);
		for (Map.Entry<?, ?> entry properties.entrySet()) {
			String factoryClassName = ((String) 									entry.getKey()).trim();
			for (String factoryName : 								StringUtils.commaDelimitedListToStringArray((String) 				entry.getValue())) {
				result.add(factoryClassName, 											factoryName.trim());
					}
				}
			}
            // Load completed and put into cache
			cache.put(classLoader, result);
            // Returns the configuration class loaded into
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to 						load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

Here we will see how to load the configuration file from facts? Resource? Location

public final class SpringFactoriesLoader {
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

That is to say, when the project is started, all the spring.factories files under META-INF will be loaded. Let's search this file. What I built is the simplest spring boot project. It will go to these three jar s to find the relevant configuration classes.

But the last auto assembled class is spring boot autoconfigure-2.1.5.release.jar

The only configuration class loaded according to EnableAutoConfiguration.class bytecode is this 118 auto configuration class

Summary

In fact, spring boot's automatic assembly principle is to load the spring.factories file under META-INF when the project is started. It seems that it is not so tall. Of course, there will be other configuration items loaded during the startup process. Let's talk about the loading process of automatic assembly. I hope it can inspire you.

Question: understand the principle of spring boot auto assembly. If we need to load our custom configuration class when the project starts, how to write it?

Published 364 original articles, won praise 324, visited 150000+
His message board follow

Tags: Spring SpringBoot Google

Posted on Sun, 12 Jan 2020 04:38:47 -0500 by kishore_marti