On SpringBoot's Auto-Configuration and Startup Process

Keywords: PHP Spring Tomcat SpringBoot xml

I. Introduction

Spring Boot simplifies the development of Spring applications. By adopting the idea that conventions are greater than configurations, it is easy to build an independent, product-level application with simplicity and simplicity.

1. Disadvantages of traditional J2EE development

Development is cumbersome, configuration is complex, development efficiency is low, deployment process is complex, third-party technology integration is difficult.

2. Advantages of SpringBoot

  • Quickly rebuild stand-alone Spring projects and integrate with mainstream frameworks.
  • With embedded Servlet containers, applications do not need to be packaged as WAR packages
  • starters Auto-Dependence and Version Control
  • A large number of automatic configuration, simplified development, can also modify its default values
  • No need to configure XML, no code generation
  • Runtime application monitoring in quasi-production environment
  • Natural Inheritance of Cloud Computing

3. Spring Boot HelloWorld description

1.starters

  • SpringBoot provides starters pom (starters) to simplify most scenarios of enterprise development. As long as starters pom of corresponding scenarios is introduced, most configuration of related technologies will be eliminated (field configuration), thus simplifying our development. We will use SpringBoot's Bean for our field configuration in our business.
  • These starters cover almost all the common scenarios in javaee, and SpringBoot has rigorously tested and versioned the jar s that these scenarios depend on.
  • The version of the jar package is defined in spring-boot-dependencies.

2. Entry class and @SpringBootApplication

  • The program starts with the main method.
  • Load the main program class using SpringApplication.run()
  • The main program class needs to be marked @SpringBootApplication
  • @ Enable AutoConfiguration is the core annotation
  • @ Import imports all automatic configuration scenarios
  • @ AutoConfiguration Package defines default package scanning rules.
  • Program startup scans the package of the main program class and the components of all the subpackages below

3. Automatic Configuration

Automatic Configuration of xxxAutoConfiguration

  • There are a lot of these classes in SpringBoot, and their purpose is to help us assemble automatically.
  • It registers and configures all the components required for this scenario into the container
  • They are in the META-INF/spring.factories file under the classpath
  • spring-boot-autoconfigure.jar contains field configuration class code for all scenarios
  • These automatic configuration classes are the key to SpringBoot's automatic assembly.

2. SpringBoot configuration

1. Configuration file

  • SpringBook uses a global configuration file. The configuration file name is fixed.
    - application.properties or application.yml

  • The configuration file is placed in the src/main/resources directory or under the classpath/config.
  • The purpose of the global configuration file is to modify some default configurations

2. Configuration file value injection

  • @ Value and @Configuration Properties are compared for attribute injection values
Contrast point @ConfigurationProperties @Value
function Bulk injection of attributes in configuration files Specify one by one
Loose Binding (Loose Grammar) Support I won't support it
SpEL I won't support it Support
JSR303 Data Check Support I won't support it
Complex type encapsulation Support I won't support it
  • Property name matching rule
    - person.firstName uses a standard approach
    - person.first-name is used-
    - person.first_name is used_
    - PERSON_FIRST_NAME Recommendation System Properties Use this Writing Method

  • @PropertySource
    Load the specified configuration file

  • ConfigurationProperties
    - Combining with @Bean to assign attributes
    - Read the specified file in conjunction with @PropertySource (only for properties files).

  • ConfigurationProperties Validation
    - Support JSR303 for profile validation.

@Component
@PropertySource(value={"classpath:person.properties"})
@ConfigurationProperties(prefix="person")
@Validated
public class Person{
    @Email
    @Value("${person.email}")
    private String email;
}
  • ImportResource reads external configuration files

3. Configuration file placeholders

  • RandomValuePropertySource
    Random numbers can be used in configuration files
    -${random.value}
    -${random.int}
    -${random.long}
    -${random.int(10)}
    -${random.int[1024,65536]}

  • Property configuration occupier
    - You can refer to the properties previously configured in the configuration file (you can use them here before Y priority).
    - ${app.name: default value} to specify the default value when an attribute cannot be found.
