This is the 15th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

Start the class

  1. The SpringBoot application has a main entry, the main method, which calls the SpringApplication.run method to launch the program

  2. @ SpringBootApplication comments:

    • EnableAutoConfiguration: Automatically configures the Spring framework based on the declared dependencies of the application
    • SpringBootConfiguration: Configuration class for the Spring IOC container in the form of JavaConfig
    • @componentscan: ComponentScan that automatically discovers and assembles beans. By default, it scans packages in the path where the startup class is located
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(...)
    public @interfaceSpringBootApplication {... }Copy the code

The basic flow

  1. The first part is to initialize the module and configure some basic environment variables, resources, constructors, and listeners

  2. The second part implements the specific startup scheme of the application, including starting the monitoring module, loading the configuration environment module, and creating the context environment module

  3. The third part is the automatic configuration module, which serves as the core of SpringBoot automatic configuration

    1) to (2) collecting all kinds of conditions and callback interface, such as ApplictionContextInitializer, ApplictionListener - > notice started () 3. Create and prepare Environment -> Notify environmentPrepared() 4. Create and initialize ApplicationContext, ContextPrepared (); contextLoaded(); refresh ApplictionContext (); The end of theCopy the code

Implementation process

  1. Start the static run method of the class

    public static ConfigurableApplicationContext run(Class
             [] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }
    Copy the code
  2. The SpringApplication constructor

    public SpringApplication(ResourceLoader resourceLoader, Class
             ... primarySources) {
        // ...
        // Determine the application type, which is divided into responsive Web application, Servlet Web application, and non-Web application
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // ...
        // Set the initializer
        this.setInitializers(
            this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // Set the listener
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        // Set the entry method
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
    Copy the code
  3. Set the initializer

    this.setInitializers(
            this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    Copy the code
    private <T> Collection<T> getSpringFactoriesInstances(Class
             
               type, Class
              [] parameterTypes, Object... args)
              {
        ClassLoader classLoader = this.getClassLoader();
        / /, depending on the type of type ApplicationContextInitializer. Class
        // Read the spring.factories from the meta-INF of the classpath and iterate over the key-value pairs
        Set<String> names = new LinkedHashSet(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // Instantiate objects according to names
        List<T> instances = this.createSpringFactoriesInstances(
            type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    Copy the code

    The spring.factories configuration file

    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    Copy the code
  4. Setting the listener

    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    Copy the code
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.autoconfigure.BackgroundPreinitializer
    Copy the code
  5. Run method

    public ConfigurableApplicationContext run(String... args) {
        / / timer
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        // The application context to return
        ConfigurableApplicationContext context = null;
        // Set the java.awt.headless system property to true
        this.configureHeadlessProperty();
        // Load the runtime listener
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // Publish the start execution event
        listeners.starting(bootstrapContext, this.mainApplicationClass);
    
        try {
            // Handle the parameters in the launcher
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // Prepare the environment by passing in parameters based on the scanned listener objects and functions
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            / / set the Banner
            Banner printedBanner = this.printBanner(environment);
            // Create the Spring container
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            // Spring container preprocessing
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            / / - > trigger SpringApplicationRunListener contextPrepared execution
            this.refreshContext(context);
            // Spring container post processing
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            // Logs are printed
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass))
                .logStarted(this.getApplicationLog(), stopWatch);
            }
            // Issue the end of startup event
            listeners.started(context);
            // Call the registered Runners, ApplicationRunner, and CommandLineRunner in turn
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, listeners);
            throw new IllegalStateException(var10);
        }
        try {
         // Publish the application context ready event
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
            throw newIllegalStateException(var9); }}Copy the code
  6. The run method – loads the runtime listener

    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    Copy the code
    // Get the listener that is running the listener, and send the corresponding event to the listener at the corresponding stage
    private SpringApplicationRunListeners getRunListeners(String[] args) { Class<? >[] types =new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(
            logger,
            this.getSpringFactoriesInstances(
                SpringApplicationRunListener.class, types, this, args),
            this.applicationStartup);
    }
    Copy the code

    SpringApplicationRunListener class:

    public interface SpringApplicationRunListener {
    
    	// This is called immediately after the run method is called and can be used for very early initialization
        default void starting(ConfigurableBootstrapContext bootstrapContext) {
            starting();
        }
    
        // When the environment is ready
        default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
            environmentPrepared(environment);
        }
    
        // Call ApplicationContext when it is ready before the resource is loaded
        default void contextPrepared(ConfigurableApplicationContext context) {}// called when the application context is loaded but before it is refreshed
    
        default void contextLoaded(ConfigurableApplicationContext context) {}// The context has been refreshed and the application started and all CommandLineRunner and ApplicationRunner have not been called before
        default void started(ConfigurableApplicationContext context) {}// The context has been refreshed and the application started and all CommandLineRunner and ApplicationRunner have been called
        default void running(ConfigurableApplicationContext context) {}// called when the startup process fails
        default void failed(ConfigurableApplicationContext context, Throwable exception) {}}Copy the code
  7. Run method – Environment preparation

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        // Create Environment objects with different implementations based on different Web types
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        // Configure the environment
        this.configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        // The sending environment is ready to complete the event
        listeners.environmentPrepared(bootstrapContext, environment);
        DefaultPropertiesPropertySource.moveToEnd(environment);
        // Configure the activeProfile in the Environment object based on the spring.profiles. Active property in the command line parameters
        this.configureAdditionalProfiles(environment);
        // Bind the spring.main property of the environment to the SpringApplication object
        this.bindToSpringApplication(environment);
        // If the user manually sets webApplicationType using the spring.main.web-application-type property
        if (!this.isCustomEnvironment) {
            // Convert the environment object to the user set webApplicationType related type, because inherit the same parent class, directly strong
            environment = new EnvironmentConverter(getClassLoader())
                .convertEnvironmentIfNecessary(environment,                             
                                               deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
    Copy the code
  8. The run method – prepareContext

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // Set the context
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        / / execution spring. Factories ApplicationContextInitializer object initialize method
        this.applyInitializers(context);
    	// The publishing context prepares the completion event to all listeners
        listeners.contextPrepared(context);
        bootstrapContext.close(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }
    
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if(printedBanner ! =null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
    
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(
                this.allowBeanDefinitionOverriding);
        }
    
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(
                new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the resource
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // Load the bean into the context object
        this.load(context, sources.toArray(new Object[0]));
        // Send the context loading completion event
        listeners.contextLoaded(context);
    }
    Copy the code
  9. Run method – refreshContext

    private void refreshContext(ConfigurableApplicationContext context) {
        // Register hooks when the JVM is stopped
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }
    
        this.refresh((ApplicationContext)context);
    }
    
    protected void refresh(ConfigurableApplicationContext applicationContext) {
        applicationContext.refresh();
    }
    Copy the code

    Call the ConfigurableApplicationContext refresh method

    public interface ConfigurableApplicationContext extends ApplicationContext.Lifecycle.Closeable {
    	void refresh(a) throws BeansException, IllegalStateException;
    }
    Copy the code

    There are three: ConfigurableApplicationContext implementation class

    AbstractApplictionContext, ServletWebServerApplicationContext, ReactiveWebServerApplicationContext

    AbstractApplictionContext is an abstract class, the other two class inherited AbstractApplictionContext

    public void refresh(a) throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            StartupStep contextRefresh = 
                this.applicationStartup.start("spring.context.refresh");
    
            // Step 1: Prepare the preparation for updating the upper and lower
            prepareRefresh();
    
            // Step 2: Get the BeanFactory inside the context
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // Step 3: Prepare the BeanFactory
            prepareBeanFactory(beanFactory);
    
            try {
                // Step 4: Allow post-processing of bean factories in context subclasses
                postProcessBeanFactory(beanFactory);
    
                StartupStep beanPostProcess = 
                    this.applicationStartup.start("spring.context.beans.post-process");
                // Step 5: Call the factory BeanFactoryPostProcessor registered as a bean in the context
                invokeBeanFactoryPostProcessors(beanFactory);
    
                // Step 6: Register the interceptor created by the interceptor bean
                registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
    
                // Step 7: initialize internationalization related MessageSource
                initMessageSource();
    
                // Step 8: Initialize the container event announcer for publishing events
                initApplicationEventMulticaster();
    
                // Step 9: Initialize some special beans
                onRefresh();
    
                // Step 10: Register all listeners with the event broadcasters created in the first two steps
                registerListeners();
    
                // Step 11: Finish the bean initialization (instantiate all singletons BeanDefinition)
                finishBeanFactoryInitialization(beanFactory);
    
                // Step 12: afterRefresh
                finishRefresh();
            } catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                                "cancelling refresh attempt: " + ex);
                }
    
                // Destroy singletons that have been created to avoid resident resource occupancy
                destroyBeans();
    
                // Reset 'active' flag.
                cancelRefresh(ex);
    
                throw ex;
            } finally {
                // Reset the public cache because some of the metadata used to create the bean may no longer be neededresetCommonCaches(); contextRefresh.end(); }}}Copy the code

    The realization of the ServletWebServerApplicationContext:

    @Override
    public final void refresh(a) throws BeansException, IllegalStateException {
        try {
            super.refresh();
        } catch (RuntimeException ex) {
            WebServer webServer = this.webServer;
            if(webServer ! =null) {
                webServer.stop();
            }
            throwex; }}Copy the code

Automatic configuration

  1. @EnableAutoConfiguration

    EnableAutoConfiguration is part of the @SpringBootApplication

    @Import({AutoConfigurationImportSelector.class})
    public @interfaceEnableAutoConfiguration {... }Copy the code

    AutoConfigurationImportSelector SpringFactoriesLoader will all eligible @ Configuration Configuration class is loaded into the IOC container

  2. AutoConfigurationImportSelector

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // Load the spring.factories configuration information
        List<String> configurations = 
            SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                                                   getBeanClassLoader());
        // ...
        return configurations;
    }
    Copy the code
  3. SpringFactoriesLoader.loadFactoryNames

    SpringFactoriesLoader is an abstract class with static properties defined in the class that define the path to which it loads resources

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    Copy the code

    There are also three static methods:

    • LoadFactories: Loads and instantiates the specified factoryClass
    • LoadFactoryNames: Loads a collection of names for the specified factoryClass
    • InstantiateFactory: Instantiates the specified factoryClass

    Many xxxAutoConfiguration classes are configured with spring.factories. When loaded into the container, these configuration classes are full of @conditional annotations indicating the valid conditions, so that the corresponding content can be automatically configured under appropriate conditions

  4. The sample

    Hello.java

    public class Hello {
    
        private String msg;
    
        public String getMsg(a) {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg; }}Copy the code

    HelloProperties.java

    @ConfigurationProperties(prefix = "hello") 
    public class HelloProperties {
    
        private String msg;
    
        public String getMsg(a) {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg; }}Copy the code

    HelloAutoConfiguration.java

    / / configuration class
    @Configuration
    // Add HelloProperties to the IOC container
    @EnableConfigurationProperties(HelloProperties.class)
    // Determine whether the Hello class exists in the classpath. If so, the class bean will be instantiated
    @ConditionalOnClass(Hello.class)
    // The configuration determines whether to instantiate beans of this class
    @ConditionalOnProperty(prefix="hello", value="enabled", matchIfMissing = true)
    public class HelloAutoConfiguration {
    
        @Autowired
        private HelloProperties helloProperties;
    
        @Bean
        // If there is no Hello bean in the container, configure a Hello bean
        @ConditionalOnMissingBean(Hello.class)
        public Hello hello(a) {
            Hello hello = new Hello();
            hello.setMsg(helloProperties.getMsg());
            returnhello; }}Copy the code

    Create a new meta-INF /spring.factories in the resource directory

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.example.demo.pojo.HelloAutoConfiguration
    Copy the code

    application.properties

    hello.enabled=true
    hello.msg=abcde
    Copy the code

    The configuration takes effect after the test

    @SpringBootTest
    class DemoApplicationTests {
    
        @Autowired
        Hello hello;
    
        @Test
        void contextLoads(a) { System.out.println(hello.getMsg()); }}Copy the code