(1) Introduction

SpringBoot is the basic step for Web development

① Create a SpringBoot application and select the required module.

② SpringBoot will default to the required environment configuration, only a small amount of configuration through the configuration file can make the project run;

③ Write the main business code;

When configuring SpringBoot, you can configure the configuration items and content by referring to the Automatic configuration principles of SpringBoot.

SpringBoot automatic configuration principle use:

What is SpringBoot already configured in this scenario?

2 Can the configured scenarios be modified? What can I change?

③ Can you extend the current scenario?

XxxAutoConfiguration: automatic configuration class in SpringBoot, help us to automatically configure components in the container;

XxxProperties: the corresponding configuration property class in the automatic configuration class, which encapsulates the content of the configuration file;

(2) SpringBoot mapping rules for static resources

1. Path for storing static resources

WebMvcAutoConfiguration: The WebMVC automatic configuration class, responsible for the automatic configuration of WebMVC

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {...// Configure the default static folder location
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
        if (!this.resourceProperties.isAddMappings()) {
            logger.debug("Default resource handling disabled");
            return;
        }
        Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
        CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
        // Configure the mapping path for static resources of the WebJars type
        if(! registry.hasMappingForPattern("/webjars/**")) {
            customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                                                 .addResourceLocations("classpath:/META-INF/resources/webjars/")
                                                 .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }
        String staticPathPattern = this.mvcProperties.getStaticPathPattern();
        // Configure the mapping path for common static resources
        if(! registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); }}...// Configure the welcome page lookup path
        @Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
        WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
            new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
            this.mvcProperties.getStaticPathPattern());
        welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
        welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
        returnwelcomePageHandlerMapping; }... }Copy the code

As you can see from the source code, static resources of the webjars type can be found in classpath:/ meta-INF /resources/webjars/

Webjars: Introduce front-end static resources through JAR packages

Blog about Webjars: Introduction to WebJars

The addResourceHandlers method in the WebMvcAutoConfiguration class calls the getStaticLocations method to get the static resource mapping path configured in the ResourceProperties property configuration class.

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {

    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/"."classpath:/resources/"."classpath:/static/"."classpath:/public/" };

    /** * Locations of static resources. Defaults to classpath:[/META-INF/resources/, * /resources/, /static/, /public/]. */
    privateString[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS; .public String[] getStaticLocations() {
        return this.staticLocations; }...// Configure path mapping for the welcome page
        @Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
        WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
            new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
            this.mvcProperties.getStaticPathPattern());
        welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
        welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
        return welcomePageHandlerMapping;
    }

    // Get the path to the welcome page
    private Optional<Resource> getWelcomePage(a) {
        String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
        return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
    }

    // Get the welcome page index.html file
    private Resource getIndexHtml(String location) {
        return this.resourceLoader.getResource(location + "index.html"); }... }Copy the code

Through the above source code analysis can be clearly obtained, static resource search file directory:

"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
Copy the code

2. Static resource path mapping

Static resources and storage paths