app.name=MyApp    
app.description=${app.name} is a SpringBoot Application

4.profile

profile is Spring's support for different configurations in different environments. It can switch environments quickly by activating specified parameters.
#### 1. Multi-profile file format

  • Format: application-{profile}.properties/yml
    application-dev.properties,application-prod.properties

    2. Multi-profile Document Block Patterns

spring.profiles.active=prod #Activate the specified configuration
spring.profiles=prod
server.port=80
# Default represents the default configuration when not specified
spring.profiles=default
server.port=8080

3. Activation modes

  • Command line: -- spring.profiles.active=dev
  • Configuration file: spring.profiles.active=dev
  • jvm parameter: - Dspring.profiles.active=dev

5. Configuration file loading location

SpringBoot startup scans the application.properties or application.yml file of the location as the default configuration file for SpringBoot.
- file:./config/
- file:./
- classpath:/config/
-classpath:/
- The above is in the order of priority from high to low. Files in all locations will be loaded, and the high priority configuration content will cover the low priority configuration content.
- You can change the default configuration by configuring spring.config.location.

6. Loading order of external configuration

  1. Command line parameters
  2. JNDI attributes from java: comp/env
  3. Java System Properties (System.getProperties())
  4. Operating system environment variables
  5. RandomValuePropertySource Configuration random. * Property Value
  6. application-{profile}.properties or application. YML (with spring.profile) configuration file outside the jar package
  7. application-{profile}.properties or application. YML (with spring.profile) configuration files inside the jar package
  8. application.properties or application. YML (without spring.profile) configuration files outside the jar package
  9. application.properties or application. YML (without spring.profile) configuration files inside the jar package
  10. @ The @PropertySource on the Configuration annotation class.
  11. Default properties specified by SpringApplication.setDefaultproperties.

7. Principle of Automatic Configuration

1.SpringBoot loads the main configuration class at startup, and turns on the automatic configuration function @EnableAutoConfiguration

2. @Enable AutoConfiguration

  • Use Enable AutoConfiguration Import Selector to import some components into the container.
  • Add all EnableAutoConfiguration values configured in META-INF/spring.factories to the container.

3.@Conditional Derived Annotations

@ Conditional Extension Annotation Function (judging whether the specified conditions of the current period are met)
@ConditionalOnJava Does the java version of the system meet the requirements
@ConditionalOnBean The specified Bean exists in the container
@ConditionalOnMissingBean No specified Bean exists in the container
@ConditionalOnExpression Satisfies SpEL expression specification
@ConditionalOnClass There are specified classes in the system
@ConditionalOnMissingClass There are no specified classes in the container
@ConditionalOnSingleCandidate There is only one specified Bean in the container, or this Bean is the preferred Bean.
@ConditionalOnProperty Does the property specified in the system have a specified value?
@ConditionalOnResource Is there a specified resource file in the class path
@ConditionalOnWebApplication The current web Environment
@ConditionalOnNotWebApplication Currently, it is not a web environment
@ConditionalOnJndi JNDI has specified items
  • Function: The condition specified by @Conditional must be established before adding components to the container and configuring all the contents in the container will take effect.

3. SpringBoot and Log

1. Logging Framework

There are many log frameworks in the market, such as JUL(java.util.logging), JCL(Apache Commons Logging), Log4J, Log4J2, Logback, SLF4j, jboss-logging, etc.

  • SpringBook uses JCL internally in its early framework. spring-boot-starter-logging takes the form of slf4j+logback. Spring Boot can also be automatically configured (jul, log4j2, logback) and simplified.
Log facade Log implementation
JCL,SLF4J,jboss-logging log4j,JUL,Log4j2,Logback
Log system configuration file
Logback logback-spring.xml, logback-spring.groovy, logback.xml or logback.groovy
Log4j2 log4j2-spring.xml,log4j2.xml
JUL logging.properties

  • Conclusion:
    1. The SpringBook underlying layer also uses slf4j+logback for logging.
    2. SpringBook also replaced all other logs with slf4j.
    3. If other logging frameworks are introduced, commons-logging dependencies of Spring frameworks should be excluded.

