Spring Boot source code analysis (a)

Spring Boot source code analysis (a)

sschrodinger

2019/05/28


Spring boot Introduction


Spring boot configuration using convention is larger than the idea of ​​the default configuration of the application, reducing the amount of configuration time.

Spring Boot comprising the following characteristics:

  • Create a Spring application can run independently
  • Directly embedded Tomcat or Jetty server, do not need to deploy the WAR file
  • Providing recommendations on the basis POM file to simplify the configuration of Apache Maven
  • Possible to automatically configure the Spring framework in accordance with project dependencies
  • It provides functionality that can be used directly in production environments, such as performance, application information and application health check
  • No code generation, and no XML configuration file

By Spring Boot, create a new Spring application becomes very easy, and create a Spring application conform to the universal best practices. It requires only a few simple steps you can create a Web application. For creating spring boot, see spring official starter .


Spring boot loader


Because Spring boot built Jetty or Tomcat server, does not need to be deployed directly War file, the program starting spring boot for a normal main function, the main function of the following categories:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

Seemingly ordinary procedure is no different, in fact, he was the most important step through annotations @SpringBootApplicationand methods SpringApplication.run()completed.

In fact, all the dependencies in this step can be completed injection, the main steps are spring read all the dependence META-INF/spring.factoriesfile indicates which dependence can be automatically loaded, and in accordance with ImportSelectorload-dependent class selector which is loaded into IoC container .

META-INF/spring.factories Read the file

The main function is only one way, i.e. SpringApplication.run(), as follows:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

runThe method is just a static method call is run(new Class<?>[] { primarySource }, args)method. as follows:

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
    return new SpringApplication(primarySources).run(args);
}

Wherein the configuration file read operation occurs in the SpringApplicationinstantiation process class. Examples of the code is as follows:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

In getSpringFactoriesInstances(ApplicationContextInitializer.class))the spring spring.factories read the file.

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    } try {
        Enumeration<URL> urls = (classLoader != null ?
            // FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_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());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    
    // output
    // like:
    // org.springframework.boot.autoconfigure.EnableAutoConfiguration -> {LinkedList@1446} size = 118
    //   |
    //   |- org.springframework.boot.autoconfigure.EnableAutoConfiguration
        
}

This method keeps all META-INF/spring.factoriescaches SpringFactoriesLoaderclass read read when needed.

to sum up

  • SpringFactoriesLoader Static method in class implements read the file dependent, reading the names of all the fully qualified name of the class configuration

@SpringBootApplication comment

@SpringBootApplication It is a complex annotation, as follows:

@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 {
    // ...
}

Notes on behalf of a configuration notes, as follows:

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

With this comment, we can put the @SpringBootApplicationnotes as modified class configuration class to use, that is in a class DemoApplicationby @Beanannotations modified code will be injected into the unified management by the IoC container Spring.

Annotations where three such packages automatically scans the various components is injected into the vessel allowed IoC management, so we can type DemoApplicationon the increase, such as @Controllerannotations, which as a Controller.

The most important thing is @EnableAutoConfigurationcomments.

In Spring, in fact, the same strain of ideas and ways of doing things annotated at the beginning of Enable, a quick overview that, with the support of @Import collected and registered bean definitions related to specific scene .

@EnableAutoConfiguration comment

@EnableAutoConfiguration It is defined as follows:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage  // 注解一
@Import(AutoConfigurationImportSelector.class) // 注解二
public @interface EnableAutoConfiguration {

    // ...

}

@Import

@Import Annotations can import three types:

  1. @Configuration Notes modified class
  2. ImportSelectorOr ImportBeanDefinitionRegistrarimplementation class
  3. Common component class, that is, from @Componetnotes modified class.

ImportSelector Interface is defined as follows:

public interface ImportSelector {
    // 返回哪些类需要被创建
    String[] selectImports(AnnotationMetadata importingClassMetadata);

}

The main role of the interface which is introduced in accordance with given selection criteria (usually one or more annotations attributes) @Configurationclass.

note

  • If the subclass interface achieves the following four interface functions will first perform the following four interfaces:
  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware
  • If you want all of @Configurationthe import of such class are imported and then, using its sub-interfacesDeferredImportSelector

ImportSelectorClass BeanFactory.refresh()calls the method, it should be specific to the BeanFactory.refresh()function invokeBeanFactoryPostProcessors, in particular see [the Spring source analysis (b) Core], invokeBeanFactoryPostProcessorsthe main role is to register all PostProcessors, i.e. bean post-processor, call before sigleton instantiated.

The main methods are as follows:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // ...
}