All the resources in webjars/** URI correspond to classpath:/ meta-INF /resources/webjars/ The browser URI localhost:8080/webjars/** corresponds to the resource path CLASspath :/ meta-INF /resources/webjars/

For example, use Webjars to import jquery and access it in a browser using the same URI

1) Configure pom. XML to import jquery

<! Create jQuery from webJAR
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.2.0</version>
</dependency>
Copy the code

2) Package structure after import

3) Enter the URI access in the browser

② All resources in the browser /** URI correspond to resources in the following paths

classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
Copy the code

For example, create hello. HTML in the static folder and access it in the browser

1) The file path in hello.html

2) The browser’s access URI

③ Configure the welcome page; All index. HTML in the static resource path is mapped to the browser’s /** path

For example, create index. HTML in the public directory and access it in the browser

1) The directory where the index. HTML file is located

2) The browser accesses the URI

(3) template engine

1. Introduction to the template engine

The goal of a template engine is to separate the display from the data. There are many types of template engines such as JSP, Velocity, Freemarker, Thymeleaf, and so on, but the template engine essentially combines template files and data to produce the final HTML code.

SpringBoot integrates Thymeleaf as a template engine. However, in the mainstream environment where the front and back ends are separated, more and more companies choose to provide services with a back end and multiple front ends in order to adapt to the flexible environment, and the use scenarios of template engines serving single applications become less and less. The Thymeleaf template engine is only briefly introduced and used here.

A brief introduction to template engines

Thymeleaf template engine

Thymeleaf is a modern server-side Java templating engine for Web and standalone environments.

The main goal of Thymeleaf is to bring elegant natural templates to your development workflow -HTML that displays correctly in a browser and works as a static prototype, thereby enhancing collaboration across development teams.

With modules for the Spring Framework, lots of integration with your favorite tools, and the ability to plug in your own functionality, Thymeleaf is ideal for modern HTML5 JVM Web development — although it can do a lot of things.

Thymeleaf website

3. Grammar rules for Thymeleaf

① th:text: changes the text content of the current element. Where th: XXX can be placed in any HTML element, replacing the value of the native attribute.

Th: function of the XXX attribute

② Use expression in Thymeleaf

Simple Expressions :(expression syntax)
Variable Expressions: ${... } : get the variable value; OGNL grammar;
1) Get the property of the object and call the method
------------------------------------
2) Use built-in base objects:
#ctx : the context object.
#vars: the context variables.
#locale : the context locale.
#request : (only in Web Contexts) the HttpServletRequest object.
#response : (only in Web Contexts) the HttpServletResponse object.
#session : (only in Web Contexts) the HttpSession object.
#servletContext : (only in Web Contexts) the ServletContext object.
${session.foo}
------------------------------------
3) Built-in tool objects:
#execInfo : information about the template being processed.
#messages : methods for obtaining externalized messages inside variables expressions, in the
same Way as they would be obtained using #{... } syntax.
#uris : methods for escaping parts of URLs/URIs
#conversions : methods for executing the configured conversion service (if any).
#dates : methods for java.util.Date objects: formatting, component extraction, etc.
#calendars : analogous to #dates , but for java.util.Calendar objects.
#numbers : methods for formatting numeric objects.
#strings : methods for String objects: contains, startsWith, prepending/appending, etc.
#objects : methods for objects in general.
#bools : methods for boolean evaluation.
#arrays : methods for arrays.
#lists : methods for lists.
#sets : methods for sets.
#maps : methods for maps.
#aggregates : methods for creating aggregates on arrays or collections.
#ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
--------------------------------------
Selection Variable Expressions: *{... } : select expression: and ${} function is the same;
Supplement: Cooperate Th: object = "${session. The user} :
    <div th:object="${session.user}">
    <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
    <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
    <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
    </div>
Message Expressions: #{... } : Get internationalized content
    Link URL Expressions: @{... } : Define URL;
    @{/order/process(execId=${execId},execType='FAST')}
    Fragment Expressions: ~{... } : fragment reference expression
    <div th:insert="~{commons :: main}">... 
----------------------------------------
Literals
    Text literals: 'one text' , 'Another one! ',...
    Number Literals: 0, 34, 3.0, 12.3...
    Boolean literals: true , false
    Null literal: null
    Literal Tokens: One, SomeText, Main...
----------------------------------------
Text Operations :(text operations)
    String concatenation: +
    Literal substitutions: |The name is ${name}|
-----------------------------------------
Arithmetic (1) The operations of mathematics
    Binary Operators: +, *, /, %
    Minus Sign (unary operator) : ‐
----------------------------------------
Boolean (Boolean operations)
    Binary operators: and , or
    Boolean negation (unary operator): ! , not
----------------------------------------
Comparisons Equality :(comparison)
    Comparators: > , < , >= , <= ( gt , lt , ge , le )
    Equality operators: == , ! = ( eq , ne )
---------------------------------------
Conditional Operators: Conditional operators
    If ‐ then: (if) ? (then)
    If ‐ then ‐ else: (if) ? (then) : (else)
Default: (value) ? : (defaultvalue)
Special Tokens
    No ‐ Operation: _
Copy the code

The syntax of Thymeleaf is not completely covered here, but those who are interested should refer to the official documentation: Thymeleaf Use Reference

4. Use of Thymeleaf

Thymeleaf template engine use:

① Introduce thymeleaf dependency in pom. XML file;

② Refer to the grammar template page of Thymeleaf;

(1) Import dependencies in POM. XML. Import dependencies without configuration. SpringBoot will automatically configure them

<! Thymeleaf -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Copy the code

In SpringBoot2.3.4.RELEASE, the integrated Thymeleaf version is 3.0.11.RELEASE is the latest version on the official website

② Write the template page success.html

<! DOCTYPEhtml>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>success</title>
    </head>
    <body>
        <h1>Success!!!</h1>
        <div th:utext="${hello}">21313</div>
        <div>
            <h3>my name is</h3>
            <h3 th:text="${name}"></h3>
            <hr/>
            <! -- th:each iteration generates the current tag: 3 h4 -->
            <h4 th:text="${user}"  th:each="user:${users}"></h4>
            <hr/>
            <h4>
                <a th:each="user:${users}"> [[${user}]] </a>
            </h4>
        </div>
    </body>
</html>
Copy the code

③ Write the method success that calls the template page

@Controller
public class HelloController {

    @ResponseBody
    @RequestMapping("/hello")
    public String hello(a) {
        return "hello";
    }

    @RequestMapping("/success")
    public String success(Map<String,Object> map){
        map.put("name"."zhangsan");
        map.put("hello"."< h3 > hello < / h3 >");
        map.put("users", Arrays.asList("zhangsan"."lisi"."wangwu"));
        return "success"; }}Copy the code

④ Start the project and access it in the browser

(4) SpringMVC automatic configuration

1. SpringMVC Auto-Configuration

Note in the official SpringBoot documentation:

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.

The auto-configuration adds the following features on top of Spring’s defaults:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
  • Support for serving static resources, including support for WebJars (covered later in this document)).
  • Automatic registration of Converter.GenericConverter, and Formatter beans.
  • Support for HttpMessageConverters (covered later in this document).
  • Automatic registration of MessageCodesResolver (covered later in this document).
  • Static index.html support.
  • Custom Favicon support (covered later in this document).
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

The original translation is as follows:

Spring Boot automatically configures SpringMVC to be used with other applications

Spring Boot Automatic Configuration In addition to the default configuration, the following features are configured:

  • containsContentNegotiatingViewResolverBeanNameViewResolverComponents;
  • Support for static resources, including support for Webjars;
  • Automatic registrationConverter.GenericConverter, andFormatterComponents;
  • supportHttpMessageConverters;
  • Automatic registrationMessageCodesResolver;
  • Static supportedindex.htmlPage;
  • user-enabledFavicon;
  • Automatically usingConfigurableWebBindingInitializerComponents;

If you want to preserve Spring Boot custom configurations and extend MVC custom configurations (interceptors, formatters, view parsers, and other features), you can add your own WebMvcConfigurer Configuration class annotated with @Configuration. And don’t take over SpringMVC with the @enableWebMVC annotation.

If you want to use a custom instance RequestMappingHandlerMapping, RequestMappingHandlerAdapter, Or ExceptionHandlerExceptionResolver and still use the Spring Boot MVC configuration, you can declare a WebMvcRegistrations type of bean, and use it to provide custom configuration to replace the existing configuration.

If you want to completely control the Spring MVC, you can add your own comments with @ EnableWebMvc Configuration class, or add your own comments with @ Configuration DelegatingWebMvcConfiguration, such as: As described in the Javadoc for @enableWebMVC.

Let’s take a look at some of the configurations mentioned:

1) ContentNegotiatingViewResolver: all view combined parser

@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
    // ContentNegotiatingViewResolver uses all the other view resolvers to locate
    // a view so it should have a high precedence
    resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return resolver;
}
Copy the code

Based on the method’s return value to the View object, the View object decides how to render (forward? Redirect?) , SpringBoot automates the configuration of the view parser

public interface ViewResolver {

    /**
	 * Resolve the given view by name.
	 * <p>Note: To allow for ViewResolver chaining, a ViewResolver should
	 * return {@code null} if a view with the given name is not defined in it.
	 * However, this is not required: Some ViewResolvers will always attempt
	 * to build View objects with the given name, unable to return {@code null}
	 * (rather throwing an exception when View creation failed).
	 * @param viewName name of the view to resolve
	 * @param locale the Locale in which to resolve the view.
	 * ViewResolvers that support internationalization should respect this.
	 * @return the View object, or {@code null} if not found
	 * (optional, to allow for ViewResolver chaining)
	 * @throws Exception if the view cannot be resolved
	 * (typically in case of problems creating an actual View object)
	 */
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;

}
Copy the code