IV. Web Development

1. Mapping rules of SpringBoot for static resources

  • For all / webjars /**, go to classpath:/META-INF/resources/webjars/ for resources.
  • If /** accesses any resources of the current project, go to the (folder of static resources) to find the mapping.
  • Welcome pages; all index.html pages under the static resource folder are mapped by "/**".
  • All **/favicon.ico is found under static resource files.

2. Spring MVC automatic configuration

1.SpringMVC auto-configuration

SpringBoot's default configuration for Spring MVC (WebMvc AutoConfiguration) is as follows:

  • Contains Content Negotiating ViewResolver and BeanNameViewResolver.
    • ViewResolver is automatically configured
    • Content Negotiating ViewResolver: Combine all view parsers
- Support static resources, including support for Wenjars
 - Static Home Page Access
 - Support for favicon.ico
 - Converter, GenericConverter, Formatter are automatically registered.  
     - Converter: Converter.
     - Formatter: Formatter
  • Support for HttpMessageConverters
    • HttpMessageConverter: Spring MVC is used to convert Http requests and responses.
    • HttpMessageConverters: Determine from the container and get all HttpMessageConverters;
  • MessageCodes Resolver is automatically injected, and error code generation rules are defined.
  • Automatically use Configurable Web Binding Initializer.

2. Extending Spring MVC

Write a configuration class (@Configuration), which is a WebMvc Configurer Adapter type and cannot be annotated with the @Enable WebMvc annotation

@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/desperado").setViewName("success");
    }
}

principle

  1. WebMvc AutoConfiguration is an automatic configuration class for Spring MVC.
  2. Import when doing other automatic configurations.
  3. All WebMvcConfigurer s in the container are registered together.
  4. Our custom configuration class will also be invoked.

3. Take over Spring MVC

If you want to invalidate Spring MVC's automatic configuration, you just need to add @Enable WebMvc annotation to our custom configuration class.

@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/desperado").setViewName("success");
    }
}

principle

  1. @ Annotations to Enable WebMvc
@Import(DelegatingWebMvcConfiguation.class)
public @interface EnableWebMvc{}
  1. DelegatingWebMvcConfiguation
@Configuration
public class DelegatingWebMvcConfiguation extend WebMvcConfigurationSupport{}
  1. WebMvcAutoConfiguration
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({Servlet.class,DispatcherServlet.class,
                      WebMvcConfigurerAdapter.class})
//Without this component in the container, the automatic configuration class will take effect
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class,
                ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration{}
  1. @ Enable WebMvc imports WebMvc Configuration Support components.
  2. Importing WebMvc Configuration Supports is only the most basic function of Spring MVC.

4. Modify the default configuration

  1. When SpringBoot configures many components automatically, does the graphics card container have user configurations (@Bean,@Component)? If it has user configurations, it will configure automatically if it does not; if some components can have multiple components, it will combine user configurations with its default.
  2. There are many xxxConfigurer s in SpringBook that help us with extended configuration
  3. There are many xxxCustomizer s in SpringBook that help us customize our configuration

5. Default Access to Home Page

Configuration using custom WebMvc Configuration Adapter

//Spring MVC can be extended with WebMvc Configuration Adapter
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //Browsers send / desperado requests to success
        registry.addViewController("/desperado").setViewName("success");
    }

    //All webMvc Configurer Adapter components work together
    @Bean   //Register components into containers
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                //Pages that configure default paths
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/index.html").setViewName("login");
            }
        };
        return adapter;
    }
}

6. internationalization