The following is PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()the code for the method, part of the code as follows:

public static void invokeBeanFactoryPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
    Set<String> processedBeans = new HashSet<>();

    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

        // 记录是否是定义类的 Processor 或者普通的 Processor

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the bean factory post-processors apply to them!
        // Separate between BeanDefinitionRegistryPostProcessors that implement
        // PriorityOrdered, Ordered, and the rest.
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

        // ...
        // 应用 Bean 定义类的后置处理器
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        // ...

}

private static void invokeBeanDefinitionRegistryPostProcessors(
            Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}

invokeBeanDefinitionRegistryPostProcessorsPostprocessor function class is defined for each application separately @Configureresolved in this function. as follows:

// 从注册表中的配置类派生更多的bean定义
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // ...
    this.registriesPostProcessed.add(registryId);
    // Build and validate a configuration model based on the registry of Configuration classes.
    processConfigBeanDefinitions(registry);
}

Entered the most crucial of class ConfigurationClassPostProcessor, this class of users to register all @Configureand @Bean. His processConfigBeanDefinitionsfunction is as follows:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    // 记录所有候选的未加载的配置

    // Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {
        return;
    }

    // 按照 Ordered 对配置进行排序

    // 加载自定义 bean 名命策略

    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }

        // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        // 解译候选集
        parser.parse(candidates);
        parser.validate();

        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // Read the model and create bean definitions based on its content
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        // ...
    } while (!candidates.isEmpty());

    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // Clear cache in externally provided MetadataReaderFactory; this is a no-op
        // for a shared cache since it'll be cleared by the ApplicationContext.
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

Note that processin the this.deferredImportSelectorHandler.process()method:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }

    this.deferredImportSelectorHandler.process();
}

This is to achieve a processing deferredImportSelectormethod interface. The method reads all class names meet the requirements and added to IoC framework.

This method reads a static class variables SpringFactoriesLoader.cacheto get all you need to load the fully qualified name of the class. Method stacks as follows:

selectImports:10, RoleImportSelector (com.example.demo.config)
/^\
process:889, ConfigurationClassParser$DefaultDeferredImportSelectorGroup (org.springframework.context.annotation)
/^\
getImports:875, ConfigurationClassParser$DeferredImportSelectorGrouping (org.springframework.context.annotation)
/^\
processGroupImports:801, ConfigurationClassParser$DeferredImportSelectorGroupingHandler (org.springframework.context.annotation)
/^\
process:771, ConfigurationClassParser$DeferredImportSelectorHandler (org.springframework.context.annotation)
/^\
parse:185, ConfigurationClassParser (org.springframework.context.annotation)
/^\
processConfigBeanDefinitions:315, ConfigurationClassPostProcessor (org.springframework.context.annotation)
/^\
postProcessBeanDefinitionRegistry:232, ConfigurationClassPostProcessor (org.springframework.context.annotation)
/^\
invokeBeanDefinitionRegistryPostProcessors:275, PostProcessorRegistrationDelegate (org.springframework.context.support)
/^\
invokeBeanFactoryPostProcessors:95, PostProcessorRegistrationDelegate (org.springframework.context.support)
/^\
invokeBeanFactoryPostProcessors:705, AbstractApplicationContext (org.springframework.context.support)
/^\

According to @EnableAutoConfigurationthe full class name org.springframework.boot.autoconfigure.EnableAutoConfigurationas Key lookup, obtain a set of corresponding @Configurationclasses.

I.e., all of the search from the classpath META-INF/spring.factoriesconfiguration files, and wherein org.springframework.boot.autoconfigure.EnableutoConfigurationthe corresponding configuration item corresponding instantiated as marked by reflection (Java Refletion) @Configurationin the form of JavaConfig IoC container configuration class, and then aggregated and loaded onto a IoC container.

Reproduced in: https: //www.jianshu.com/p/e7a33e9eec0e

Guess you like

Origin blog.csdn.net/weixin_34061482/article/details/91227168