③ Converter; Converters are used when converting between browsers and beans, and there are many converters configured for converting between types

For example, format 2020-11-11 as Date type data

@Bean
@Override
public FormattingConversionService mvcConversionService(a) {
    Format format = this.mvcProperties.getFormat();
    WebConversionService conversionService = new WebConversionService(new DateTimeFormatters()
                                                                      .dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));
    addFormatters(conversionService);
    return conversionService;
}
Copy the code

We can add custom formatters and converters just by putting them in containers.

⑤ HttpMessageConverter: SpringMVC is used to convert Http requests and responses, for example: converting entity classes into JSON arrays

HttpMessageConverters, determined from the container, gets all HttpMessageConverters

@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider
       
        > converters)
       > {
    return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}
Copy the code

So we can also add HttpMessageConverter to the container by registering our own components (@bean,@Component)

SpringBoot Web application Development official document: SpringMVC Configuration

2. The extension for SpringMVC

Configuration information required to use SpringMVC when SpringBoot is not used previously

<mvc:view-controller path="/hello" view-name="success"/>
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/hello"/>
        <bean></bean>
    </mvc:interceptor>
</mvc:interceptors>
<mvc:default-servlet-handler/>
Copy the code

Use configuration files to extend the SpringMVC functionality

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    /** * super.addViewControllers(registry); * The browser sends the /atguigu request to Success *@param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/Bruce").setViewName("ok"); }}Copy the code

Some blogs may refer to inheriting the WebMvcConfigurerAdapter class, but the WebMvcConfigurerAdapter was abandoned after Spring5.0 and replaced with the WebMvcConfigurer interface.

With SpringBoot we can write a Configuration class of type WebMvcConfigurer (annotated with @Configuration) without @enableWebMVC Annotations (annotating this means that our configuration takes over SpringBoot configuration) so that we can write configuration classes that both retain all of SpringBoot’s automatic configuration and use our extended configuration;

Both automatic and custom configurations can be retained for the following reasons:

WebMvcAutoConfigurationAdapter import EnableWebMvcConfiguration classes used to load the custom configuration information;

@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {... }Copy the code

EnableWebMvcConfiguration class inheritance DelegatingWebMvcConfiguration class in order to realize the load custom MVC configuration;

@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {... }Copy the code

DelegatingWebMvcConfiguration class contains setConfigurers method, can put all the custom configuration and SpringBoot automatically configure load at the same time, in order to realize the automatic configuration and custom configurations to take effect at the same time;

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if(! CollectionUtils.isEmpty(configurers)) {this.configurers.addWebMvcConfigurers(configurers); }}}Copy the code

Summary of loading configuration:

① WebMvcAutoConfiguration is an automatic configuration class for SpringMVC

(2) in the automatic configuration, will Import @ Import (EnableWebMvcConfiguration. Class)

③ All WebMvcConfigurer configurations in the container are loaded during configuration

④ Our self-written configuration classes are therefore loaded

Result: Automatic configuration of SpringMVC in SpringBoot and our custom configuration information will work

Automatic configuration and custom configuration inheritance diagrams

3. Take over SpringMVC

Instead of using SpringBoot’s automatic configuration of SpringMVC at all, we can use our own configuration content for SpringMVC.

We can add the @enableWebMVC annotation to our own configuration class, at which point the SpringBoot configuration for SpringMVC is completely invalidated, and only our own SpringMVC configuration will take effect.

@EnableWebMVCNote why SpringBoot autoconfiguration is disabled:

1) EnableWebMVC annotations in the imported DelegatingWebMvcConfiguration class

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
Copy the code

(2) and DelegatingWebMvcConfiguration class inherited WebMvcConfigurationSupport, WebMvcConfigurationSupport contains only the basic configuration for SpringMVC

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {... }Copy the code

Conditions, (3) and WebMvcAutoConfiguration configuration class contains annotations @ ConditionalOnMissingBean (WebMvcConfigurationSupport. Class)

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {... }Copy the code

Conclusion:

  • @EnableAnnotations imported directly from theDelegatingWebMvcConfigurationClass inheritsWebMvcConfigurationSupportClass;
  • whileWebMvcAutoConfigurationConfigure conditional annotations in a class@ConditionalOnMissingBeanRequirement not to includeWebMvcConfigurationSupportType bean;
  • So mark@EnableThe annotation’s custom configuration class makesWebMvcAutoConfigurationAutomatic configuration class failure, that is, SpringBoot to SpringMVC automatic configuration failure;

4. Modify the default SpringBoot configuration

How SpringBoot loads components:

When SpringBoot automatically configits components, it checks whether there are any user-configured components in the container, i.e. configuration classes with @bean and @Component annotations.

An extended annotation implementation that depends on the @conditition annotation to load components;

SpringBoot also supports the combination of automatic and user configuration for components such as the ViewResovler component;

Basic idea:

XxxAutoConfiguration: SpringBoot automatic configuration class, configuration of related components, configuration class annotation can help us understand the role and requirements of the configuration class;

XxxProperties: is the property class of the automatic configuration class in SpringBoot. The configuration items in the property class are things that we can configure in external configuration files.

XxxConfiguration: Extended configuration class in SpringBoot for extended configuration of automatic configuration. We can look at the composition of these extension configuration classes and write our own configuration classes to extend the SpringBoot configuration classes;

XxxCustomizer: a customizer class in SpringBoot that can be used to help us customize certain configurations;

(5) RestfulCRUD sample project

1. Set the default home page

We can inject a custom WebMvc configuration class into the container to extend the existing WebMvc configuration, and SpringBoot loads both automatic configuration and custom configuration. For example, write a custom configuration class to configure view mapping and modify the default home page access template information. The principle used is the principle we mentioned above for modifying the default configuration.

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    /** * All WebMVCConfiguration components are loaded together *@return* /
    @Bean
    public WebMvcConfigurer webMvcConfigurer(a) {
        /** * Add view mapping */
        WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                // Redirect the default access to the login template
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/login.html").setViewName("login");
                registry.addViewController("/main.html").setViewName("dashboard"); }};returnwebMvcConfigurer; }}Copy the code