1. Write an internationalized configuration file
Write configuration files in different languages, such as login.properties, login_en_US.properties, login_zh_CN.properties, etc.

  1. SpringBook automatically configures components to manage international resource files.
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
    private static final Resource[] NO_RESOURCES = new Resource[0];

    public MessageSourceAutoConfiguration() {
    }

    @Bean
    @ConfigurationProperties(
        prefix = "spring.messages"
    )
    public MessageSourceProperties messageSourceProperties() {
        return new MessageSourceProperties();
    }

    @Bean
    public MessageSource messageSource(MessageSourceProperties properties) {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        if (StringUtils.hasText(properties.getBasename())) {
            //Set the base name of the internationalized resource file (remove the language country code)
      
                messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
        }

        if (properties.getEncoding() != null) {
            messageSource.setDefaultEncoding(properties.getEncoding().name());
        }

        messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
        Duration cacheDuration = properties.getCacheDuration();
        if (cacheDuration != null) {
            messageSource.setCacheMillis(cacheDuration.toMillis());
        }

        messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
        messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
        return messageSource;
    }

principle
Locale is acquired according to the regional information brought by the request header for internationalization.

5. Error handling mechanism

1. Default error handling mechanism

  1. Browser, return a default error page by default.
  2. Other clients respond to a json data by default.

principle

  1. Get information about the error page in Default Error Attributes
public class DefaultErrorAttributes implements ErrorAttributes {
 
    //Get information about the error page
    public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        Map<String, Object> errorAttributes = new LinkedHashMap();
        errorAttributes.put("timestamp", new Date());
        errorAttributes.put("path", request.path());
        Throwable error = this.getError(request);
        HttpStatus errorStatus = this.determineHttpStatus(error);
        errorAttributes.put("status", errorStatus.value());
        errorAttributes.put("error", errorStatus.getReasonPhrase());
        errorAttributes.put("message", this.determineMessage(error));
        this.handleException(errorAttributes, this.determineException(error), includeStackTrace);
        return errorAttributes;
    }
}
  1. Processing/error requests in Basic Error Controller
@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
   
    //Generate html-type data, and browsers send requests to process this method
    @RequestMapping(
        produces = {"text/html"}
    )
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
       //Which page to go as the error page? Contains page address and page content
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

   //Generate json data, and other clients come to this method for processing
    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = this.getStatus(request);
        return new ResponseEntity(body, status);
    }
}
  1. Error Page Customizer for misconfiguration
public class ErrorProperties {
    @Value("${error.path:/error}")
    private String path = "/error";
    private boolean includeException;
    private ErrorProperties.IncludeStacktrace includeStacktrace;
    private final ErrorProperties.Whitelabel whitelabel;
}
  1. Error Mvc AutoConfiguration Generates Error Pages
public class ErrorMvcAutoConfiguration {
    
    private static class StaticView implements View {
        private static final Log logger = LogFactory.getLog(ErrorMvcAutoConfiguration.StaticView.class);

        private StaticView() {
        }

        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            if (response.isCommitted()) {
                String message = this.getMessage(model);
                logger.error(message);
            } else {
                StringBuilder builder = new StringBuilder();
                Date timestamp = (Date)model.get("timestamp");
                Object message = model.get("message");
                Object trace = model.get("trace");
                if (response.getContentType() == null) {
                    response.setContentType(this.getContentType());
                }

                builder.append("<html><body><h1>Whitelabel Error Page</h1>").append("<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>").append("<div id='created'>").append(timestamp).append("</div>").append("<div>There was an unexpected error (type=").append(this.htmlEscape(model.get("error"))).append(", status=").append(this.htmlEscape(model.get("status"))).append(").</div>");
                if (message != null) {
                    builder.append("<div>").append(this.htmlEscape(message)).append("</div>");
                }

                if (trace != null) {
                    builder.append("<div style='white-space:pre-wrap;'>").append(this.htmlEscape(trace)).append("</div>");
                }

                builder.append("</body></html>");
                response.getWriter().append(builder.toString());
            }
        }

        private String htmlEscape(Object input) {
            return input != null ? HtmlUtils.htmlEscape(input.toString()) : null;
        }

        private String getMessage(Map<String, ?> model) {
            Object path = model.get("path");
            String message = "Cannot render error page for request [" + path + "]";
            if (model.get("message") != null) {
                message = message + " and exception [" + model.get("message") + "]";
            }

            message = message + " as the response has already been committed.";
            message = message + " As a result, the response may have the wrong status code.";
            return message;
        }

