Spring boot source code analysis

One: SpringBoot

1. Three features of SpringBoot

1. Help developers quickly integrate the third-party framework (principle Maven dependency encapsulation)

2. Embedded server (principle Java language creation server)

3. Complete annotation instead of XML (principle packaging Spring system annotation) Spring boot starter Web integration Spring, Spring MVC

2. Concept of SpringBoot and SpringCloud

Spring cloud's RPC remote call depends on spring MVC writing interface (Http+json)

Spring cloud is a microservice one-stop solution built on top of spring boot

3. Summary of common notes

@Enable autoconfiguration: start spring MVC. When starting, the scope of scanning package is under the current package

@ComponentScan: scan package range at startup

@Configuration: identify the current class as a configuration class, and inject beans with @ Bean

@SpringBootApplication: integrate the previous three notes, scan the current peer package and sub package

4. SpringBoot integrates multiple data sources

1. Subcontractor name (recommended)

2. Note form:

@EnableTransactionManager annotation is on by default

The problem of multi data source distributed transaction arises in the same project. There are many different two-stage commit protocols of database connection (jta+automic). Unify data source to global xa transaction management

5. Global capture exception

@ControllerAdvice: identified as an exception facet class

@ExceptionHandler(XXX.class): intercept exception (exception type. class)

6. Multi environment version

Local development, test environment, pre production environment, production environment

application.yml: Specifies the environment to read from:

    spring:  
        profiles:    
active: dev defaults to the development environment

II. Spring boot source code analysis

1. User defined starter

@Configuration: equivalent to xml configuration, combined with @ Bean

Custom starter

1. Introduce autoconfiguration dependency: automatic injection

2.META-INF/spring.factories: configuration EnableAutoConfiguration = custom configuration

3. Introduce process dependency and write configuration file with prompt

4. Enter maven warehouse

2. Source code analysis

First, the project startup class:

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

One: creating SpringApplication object process: new SpringApplication(primarySources)

public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
     this.sources = new LinkedHashSet();
     this.bannerMode = Mode.CONSOLE;
     this.logStartupInfo = true;
     this.addCommandLineProperties = true;
     this.addConversionService = true;
     this.headless = true;
     this.registerShutdownHook = true;
     this.additionalProfiles = new HashSet();
     this.isCustomEnvironment = false;
     this.resourceLoader = resourceLoader;
     Assert.notNull(primarySources, "PrimarySources must not be null");
     this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
 	//The program enters here and selects the startup mode
     this.webApplicationType = WebApplicationType.deduceFromClasspath();
     this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
     this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
     this.mainApplicationClass = this.deduceMainApplicationClass();
 }

Two: this.webApplicationType = WebApplicationType.deduceFromClasspath();

static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            //1. Start with responsive web
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    //2. Do not embed web server, and finally run through external tomcat server
                    return NONE;
                }
            }
			//The program branch came here
            //3. The application is based on servlet application and embedded in web server server
            return SERVLET;
        }
    }
//Injecting spring context sensitive classes into spring containers
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

//Inject spring listening related classes into spring container
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

this.mainApplicationClass = this.deduceMainApplicationClass();
 //Get started class
private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
        }

        return null;
    }
return (new SpringApplication(primarySources)).run(args);
 public ConfigurableApplicationContext run(String... args) {
     
        StopWatch stopWatch = new StopWatch();
     	//Record start-up time
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            //banner chart output from the print console
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            //Record start end time
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
/** * Application startup portal */
@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@EnableAutoConfiguration
//Selector mode injected into our IOC container
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //Here we get 109 configuration classes, and finally selectively register them in the IOC container
            //109 classes under EnableAutoConfiguration under META-INF/spring.factories. If they are introduced, the startup classes of the third-party configuration will be loaded
            //Load DispatcherServletAutoConfiguration
            //Load ServletWebServerFactoryAutoConfiguration
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

ServletWebServerFactoryAutoConfiguration
 @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }
  public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        this.serverProperties = serverProperties;
    }
@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    @NestedConfigurationProperty
    private final ErrorProperties error = new ErrorProperties();
    private Boolean useForwardHeaders;
    private String serverHeader;
    private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
    private Duration connectionTimeout;
    @NestedConfigurationProperty
    private Ssl ssl;
    @NestedConfigurationProperty
    private final Compression compression = new Compression();
    @NestedConfigurationProperty
    private final Http2 http2 = new Http2();
    private final ServerProperties.Servlet servlet = new ServerProperties.Servlet();
    private final ServerProperties.Tomcat tomcat = new ServerProperties.Tomcat();
    private final ServerProperties.Jetty jetty = new ServerProperties.Jetty();
    private final ServerProperties.Undertow undertow = new ServerProperties.Undertow();