2. Internationalization configuration

① Write internationalization configuration files;

(2) use ResourceBundleMessageSource management internationalized resource file;

(3) Use FMT :message to fetch internationalized content;

④ Click the link to switch internationalization

Main steps:

(1) Compile the internationalization configuration file and extract the configuration information to be displayed on the page

(2) SpringBoot automatic configuration management internationalized resource file MessageSourceAutoConfiguration configuration the configuration of internationalization content of components

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {...@Bean
	public MessageSource messageSource(MessageSourceProperties properties) {
		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
		if (StringUtils.hasText(properties.getBasename())) {
            The default loading location of internationalization files is set using the basebame attribute
			messageSource.setBasenames(StringUtils
				.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
		}
		if(properties.getEncoding() ! =null) {
            // Set the base name of the internationalized resource file (remove the language country code)
			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());
		returnmessageSource; }... }Copy the code

In the MessageSourceProperties property configuration file

public class MessageSourceProperties {

	/** * Comma-separated list of basenames (essentially a fully-qualified classpath * location), each following the ResourceBundle convention with relaxed support for * slash based locations. If it doesn't contain a package qualifier (such as * "org.mypackage"), it will be resolved from the classpath root. */
    // This property is the configuration information that configures the location property of the internationalization profile
	private String basename = "messages"; . }Copy the code

So we can configure the path of the internationalization language in the configuration file

server.port=9080
server.servlet.context-path=/crud


spring.thymeleaf.cache=false
Configure the default SpringBoot internationalization language switch path
spring.messages.basename=i18n.login
Copy the code

③ Modify the display of the login page and replace the display with the internationalization configuration attribute.

<! DOCTYPEhtml>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Signin Template for Bootstrap</title>
    <! -- Bootstrap core CSS -->
    <link th:href="@ {/ webjars/bootstrap / 4.0.0 / CSS/bootstrap CSS}" rel="stylesheet"/>
    <! -- Custom styles for this template -->
    <link th:href="@{/asserts/css/signin.css}" rel="stylesheet"/>
</head>

<body class="text-center">
<form class="form-signin" action="dashboard.html">
    <img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" alt="" width="72" height="72">
	<! Thymeleaf (Thymeleaf, Thymeleaf);
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
    <! -- Error message -->
    <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
    <label class="sr-only" th:text="#{login.username}">Username</label>
    <input type="text" name="username" class="form-control" placeholder="Username"
           th:placeholder="#{login.username}" required="" utofocus="">
    <label class="sr-only" th:text="#{login.password}">Password</label>
    <input type="password" name="password" class="form-control" placeholder="Password"
           th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" value="remember-me"> [[#{login.remember}]]
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
    <p class="mt-5 mb-3 text-muted">© 2017-2018</p>
	<! -- Configure click Chinese and English to switch display information, and configure display content using the information in the sent request header -->
    <a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">Chinese</a>
    <a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
</form>

</body>

</html>
Copy the code

4. Customize LocateResolver to switch international information by clicking the link

public class MyLocaleRrsovler implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        // huu'o'qu
        String l = request.getParameter("l");
        Locale locale = Locale.getDefault();
        if(! StringUtils.isEmpty(l)) { String[] split = l.split("_");
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {}}Copy the code

Load our custom LocateResolver component into the container in the custom configuration class MyMvcConfig

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    /** * All WebMVCConfiguration components are loaded together *@return* /
    @Bean
    public WebMvcConfigurer webMvcConfigurer(a) {
        /** * Add view mapping */
        WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/login.html").setViewName("login");
                registry.addViewController("/main.html").setViewName("dashboard"); }};return webMvcConfigurer;
    }

    /** * Load our custom LocateResolver component into the container *@return* /
    @Bean
    public LocaleResolver localeResolver(a) {
        return newMyLocaleRrsovler(); }}Copy the code

⑤ Final effect: click Chinese and English and the interface will change the display effect according to the language

Principle of international switching:

The AutoWebMvcConfiguration configures internationalization Locate and LocateResolver

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver(a) {
    
    if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
        return new FixedLocaleResolver(this.mvcProperties.getLocale());
    }
    // By default, Locale is obtained based on the information in the request header
    AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
    localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
    return localeResolver;
}
Copy the code

Inheritance relationship of LocateResolver

3. Log in and configure

Configuration mode:

1 Modify the configuration information on the page

② Write the controller related to login

③ Configure a login interceptor

(1) Modify the configuration information on the page. Add Action, that is, the request interface, and set the request mode to POST

<! DOCTYPEhtml>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Signin Template for Bootstrap</title>
    <! -- Bootstrap core CSS -->
    <link th:href="@ {/ webjars/bootstrap / 4.0.0 / CSS/bootstrap CSS}" rel="stylesheet"/>
    <! -- Custom styles for this template -->
    <link th:href="@{/asserts/css/signin.css}" rel="stylesheet"/>
</head>

<body class="text-center">
<! -- Configure the submit interface action to send post requests
<form class="form-signin" action="dashboard.html" th:action="@{/user/login}" method="post">
    <img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" alt="" width="72" height="72">
	<! Thymeleaf (Thymeleaf, Thymeleaf);
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
    <! -- Error message -->
    <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
    <label class="sr-only" th:text="#{login.username}">Username</label>
    <input type="text" name="username" class="form-control" placeholder="Username"
           th:placeholder="#{login.username}" required="" utofocus="">
    <label class="sr-only" th:text="#{login.password}">Password</label>
    <input type="password" name="password" class="form-control" placeholder="Password"
           th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" value="remember-me"> [[#{login.remember}]]
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
    <p class="mt-5 mb-3 text-muted">© 2017-2018</p>
	<! -- Configure click Chinese and English to switch display information, and configure display content using the information in the sent request header -->
    <a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">Chinese</a>
    <a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
</form>

</body>

</html>
Copy the code

② Configure LoginController. Set the login request rule

@Controller
public class LoginController {

    //@RequestMapping(value = "/user/login",method = RequestMethod.POST)
    @PostMapping(value = "/user/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        Map<String,Object> map, HttpSession session){
        if(! StringUtils.isEmpty(username) &&"123456".equals(password)){
            // If the login succeeds, the form can be redirected to the home page
            session.setAttribute("loginUser",username);
            return "redirect:/main.html";
        }else{
            // Login failed
            map.put("msg"."Username and password error");
            return  "login"; }}}Copy the code

③ Configure the LoginHandlerInterceptor to intercept requests from users who are not logged in

public class LoginHandlerInterceptor implements HandlerInterceptor {
    /** * Perform the operation before executing the target method */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("loginUser");
        if(user == null) {// No login, return to login page
            request.setAttribute("msg"."No permission, please log in first.");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }else{
            // Logged-in, release request
            return true; }}@Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}Copy the code