        public String getContentType() {
            return "text/html";
        }
    }

5.DefaultErrorViewResolver parsing page

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
  
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
        }

        return modelAndView;
    }

    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //Find a page by default, error/404
        String errorViewName = "error/" + viewName; 
        //Template engine can parse this page address, so template engine parses it.
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
        //Return to the view address specified by errorViewName when the template engine is available
        //Template engine is not available, just find the corresponding page under the static resource folder
        return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
    }

2. Priority of error pages (custom error pages)

  1. In the case of template engine, error/status code (named error status code for the error page. html is placed under the error folder in the template engine folder), the error of this status code will come to the corresponding page.
  2. If there is no template engine (the template engine can't find the error page), look under the static resource folder.
  3. None of the above error pages is the default error prompt page for SpringBoot.

3. How to customize wrong json data

  1. Custom exception handling & returns custom json data (no adaptive effect)
@ControllerAdvice
public class MyExceptionHandler  {
    
    @ResponseBody
    @ExceptionHandler(CustomException.class)
    public Map<String,Object> handleException(Exception e){
        HashMap<String, Object> map = new HashMap<>();
        map.put("code","error message");
        map.put("message",e.getMessage());
        return map;
    }
}

2. Forwarding to/error for adaptive response effect processing.

@ControllerAdvice
public class MyExceptionHandler  {

    @ResponseBody
    @ExceptionHandler(CustomException.class)
    public String handleException(Exception e, HttpServletRequest request){
        HashMap<String, Object> map = new HashMap<>();
        //Pass in our own error status code, otherwise we won't go into the parsing process of customized error pages.
        request.setAttribute("java.servlet.error.status_code","500");
        map.put("code","error message");
        map.put("message",e.getMessage());
        //Forwarding to / error
        return "forward:/error";
    }
}

4. Send out customized data

After an error occurs, the return to / error request is processed by BasicErrorController, and the data that can be retrieved in response is retrieved by getErrorAttributes.

  1. Write an implementation class of ErrorController (or a subclass of AbstractErrorController) and put it in a container.
  2. The data available on the page or returned by json is obtained through errorAttributes.getErrorAttributes; DefaultErrorAttributes.getErrorAttributes() in the container defaults to data processing.
//Add custom ErrorAttributes to the container
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        //Get the map of ErrorAttributes
        Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
        //Add your own property field
        map.put("name","desperado");
        return map;
    }
}

Configuration of Embedded Servlet Container

SpringBoot defaults to Tomcat as an embedded Servlet container

1. Modify the configuration of the Servlet container

Modify the server-related configuration in the configuration file application file.

server.port=8081
server.context_path=/crud
server.tomcat.uri-encoding=utf-8

2. Configuration of customized Servlet containers

Write an Embedded Servlet Container Customizer (using Web Server Factory Customizer in 2.x) to modify the configuration of the Servlet container.

@Bean
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
        return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){
            @Override
            public void customize(ConfigurableWebServerFactory factory) {
                 factory.setPort(8081);
            }
        };
    }

3. Register three components of Servlet

Because SpringBoot is the default way to start the embedded Servlet container in the jar package to start SpringBoot's web application, there is no web.xml file. So the way to register Servlet, Filter and Listener is different.

1. Injecting Servlet

    @Bean
    public ServletRegistrationBean<MyServlet> myServlet(){
        ServletRegistrationBean<MyServlet> registrationBean = 
                new ServletRegistrationBean<>(new MyServlet(), "/myServlet");
        return registrationBean;
    }

2. Injecting Filter

    @Bean
    public FilterRegistrationBean<MyFilter> myFilter(){
        FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new MyFilter());
        registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
        return registrationBean;
    }

3. Injecting Listener

    @Bean
    public ServletListenerRegistrationBean<MyListener> myListener(){
        ServletListenerRegistrationBean<MyListener> registrationBean =
                new ServletListenerRegistrationBean<>(new MyListener());
        return registrationBean;
    }

4. Replace with other embedded Servlet containers

Replacing other servlets is very simple, just introduce their dependencies into the pom and then eliminate tomcat's dependencies.

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