@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
//Three startup configuration classes, supporting three types of servers
//EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
    public ServletWebServerFactoryAutoConfiguration() {
    }

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }
 @Configuration
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedTomcat {
        public EmbeddedTomcat() {
        }

        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    }
 public WebServer getWebServer(ServletContextInitializer... initializers) {
     	//Create our tomcat server
        Tomcat tomcat = new Tomcat();
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        this.configureEngine(tomcat.getEngine());
        Iterator var5 = this.additionalTomcatConnectors.iterator();

        while(var5.hasNext()) {
            Connector additionalConnector = (Connector)var5.next();
            tomcat.getService().addConnector(additionalConnector);
        }

        this.prepareContext(tomcat.getHost(), initializers);
        return this.getTomcatWebServer(tomcat);
    }

DispatcherServletAutoConfiguration

WebMvcProperties

@ConfigurationProperties(
    prefix = "spring.mvc"
)
public class WebMvcProperties {
    private Format messageCodesResolverFormat;
    private Locale locale;
    private WebMvcProperties.LocaleResolver localeResolver;
    private String dateFormat;
    private boolean dispatchTraceRequest;
    private boolean dispatchOptionsRequest;
    private boolean ignoreDefaultModelOnRedirect;
    private boolean throwExceptionIfNoHandlerFound;
    private boolean logResolvedException;
    private String staticPathPattern;
    private final WebMvcProperties.Async async;
    private final WebMvcProperties.Servlet servlet;
    private final WebMvcProperties.View view;
    private final WebMvcProperties.Contentnegotiation contentnegotiation;
    private final WebMvcProperties.Pathmatch pathmatch;
//Load spring MVC
@Bean(
            name = {"dispatcherServlet"}
        )
        public DispatcherServlet dispatcherServlet() {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
            dispatcherServlet.setEnableLoggingRequestDetails(this.httpProperties.isLogRequestDetails());
            return dispatcherServlet;
        }

III. analysis of SpringBoot startup process

//1. Create SpringApplication object
new SpringApplication(primarySources)
//1.1 principle of getting the current startup type: judge whether the current classpath has loaded our servlet class, and return the startup mode. webApplicationType can be divided into three startup types: REACTIVE, NONE, servlet, default servlet startup: Embedded web server startup
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//1.2 read the ApplicationContextInitializer under META-INF.spring.factories under the springboot package and assemble it to the collection
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//Read the application listener under META-INF.spring.factories under the springboot package and assemble it to
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
 //2. Call the SpringApplication run implementation to start and return the context of the current container
(new SpringApplication(primarySources)).run(args)
//3. Record the spring boot time
StopWatch stopWatch = new StopWatch();
//4. Read the ApplicationListener under META-INF/spring.factories and assemble it to the collection
SpringApplicationRunListeners listeners = this.getRunListeners(args)
//5. Loop call to listen to the starting method (listener initialization, do some callback methods)
listeners.starting();
//6. Assign values to parameters
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//6.1 read the configuration file into our springboot container
listeners.environmentPrepared((ConfigurableEnvironment)environment)
//6.1.1
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
//6.1.2
this.multicastEvent(event, this.resolveDefaultEventType(event));
//6.1.3
this.invokeListener(listener, event);
//6.1.4
this.doInvokeListener(listener, event);
//6.1.5
listener.onApplicationEvent(event);

this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());

this.addPropertySources(environment, application.getResourceLoader());
//7. Read the configuration file and put it into the springboot container
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        RandomValuePropertySource.addToEnvironment(environment);
        (new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();
    }
this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true));
names.forEach((name) -> {    this.load(location, name, profile, filterFactory, consumer);});
this.load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
locations.addAll(this.asResolvedSet(ConfigFileApplicationListener.this.searchLocations, "classpath:/,classpath:/config/,file:./,file:./config/"));
//8. Print banner
Banner printedBanner = this.printBanner(environment);
//9. Create the spring boot context annotation configservletwebserver ApplicationContext
context = this.createApplicationContext();
case SERVLET:contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
 this.refreshContext(context);
  ((AbstractApplicationContext)applicationContext).refresh();
//10. Follow the spring refresh method
 public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

//11. Start to create web server

//12. Load springmvc

//13. Null method callback
this.afterRefresh(context, applicationArguments);
//14. Start to use the broadcast and callback mechanism to notify the listener that the SpringBoot container is started successfully
listeners.started(context);
//15. Start running the project using broadcast and callback mechanism
listeners.running(context);
//16. Return to current context
return context;

Reference: ant class: http://www.mayikt.com/

Tags: Programming Spring Tomcat SpringBoot Web Server

Posted on Mon, 04 Nov 2019 19:56:10 -0800 by marcusb