(4) Register the interceptor in the container in the custom configuration WebMvcConfig

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    /** * All WebMVCConfiguration components are loaded together *@return* /
    @Bean
    public WebMvcConfigurer webMvcConfigurer(a) {
        /** * Add view mapping */
        WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/index.html").setViewName("login");
                registry.addViewController("/main.html").setViewName("dashboard");
            }

            /** * Register interceptor *@param registry
             */
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                // Block all page requests except the login page. Only after login can you access all pages
                registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/ * *")
                        .excludePathPatterns("/index.html"."/"."/user/login"); }};return webMvcConfigurer;
    }

    /** * Load our custom LocateResolver component into the container *@return* /
    @Bean
    public LocaleResolver localeResolver(a) {
        return newMyLocaleRrsovler(); }}Copy the code

4. Restful requests for access

4.1 LIST of CRUD employees

URI: / Resource name/resource ID. CRUD operations are performed on resources using HTTP requests. Resource operations: Perform operations on resources by POST, DELETE, PUT, and GET. Corresponding to add, delete, modify, query.

Plain CRUD (discriminating operations by URI) RestfulCRUD
The query getEmployees employee—GET
add addEmployee? xxx employee—POST
Modify the updateEmployee? id=xxx&xxx=xxxx empoyee/{id}—PUT
delete deleteEmployee? id=1 employee/{id}—DELETE

Blog about Restful style introduction: Restful style

Employee CRUD request design

Request the functionality of the interface The request URI Request way
Query all employees emps GET
Query an employee (displayed on the modify page) emp/1 GET
Request to add page emp GET
Adding employee Information emp POST
Enter the modify page (query and display employee information at the same time) emp/1 GET
Modifying employee Information emp PUT
Delete an employee emp/1 DELETE

Thymeleaf public page extraction

1. Extract common fragments<div th:fragment="copy">
&copy; 2020 The Good Thymes Virtual Grocery
</div>2. Introduce public fragments<div th:insert="~{footer :: copy}"></div>~ {templatename: : the selector} : template name: : selector ~ {templatename: : fragmentname} : template name: : fragments of 3, the default effect: Insert: [[~{}]]; [[~{}]]; [[~{}]]; [[~{}]]; [(~ {})];Copy the code

Three th attributes introduced into public fragments:

Th: INSERT: Inserts the entire public fragment into the element introduced by the declaration

Th :replace: Replaces the element introduced by the declaration with a public fragment

Th :include: Includes the content of the introduced fragment in this tag

Code examples:

<footer th:fragment="copy">
    &copy; 2011 The Good Thymes Virtual Grocery
</footer>

<! -- Introduction mode -->
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>

<! - effect - >
<div>
    <footer>
        &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
</div>
<footer>
    &copy; 2011 The Good Thymes Virtual Grocery
</footer>
<div>
    &copy; 2011 The Good Thymes Virtual Grocery
</div>
Copy the code

The introduction of fragments can also pass parameters

<div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>
Copy the code

4.2 Page and interface design

Specific page and interface design information, more content, please see the specific code: Restful CRUD, staff basic CRUD