5. Principle of Automatic Configuration of Embedded Servlet Containers

  1. Embedded Servlet Container AutoConfiguration (2.x corresponding to Servlet Web Server Factory Configuration): Automatic configuration of embedded containers
@Configuration
class ServletWebServerFactoryConfiguration {
    ServletWebServerFactoryConfiguration() {
    }

    @Configuration
    @ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedUndertow {
        public EmbeddedUndertow() {
        }

        @Bean
        public UndertowServletWebServerFactory undertowServletWebServerFactory() {
            return new UndertowServletWebServerFactory();
        }
    }

    @Configuration
    @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedJetty {
        public EmbeddedJetty() {
        }

        @Bean
        public JettyServletWebServerFactory JettyServletWebServerFactory() {
            return new JettyServletWebServerFactory();
        }
    }

    @Configuration
   //Determine whether tomcat dependencies are currently introduced
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class}) 
    /// Determine that the current container does not have a user-defined Servlet Web ServerFactory:
    //Embedded Servlet Container Factory; Role: Create Embedded Servlet Containers

    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedTomcat {
        public EmbeddedTomcat() {
        }

        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    }
}
  1. Embedded Servlet Container Factory

  1. Embedded Servlet container

4. Take tomcat as an example

public WebServer getWebServer(ServletContextInitializer... initializers) {
        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 thisb.getTomcatWebServer(tomcat);
    }
  1. Import Web Server Factory Customizer Bean PostProcessor into the container
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
  
     //Before initialization   
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //If the current initialization is a component of type WebServerFactory
        if (bean instanceof WebServerFactory) {
            this.postProcessBeforeInitialization((WebServerFactory)bean);
        }

        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
       //  Get all the customizers and call the customize method of each customizer to assign attributes to the servlet container
        ((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
            customizer.customize(webServerFactory);
        });
    }

    private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
        if (this.customizers == null) {
            // Customize the Servlet container to add a component of type WebServerFactoryCustomizer to the container
            this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());
            this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
            this.customizers = Collections.unmodifiableList(this.customizers);
        }

        return this.customizers;
    }

    private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
      //Get all the components of this type from the container: WebServerFactoryCustomizer
        return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
    }
}

summary

  1. SpringBoot adds the corresponding WebServer Factory to the container according to the import dependencies.
  2. To create an object, a component in the container uses a WebServer Factory Customizer Bean PostProcessor postprocessor, which processes as long as it is an embedded Servlet factory.
  3. The post processor fetches all WebServer FactoryCustomizer from the container and calls the customization method of the customizer.

6. Embedded Servlet Container Startup Process

  1. SpringBoot starts running the run method.
  2. Call refreshContext(context); refresh IOC container (create IOC container, initialize container, create each component in container); create Annotation Config Servlet Web Server Application Context if a web application, and Annotation Config Reactive Web Application Context if a reactive application, otherwise create Annotation Config Reactive Web Application Context.
 protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }
  1. Call refresh(context); refresh the IOC container created above
 public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            //context to be refreshed
            this.prepareRefresh();
            //Call subclasses to refresh internal instance factories
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //Instance factory to be used in this context
            this.prepareBeanFactory(beanFactory);

            try {
                
                // Allows post-processing of bean factories in context subclasses.
                this.postProcessBeanFactory(beanFactory);
                //Call the factory processor registered as a bean in context.
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //Register the bean processor created by the intercepting bean.      
                this.registerBeanPostProcessors(beanFactory);
                //Initialize the message source for this context
                this.initMessageSource();
                //Initialize the event multicast for this context.
                this.initApplicationEventMulticaster();
                //Initialize other special bean s in a specific context subclass.
                this.onRefresh();
                // Check listener bean s and register them.
                this.registerListeners();
                // Instantiate all remaining (non-delayed initialization) singletons.
                this.finishBeanFactoryInitialization(beanFactory);
                //Last step: Publish the corresponding events.
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                //Destroy the created singletons to avoid occupying resources.
                this.destroyBeans();
                //Reset the'active'flag
                this.cancelRefresh(var9); 
                //Propagate exception to caller.
                throw var9;
            } finally {
              //Starting with us, reset the common introspective caches in the Spring core
              //The metadata of a single bean may no longer be needed.
                this.resetCommonCaches();
            }

        }
    }
  1. Call onRefresh (); the IOC container of the web overrides the onRefresh method.
  2. Web IOC containers create embedded Servlet containers.
