Spring Boot automatic assembly principle and source code analysis

The main content of the article

  1. Spring Boot principle of automatic assembly [there] simulation case
  2. Spring Boot automatic assembly source code analysis

Automatic loading of principle SpringBoot

SpringBoot with features out of the box, it silently help us to do a lot of things [are automatically created and assembled many objects]

Implementation of automated assembly

The interface is mainly done by the automatic assembly ImportSelector

ImportSelector

ImportSelector interface is the core Spring import the foreign configuration interface (functional annotation) played a decisive role in automated configuration SpringBoot and @EnableXXX. When using a ImportSelector @Import introduced in the implementation class @Con fi guration marked Class, it will return Class implementation class names are defined as bean.

package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;

public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

} 复制代码

The inheritance graph interface

Here Insert Picture Description

DeferredImportSelector

From the above chart shows DeferredImportSelector interface inherits from ImportSelector, it ImportSelector difference is the timing of the loading of the bean, it will be loaded after DeferredImportSelector need to wait for all the @Con fi guration were completed .

Spring Boot automatic assembly case

Projects Portal: autoConfig

Case major code

  1. Bean production to create a configuration class, but we do not use @Configuration annotation to declare it.

public class MyConfig {
    @Bean(value = "chenfu", name = "chenfu")
    public Map<Object, Object> getMap() {
        HashMap<Object, Object> map = new HashMap<>();
        map.put("code", 200);
        map.put("msg", "success");
        String nowDate = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        map.put("data", nowDate);
        return map;
    }
} 复制代码
2. Implement ImportSelector interfaces, we return to the top of the configuration class name

public class MyConfigImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
//        返回配置名称
        return new String[]{MyConfig.class.getName()};
    }
} 复制代码
3. Run the test

@SpringBootApplication
@Import(value = MyConfigImportSelector.class)
public class AutoConfigApp {
    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(AutoConfigApp.class, args);
        Object chenfu = app.getBean("chenfu");
        System.out.println(chenfu);
    }
} 

复制代码

Case general content above, after Spring Boot automatic assembly of the object does not create annotation statement (@ Controller, @ Service, @ Repostiroty) using Spring object, but dynamically loaded bean using a programmed through case known. Mainly in the class ConfigurationClassParser processImports method performed in the step of parsing Spring Boor these objects

The resolution process ImportSelector

ConfigurationClassParser

ConfigurationClassParser class source

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
	if (importCandidates.isEmpty()) {
		return;
	}
	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			for (SourceClass candidate : importCandidates) {
				//对ImportSelector类型的类进行处理
				if (candidate.isAssignable(ImportSelector.class)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass();
					ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
					ParserStrategyUtils.invokeAwareMethods(
							selector, this.environment, this.resourceLoader, this.registry);
 					//上边我们说过的另外一种Selector类型,可以理解为延迟加载
					if (selector instanceof DeferredImportSelector) {
						//该方法内部会将该Selector保存到一个集合【deferredImportSelectors】中
						this.deferredImportSelectorHandler.handle(
								configClass, (DeferredImportSelector) selector);
					} else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
						processImports(configClass, currentSourceClass, importSourceClasses, false);
					}
				} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional bean definitions
					Class<?> candidateClass = candidate.loadClass();
					ImportBeanDefinitionRegistrar registrar =
							BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
					ParserStrategyUtils.invokeAwareMethods(
							registrar, this.environment, this.resourceLoader, this.registry);
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				} else {
					// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
					// process it as an @Configuration class
					// 直接把上边的官方英文注释硬翻译了,当前类不是ImportSelector或ImportBeanDefinitionRegistrar类型,直接让其走@Configuration类的处理流程
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					processConfigurationClass(candidate.asConfigClass(configClass));
				}
			}
		} catch (BeanDefinitionStoreException ex) {
			throw ex;
		} catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to process import candidates for configuration class [" +
					configClass.getMetadata().getClassName() + "]", ex);
		} finally {
			this.importStack.pop();
		}
	}
} 

复制代码

In summary, the general process is the return value of the interface ImportSelector parsed recursively, and then the full name of the class to resolve the final processed according @Con fi guration.

ImportSelector summary

SpringBoot features out of the box ready to use, largely thanks to ImportSelector.

Spring Boot source code analysis

Spring Boot Spring on the basis of doing some extensions.

  1. It declares a note in SpringBootApplication @EnableAutoConfiguration comment SpringBoot in

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication { 复制代码
2. @ EnableAutoConfiguration introduced @Import ({AutoConfigurationImportSelector.class}) SpringBoot defined by the Import

@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration { 复制代码

Next, begin to be source code analysis AutoConfigurationImportSelector

AutoConfigurationImportSelector

AutoConfigurationImportSelector is selectImports implementation class, we look selectImports method

selectImports

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
} 

复制代码

The main logic of the method are a method in getAutoConfigurationEntry

getAutoConfigurationEntry

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        // 2.1通过getCandidateConfigurations方法获取所有需要加载的bean
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        // 去重
        configurations = this.removeDuplicates(configurations);
        // 获取不需要加载的bean,我们可以通过spring.autoconfigure.exclude配置
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = this.filter(configurations, autoConfigurationMetadata);
        // 发送事件,通知所有的AutoConfigurationImportListener进行监听
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
} 复制代码

Next, let's look at the top source getCandidateConfigurations method called in

getCandidateConfigurations

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	// 这里的getSpringFactoriesLoaderFactoryClass()最终返回的是EnableAutoConfiguration.class
    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;
} 

复制代码

Can be seen from the above logic, the final channel in SpringFactoriesLoader.loadFactoryNames bean obtain the SpringFactoriesLoader.loadFactoryNames (this.getSpringFactoriesLoaderFactoryClass (), this.getBeanClassLoader ());

SpringFactoriesLoader

loadFactoryNames

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	// 通过factoryClassName获取相应的bean全称
    String factoryClassName = factoryClass.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
} 

复制代码

loadSpringFactories

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
        	// 获取项目中所有META-INF/spring.factories文件,将其组装成Map
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            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);
        }
    }
} 

复制代码

Each jar package can define its own META-INF / spring.factories, the jar is loaded at the same time spring.factories defined inside bean will be automatically loaded. We can look at Spring Boot of the contents of the configuration file [only] part out

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ 复制代码

Therefore, we can transform the top of the case, create the file in the directory resouces, then add the following

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
top.chenfu.auto.config.MyConfig   复制代码

Remove the boot class @import or custom annotations @EnableXXX

Here Insert Picture Description

More exciting articles, may be concerned about micro-channel public number: Java programmers gathering




Guess you like

Origin juejin.im/post/5df76ed46fb9a016526ebbcc