private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            ServletWebServerFactory factory = this.getWebServerFactory();
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var4) {
                throw new ApplicationContextException("Cannot initialize servlet context", var4);
            }
        }

        this.initPropertySources();
    }
  1. Get the embedded Servlet container factory: Servlet WebServerFactory = this. getWebServerFactory (); Get the Servlet WebServerFactory component from the IOC container;

  2. Get the embedded Servlet container using the container factory: his. webServer = factory. getWebServer (new Servlet Context Initializer []{this. getSelfInitializer ()});

public WebServer getWebServer(ServletContextInitializer... initializers) {
        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);
    }
  1. The embedded Servlet container creates and starts the Servlet container.

9. Start the embedded Servlet container first, then fetch the remaining uncreated objects in the IOC container, and the embedded Servlet container will be created when the IOC container starts.

7. Use an external Servlet container

1. Advantages and disadvantages of embedded Servlet container

Advantages: Simple and convenient.
Disadvantage: JSP is not supported by default, and optimizing customization is complex.

2. Using external Servlet container steps

  1. You must create a war project.
  2. Specify the embedded Tomcat as provided.
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring‐boot‐starter‐tomcat</artifactId>
    <scope>provided</scope>
</dependency>
  1. You must write a subclass of SpringBootServletInitializer and call the configure method.
public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        //Main program imported into SpringBoot application
        return application.sources(SpringBoot04WebJspApplication.class);
  }
}
  1. Start the server.

3. Principles and Rules

principle
Start the server, the server starts the SpringBoot application [SpringBoot Servlet Initializer], and starts the IOC container.

rule

  1. Server startup creates a Servlet Container Initializer instance in each jar package in the current web application.
  2. The implementation of Servlet Container Initializer is placed under the META-INF/services file of the jar package. There is a file named javax. servlet. Servlet Container Initializer, which contains the full class name of the Servlet Container Initializer implementation class.
  3. You can also use @HandlerType to load the specified class when starting the application.

4. Start up the process

  1. Start tomcat.
  2. Load the javax. servlet. Servlet Container Initializer file under META-INF/services under the spring-web package.

  1. Spring Servlet Container Initializer passes all classes of this type labeled with @HandlerType into Set s with onStartup methods to create instances for these classes of Web Application Initializer type.
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }

    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList();
        Iterator var4;
        if (webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class<?> waiClass = (Class)var4.next();
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            var4 = initializers.iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                initializer.onStartup(servletContext);
            }

        }
    }
}
  1. Each Web Application Initializer calls its own onStartup() method.

  1. The class equivalent to SpringBootServletInitializer is created and the onStartup() method is executed.

  2. When the SpringBootServletInitializer instance executes onStartup, crateRootApplication Context creates the container.

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
        // 1. Create Spring Application Builder
        SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
        builder.main(this.getClass());
        ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
        if (parent != null) {
            this.logger.info("Root context already created (using as parent).");
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
            builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
        }

        builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
        builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
        // 2. Call the configure method, and the subclass reappears the method, passing in SpringBook's main program class
        builder = this.configure(builder);
        builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
        // 3. Create a Spring application using builder
        SpringApplication application = builder.build();
        if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
            application.addPrimarySources(Collections.singleton(this.getClass()));
        }

        Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
        //Ensure that error pages are registered
        if (this.registerErrorPageFilter) {
            application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
        }
        // 4. q Start Spring Application
        return this.run(application);
    }
  1. Spring's application starts and creates an IOC container.

  2. Start the Servlet container first, then the SpringBook application.

Posted by ask9 on Tue, 04 Jun 2019 11:46:48 -0700