SpringBoot Web development and source code analysis

SpringBoot quick start For conceptual things,...
1.0 basic features of springboot
2.0 container function
3.0 introduction to automatic configuration principle
4.0 configuration file
1.0 spring MVC auto configuration overview
2.0 simple function analysis
3.0 request parameter processing
4.0 data response and content negotiation
5.0 view parsing and template engine
6.0 interceptors
7.0 file upload
SpringBoot quick start

For conceptual things, see: https://www.yuque.com/atguigu/springboot/rmxq85

Required environment: jdk8 & compatible with java14, maven 3.3+, idea 219.1.2

maven settings:

Go to maven's configuration file to modify and add:

<mirrors> <mirror> <id>nexus-aliyun</id> <mirrorOf>central</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror> </mirrors> <profiles> <profile> <id>jdk-1.8</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile> </profiles>

Step 1: create maven project

Step 2: import dependency in pom file:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>

step 3: create package and main class (also called main application) under src/main/java

// Tell springboot that this is a springboot application // Main program application @SpringBootApplication public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }

step 4: create a Controller under another package and write business code

/* You can use RestController instead @ResponseBody // Represents that this class returns a string @Controller */ @RestController public class HelloController { @RequestMapping("/hello") public String handle01() { return "hello SpringBoot"; } }

step 5: main method for directly running the main application

step 6: create an application.properties file under resource, which can be configured globally. See the official document for details

step 7: simplify deployment

By adding a plug-in, you can generate a jar package and execute it directly on the target server
(command line input: such as java -jar springboot01-1.0-SNAPSHOT.jar) note to cancel the Quick Edit mode of cmd

<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>

Under target /, SpringBoot packages us into a jar package, which contains the third-party jar package we need for runtime

SpringBoot auto configuration principle

1.0 basic features of springboot

1.1 dependency management

Dependency management <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> </parent> Its parent project <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.4.RELEASE</version> </parent> It declares the version number of almost all dependencies commonly used in development,Automatic version arbitration mechanism, So we don't need to declare the version number

1. By default, no version can be written when importing dependencies
2. To import jar s that are not version arbitrated, write the version number.

Custom modification version number

1,see spring-boot-dependencies It specifies the current dependent version 2,Rewrite the configuration in the current project <properties> <mysql.version>5.1.43</mysql.version> </properties>

Staters

A set of dependency descriptions is maven's dependency feature

1,See a lot spring-boot-starter-* : *It's some kind of scene 2,Just introduce starter,We will automatically introduce all the dependencies required for this scenario 3,SpringBoot All supported scenarios https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter 4,See *-spring-boot-starter: Scenario launcher for simplified development provided by the third party 5,The lowest level dependency of all scenario initiators: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>

1.2 automatic configuration

  • Automatically configure Tomcat

    • Introduce dependency
    • Configure Tomcat
  • Automatically configure spring MVC

    • The full set of components of spring MVC is introduced
      How to view the components of the IOC container:

      Main method under main program application: public static void main(String[] args) { // Return to IOC container ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); // View container component name String[] names = run.getBeanDefinitionNames(); for(String s : names) { System.out.println(s); } run.getBean("yang", User.class); // There is only one single instance of a component in the container }
    • Spring MVC common components (functions) are automatically configured

  • Automatically configure common Web functions, such as character encoding

  • Default package structure

    • All components of the package where the main program is located and its sub packages will be scanned by default
    • No previous package scan configuration is required
    • To change the scanning path, @ SpringBootApplication(scanBasePackages = "com.yangyu")
      Or @ ComponentScan specifies the scan path
@SpringBootApplication Equivalent to @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com.atguigu.boot") // The scan path can be specified here
  • Various configurations have default values
    • The default configuration is ultimately mapped to a class, such as MultipartProperties
    • The value of the configuration file will eventually be bound to each class, which will create objects in the container
  • Load all auto configuration items on demand
    • Very many starter s
    • As long as this scenario is introduced, the automatic configuration of this scenario will be enabled
    • All spring boot autoconfigure functions are in the spring boot autoconfigure package

2.0 container function

2.1 component addition

1. @Configuration

Spring adds components by creating classes, creating spring configuration files, and writing < beans > in xml files

How to add components to SpringBoot:

  • There is no dependency between configuration class components. Use Lite mode to accelerate the container startup process and reduce judgment

  • There are dependencies between configuration class components, and the method will be called to obtain the previous single instance component in Full mode

/* 1. @Bean Is to register components for containers. The default is single instance 2. The configuration class itself is also a component 3. proxyBeanMethods: The method of the code bean defaults to true Full(proxyBeanMethods = true) (Ensure that each @ Bean method is called many times, and the returned component is single instance) Lite(proxyBeanMethods = false) (How many times each @ Bean method is called, the returned component is newly created, and the container does not save the proxy object) Component dependencies must use the Full mode default. The other defaults to Lite mode */ @Configuration(proxyBeanMethods = true) // Tell SpringBoot that this is the default single instance of a configuration class public class MyConfig { // Full: no matter how many external calls are made to this component registration method in the configuration class, the single instance object in the previous registration container is obtained @Bean // Add a component to the container, take the method name as the component id, the return type is the component type, and the returned value is the instance of the component in the container public User user01() { return new User("01", 20); } @Bean("yang") public User yangyu() { return new User("yangyu", 20); } }

2. @Component @Controller @Service @Repository (applicable to labeling self written classes) @ Bean (applicable to labeling components in third-party packages)

3. @ComponentScan @Import (quickly import components into the container)

@Import() Automatically create these two types of components in the container. The name of the default component is the full class name @Import() @Configuration(proxyBeanMethods = false) public class MyConfig { }

ImportSelector: it is an interface that needs to be implemented by a custom class. The method that needs to be rewritten can return the full class name array of the components to be imported. The full class name of the class that needs to be returned can be obtained through its own logic, and then a full class name string group is returned. Note: in the config class, you need to @ import the class of the interface you implement, In this way, the corresponding instance will be automatically generated in the container. The class of the interface you implement will not generate an instance in the container

ImportBeanDefinitionRegistrar: it is also an interface that needs to be implemented. Call BeanDefinitionRegistry.registerBeanDefinition to manually register all beans that need to be added to the container

**Factorbean (provided by spring): * * is also an interface and needs to be implemented

4. @Conditional

Conditional assembly: component injection is performed when the specified conditions are met

ConditionOnBean: only when there is this component in the container

@ConditionalOnBean(name = {"user01", "myConfig"})

2.2 native configuration file import

1. @ImportResource

Native XML: Resources / beans.xml

@ImportResource("classpath:beans.xml") public class MyConfig {}

2.3 configuration binding

Read the contents of the properties file and package it into JavaBean s for use at any time;

Native java practices:

public class getProperties { public static void main(String[] args) throws FileNotFoundException, IOException { Properties pps = new Properties(); pps.load(new FileInputStream("a.properties")); Enumeration enum1 = pps.propertyNames();//Get the name of the configuration file while(enum1.hasMoreElements()) { String strKey = (String) enum1.nextElement(); String strValue = pps.getProperty(strKey); System.out.println(strKey + "=" + strValue); //Encapsulate into JavaBean s. } } }

1. @ConfigurationProperties

xxx.properties:

mycar.aaa=3 mycar.bbb=good

Car class:

@Component // Only the components in the container have the powerful functions provided by SpringBoot @ConfigurationProperties(prefix = "mycar") public class Car { private Integer aaa; private String bbb; }

**2. @EnableConfigurationProperties + @ConfigurationProperties **

If it is a third-party class, use @ EnableConfigurationProperties(Car.class) on the configuration class

  1. Enable Car configuration binding
  2. Automatically register the Car component into the container

It is different from the previous one: either add it to the container with @ Component or use @ EnableConfigurationProperties

3.0 introduction to automatic configuration principle

3.1 boot load auto configuration class

@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication{}

1. @SpringBootConfiguration
Where @ Configuration represents the current class and is a Configuration class

2. @Component
Specify which packages to scan. Refer to the Spring annotation

3. EnableAutoConfiguration

@AutoConfigurationPackage @Import() public @interface EnableAutoConfiguration {}
  1. @AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class) // Import a component into the container public @interface AutoConfigurationPackage {} // Use the Registrar to import a series of components into the container // Import all components under the specified package: under the package of MainApplication
  1. @Import()
1,utilize getAutoConfigurationEntry(annotationMetadata);Batch import some components into the container 2,call List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)Get all the configuration classes that need to be imported into the container 3,Using factory loading Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) Get all the components 4,from META-INF/spring.factories Location to load a file. By default, it scans all of our current systems META-INF/spring.factories Location file spring-boot-autoconfigure-2.3.4.RELEASE.jar It's also in the bag META-INF/spring.factories It's dead in the file spring-boot All configuration classes loaded in the container should be given as soon as they are started

3.2 turn on automatic configuration items on demand

Although all the automatic configurations of our 127 scenarios are loaded by default when they are started. xxxxAutoConfiguration
According to the Conditional assembly rule (@ Conditional), it will be configured as required.

3.3 source code analysis and modification of default configuration

@Bean @ConditionalOnBean(MultipartResolver.class) // There are components of this type in the container @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) // There is no component with this name multipartResolver in the container public MultipartResolver multipartResolver(MultipartResolver resolver) { // If the method labeled @ Bean passes in an object parameter, the value of this parameter will be found from the container. // SpringMVC multipartResolver. Prevent the file upload parser configured by some users from not conforming to the specification // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; } Add a file upload parser to the container;

SpringBoot will configure all components at the bottom by default. However, if the user has configured it himself, the user's priority shall prevail

@Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() {} If you want to match yourself: public CharacterEncodingFilter filter(){ return null; }

Summary:

  • SpringBoot loads all the autoconfiguration classes xxxxconfiguration first

  • Each automatic configuration class takes effect according to conditions, and will bind the value specified in the configuration file by default. The. xxxProperties are bound to the configuration file in xxxProperties

  • The effective configuration class will assemble many components in the container

  • As long as these components are in the container, they are equivalent to these functions

  • Customized configuration

    • Users directly replace the underlying components with @ Bean
    • The user can modify the value of the configuration file obtained by this component.

Xxxxxxautoconfiguration - > component - > xxxxproperties - > get the value - > application.properties

3.4 best practices

  • Introduce scenario dependency
    https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
  • View what you have configured (optional)
    • Analyze the source code and introduce the automatic configuration of the scene, which generally takes effect
    • Enter debug=true in application.properties to enable the automatic configuration report Negative matches \ Positive matches
  • Need to modify
    • Modify configuration items by referencing documents
      https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
      Analyze for yourself: which of the configuration files are bound by xxproperties
    • Custom add or replace components
      @Bean @Component
    • Customizer XXXCustomizer
Development skills LomBok

Simplify JavaBean development

<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> And in idea Search installation lombok plug-in unit
@NoArgsConstructor @AllArgsConstructor @Data @ToString @EqualsAndHashCode ============================================ // Simplified log development @Slf4j public class HelloController { @RequestMapping("/hello") public String handle01(){ log.info("The request came in...."); return "123" } }
dev-tools

Hot update (actually automatic restart)

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>

After modifying the item or page: Ctrl+F9

Spring Initailizr

IDEA can quickly create SpringBoot applications, check the scenes you need, and automatically generate pom files

4.0 configuration file

4.1 properties

4.2 yaml

It is still a markup language

It is very suitable for data centric configuration files

Basic syntax:

  • key: value; There is a space between K and V

  • Case sensitive

  • Use indentation to represent hierarchical relationships

  • tab is not allowed for indentation, only spaces are allowed

  • The number of indented spaces is not important, as long as the elements of the same level are aligned to the left

  • '#' indicates a comment

  • There is no need to quote the string. If you want to add, '' and '' means that the string content will be escaped / not escaped

Data type:

  • Literal: a single, non separable value. date,boolean,string,number,null

    k: v
  • Object: a collection of key value pairs. map,hash,set,object

    Inline writing: k: #No spaces #or k: k1: v1 k2: v2 k3: v3
  • Array: a set of values arranged in order: array, list, queue

    Inline writing: k: [v1,v2,v3] #perhaps k: - v1 - v2 - v3

Example:

# yaml represents the above objects person: userName: zhangsan boss: false birth: 2019/12/12 20:12:33 # private Date birth; age: 18 pet: # private Pet pet name: tomcat weight: 23.4 interests: [Basketball,Swimming] # private String[] interests; animal: # private List<String> animal; - jerry - mario score: # private Map<String, Object> score; english: first: 30 second: 40 third: 50 math: [131,140,148] chinese: salarys: # private Set<Double> salarys; - 9999.98 - 9999.99 allPets: # private Map<String, List<Pet>> allPets; sick: - - - name: A bug weight: 77.77 health: []

4.3 configuration tips

Custom class and configuration file binding are generally not prompted

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- Prevent typing in when packing --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
Web development

1.0 spring MVC auto configuration overview

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.
    • Content negotiation view parser and BeanName view parser
  • Support for serving static resources, including support for WebJars (covered later in this document)).
    • Static resources (including webjars)
  • Automatic registration of Converter, GenericConverter, and Formatter beans.
    • Automatically register Converter, GenericConverter, Formatter
  • Support for HttpMessageConverters (covered later in this document).
    • Support HttpMessageConverters (later we cooperated with content negotiation to understand the principle)
  • Automatic registration of MessageCodesResolver (covered later in this document).
    • Automatically register MessageCodesResolver (for internationalization)
    • Static index.html support.
    • Static index.html page support
  • Custom Favicon support (covered later in this document).
    • Custom Favicon
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
    • Automatically use the configurablewebbindinginitializer (databinder is responsible for binding the request data to the JavaBean)

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.

Do not use @ EnableWebMvc annotation. Use @ Configuration + WebMvcConfigurer to customize rules

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.

Declaring WebMvcRegistrations changes the default underlying 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.

Use @ EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration to fully take over spring MVC

2.0 simple function analysis

2.1 static resource access

As long as static resources are placed in the classpath: / static or /public or /resources or /META-INF/resources

Access: current project root path / + static resource name

Principle: static mapping/**

When the request comes in, first go to the Controller to see if it can be processed. All requests that cannot be processed are handed over to the static resource processor. If the static resource cannot be found, respond to page 404

Change the default static resource address

spring: mvc: static-path-pattern: /res/** resources: static-locations: [classpath:/haha/]

spring.resources.static-locations is used to tell Spring Boot where to find static resource files. This is a list configuration. When finding files, it depends on the order of configuration. The default official configuration is as follows:
spring.resources.static-locations=classpath:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources

In order to prevent static resources from being intercepted by the interceptor in actual development, a prefix will be added to the static resource path to allow the interceptor to release all requests for the path with the specified prefix

Current project + static path pattern + static resource name = static resource folder

webjar

Automatic mapping/ webjars/**
https://www.webjars.org/

<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.5.1</version> </dependency>

Access address: http://localhost:8080/webjars/jquery/3.5.1/jquery.js The following address depends on the package path inside

2.2 welcome page support

  • index.html under static resource path
    • Static resource paths can be configured
    • However, the access prefix of static resources cannot be configured, otherwise index.html cannot be accessed by default
    • controller can handle / index
  • spring: # mvc: # Static path pattern: / RES / * * this will cause the function of the welcome page to fail resources: static-locations: [classpath:/haha/]

2.3 custom Favicon

favicon.ico can be placed in the static resource directory, and static path pattern: / RES / * * will cause the function of the welcome page to fail

2.4 static resource allocation principle

  • SpringBoot starts by loading the xxxAutoConfiguration class (autoconfiguration class) by default

  • The automatic configuration class webmvcoautoconfiguration of spring MVC function takes effect

    @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 {}
  • The relevant properties of the configuration file are bound to xxx. WebMvcProperties==spring.mvc ResourceProperties==spring.resources

    @Configuration( proxyBeanMethods = false ) @Import() @EnableConfigurationProperties() @Order(0)

    1. There is only one constructor with parameters

    // The values of all parameters of the parameterized constructor are determined from the container // ResourceProperties resourceProperties; an object that gets all values bound to spring.resources // WebMvcProperties mvcProperties object that gets all values bound to spring.mvc // Listablebeanfactory beanFactory beanfactory of spring // HttpMessageConverters find all HttpMessageConverters // ResourceHandlerRegistrationCustomizer found the customizer for the resource processor========= // DispatcherServletPath // The ServletRegistrationBean registers servlets, filters public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) { this.resourceProperties = resourceProperties; this.mvcProperties = mvcProperties; this.beanFactory = beanFactory; this.messageConvertersProvider = messageConvertersProvider; this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable(); this.dispatcherServletPath = dispatcherServletPath; this.servletRegistrations = servletRegistrations; }

    2. Default rules for resource processing

    @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/"); addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> { registration.addResourceLocations(this.resourceProperties.getStaticLocations()); if (this.servletContext != null) { ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION); registration.addResourceLocations(resource); } }); }
    spring: # mvc: # static-path-pattern: /res/** resources: add-mappings: false Disable all static resource rules
    public static class Resources { 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/]. */ private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS; }

    3. Welcome page processing rules
    Under EnableWebMvcConfiguration class

    HandlerMapping: Processor mapping. Saved each Handler Which requests can be processed. @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; } // The welcome page function is dead WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) { //To use the welcome page function, you must be/** if (welcomePage != null && "/**".equals(staticPathPattern)) { logger.info("Adding welcome page: " + welcomePage); setRootViewName("forward:index.html"); } else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) { // Call controller / index logger.info("Adding welcome page template: index"); setRootViewName("index"); } }

3.0 request parameter processing

3.1 request mapping

  • xxxMapping

  • Rest style support (use HTTP request mode verbs to indicate the operation of resources)

    • Previous: / getUser get user / deleteUser delete user / editUser modify user / saveUser save user

    • Now: / user GET - get user DELETE - DELETE user PUT - modify user POST - save user

    • Core Filter; HiddenHttpMethodFilter

      • Usage: form method=post, hide field_ method=put

      • Manual startup in SpringBoot

        mvc: hiddenmethod: filter: enable: true
    • Extension: how to_ Change the name method to our own favorite.

// Test code //@RequestMapping(value = "/user",method = RequestMethod.GET) @GetMapping("/user") public String getUser(){ return "GET-Zhang San"; } //@RequestMapping(value = "/user",method = RequestMethod.POST) @PostMapping("/user") public String saveUser(){ return "POST-Zhang San"; } //@RequestMapping(value = "/user",method = RequestMethod.PUT) @PutMapping("/user") public String putUser(){ return "PUT-Zhang San"; } //@RequestMapping(value = "/user",method = RequestMethod.DELETE) @DeleteMapping("/user") public String deleteUser(){ return "DELETE-Zhang San"; } @Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { return new OrderedHiddenHttpMethodFilter(); } //Create a configuration class and customize the filter in the class @Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter(){ HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter(); methodFilter.setMethodParam("_m"); return methodFilter; }

REST principle (when REST is used for form submission)

  • The form will be submitted with_ method=PUT
  • The request was intercepted by HiddenHttpMethodFilter
    • Is the request normal and POST
      • Get**_ method * *.
      • Compatible with the following requests; PUT.DELETE.PATCH
      • Native request (post). The wrapper pattern requestwrapper overrides the getMethod method method and returns the passed in value
      • When the filter chain is released, use wrapper. The later method to call getMethod is the getMethod to call requestwrapper

Rest uses client tools,

  • For example, PostMan directly sends Put, delete and other requests without Filter

3.2 request mapping principle

Observe the inheritance tree of the dispatcher servlet
Spring MVC functional analysis starts with doDispatch() of org.springframework.web.servlet.DispatcherServlet

// Find which Handler (Controller's method) is used to process the current request mappedHandler = getHandler(processedRequest); // Handler mapping: processor mapping/ xxx -> xxxx

RequestMapping handler mapping: saves the mapping rules of all @ RequestMapping and handlers

All request mappings are in HandlerMapping

  • SpringBoot automatically configures the welcome pagehandlermapping of the welcome page. You can access / access index.html;

  • SpringBoot automatically configures the default RequestMappingHandlerMapping

  • Request to come in and try all HandlerMapping one by one to see if there is the requested information

    • If yes, find the handler corresponding to the request
    • If not, find the next HandlerMapping
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
  • We need some custom mapping processing. We can also put HandlerMapping. Custom HandlerMapping in the container ourselves

3.3 general parameters and basic notes

1. Notes
@PathVariable,@RequestHeader,@ModelAttribute,@RequestParam,@MatrixVariable,@CookieValue,@RequestBody

@PathVariable, @RequestParam, @RequestBody, @CookieValue, @RequestHeader :

// car/2/owner/zhangsan path variable @GetMapping("/car//owner/") public Map<String,Object> getCar(@PathVariable("id") Integer id, @PathVariable("username") String name, @PathVariable Map<String,String> pv, @RequestHeader("User-Agent") String userAgent, @RequestHeader Map<String,String> header, @RequestParam("age") Integer age, @RequestParam("inters") List<String> inters, @RequestParam Map<String,String> params, @CookieValue("_ga") String _ga, @CookieValue("_ga") Cookie cookie){ Map<String,Object> map = new HashMap<>(); // map.put("id",id); // map.put("name",name); // map.put("pv",pv); // map.put("userAgent",userAgent); // map.put("headers",header); map.put("age",age); map.put("inters",inters); // inters is a linked list, so only one data is obtained map.put("params",params); map.put("_ga",_ga); System.out.println(cookie.getName()+"===>"+cookie.getValue()); return map; } @PostMapping("/save") public Map postMethod(@RequestBody String content){ Map<String,Object> map = new HashMap<>(); map.put("content",content); return map; }

@requestAttribute

@GetMapping("/goto") public String goToPage(HttpServletRequest request){ request.setAttribute("msg","succeed..."); request.setAttribute("code",200); return "forward:/success"; //Forward to / success request } @GetMapping("/params") public String testParam(Map<String,Object> map, Model model, HttpServletRequest request, HttpServletResponse response){ map.put("hello","world666"); model.addAttribute("world","hello666"); request.setAttribute("message","HelloWorld"); Cookie cookie = new Cookie("c1","v1"); response.addCookie(cookie); return "forward:/success"; } @ResponseBody @GetMapping("/success") public Map success(@RequestAttribute(value = "msg",required = false) String msg, @RequestAttribute(value = "code",required = false)Integer code, HttpServletRequest request){ Object msg1 = request.getAttribute("msg"); Map<String,Object> map = new HashMap<>(); Object hello = request.getAttribute("hello"); Object world = request.getAttribute("world"); Object message = request.getAttribute("message"); map.put("reqMethod_msg",msg1); map.put("annotation_msg",msg); map.put("hello",hello); map.put("world",world); map.put("message",message); return map; }

@MatrixVariable and UrlPathHelper
/cars/? XXX = XXX & AAA = CCC querystring query string@ RequestParam;
/cars/sell;low=34;brand=byd,audi,yd ; Matrix variable

In page development, if cookie s are disabled, how to use the contents in the session
First, we can save data in the session, and everyone has a jssessionid. This jssessionid will be saved in the cookie, and the cookie will be carried every request, so the server will find it through the jssessionid of the cookie
We can solve this problem through matrix variables: url Rewriting: / ABC; Jssessionid = XXX passes the value of the cookie in the form of a matrix variable

The first semicolon is preceded by a path, followed by a matrix variable. Multiple matrix variables are distinguished by semicolons

//1. Syntax: request path: / cars/sell;low=34;brand=byd,audi,yd //2. SpringBoot disables the function of matrix variables by default // Manual opening: Principle: for path processing: the UrlPathHelper parses. The removeSemicolonContent (remove semicolon content) is used to support the processing of matrix variables //3. The matrix variable must have a url path variable to be resolved @GetMapping("/cars/") public Map carsSell(@MatrixVariable("low") Integer low, @MatrixVariable("brand") List<String> brand, @PathVariable("path") String path){ Map<String,Object> map = new HashMap<>(); map.put("low",low); map.put("brand",brand); map.put("path",path); return map; } // /boss/1;age=20/2;age=10 @GetMapping("/boss//") public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge, @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){ Map<String,Object> map = new HashMap<>(); map.put("bossAge",bossAge); map.put("empAge",empAge); return map; }

Manually turn on the matrix variable:

@Configuration(proxyBeanMethods = false) public class WebConfig /*implements WebMvcConfigurer*/ { //1. WebMvcConfigurer customizes the functionality of spring MVC @Bean public WebMvcConfigurer webMvcConfigurer(){ return new WebMvcConfigurer() { @Override public void configurePathMatch(PathMatchConfigurer configurer) { UrlPathHelper urlPathHelper = new UrlPathHelper(); // Not removed; What follows. The matrix variable function can take effect urlPathHelper.setRemoveSemicolonContent(false); configurer.setUrlPathHelper(urlPathHelper); } } }

2. Servlet API

WebRequest,ServletRequest,MultipartRequest, HttpSession,javax.servlet.http.PushBuilder,Principal,InputStream,Reader,HttpMethod,Locale,TimeZone,ZoneId

ServletRequestMethodArgumentResolver can resolve some of the above parameters

@Override public boolean supportsParameter(MethodParameter parameter) { Class<?> paramType = parameter.getParameterType(); return (WebRequest.class.isAssignableFrom(paramType) || ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType) || HttpSession.class.isAssignableFrom(paramType) || (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) || Principal.class.isAssignableFrom(paramType) || InputStream.class.isAssignableFrom(paramType) || Reader.class.isAssignableFrom(paramType) || HttpMethod.class == paramType || Locale.class == paramType || TimeZone.class == paramType || ZoneId.class == paramType); }

3. Complex parameters

Map, * * model (the data in map and model will be placed in the request field of request request.setAttribute), * * Errors/BindingResult, RedirectAttributes (redirect carried data), ServletResponse (response), SessionStatus, UriComponentsBuilder, ServletUriComponentsBuilder

Map<String,Object> map, Model model, HttpServletRequest request All can be given request Put data in the domain, request.getAttribute();

Map, a parameter of Model type, will return mavContainer.getModel(); (BindingAwareModelMap type, which is both Model and Map)

Model stores data and View stores View

4. User defined object parameters

Automatic type conversion and formatting, cascading encapsulation

/** * Name: < input name = "username" / > < br / > * Age: < input name = "age" / > < br / > * Birthday: < input name = "birth" / > < br / > * Pet name: < input name = "pet. Name" / > < br / > * Pet age: < input name = "pet. Age" / > */ @Data public class Person { private String userName; private Integer age; private Date birth; private Pet pet; } @Data public class Pet { private String name; private String age; }

5. POJO packaging process

The parameter processing of ServletModelAttributeMethodProcessor is shown below

3.4 parameter processing principle

  • The Handler (Controller.method()) that can handle the request is found in the HandlerMapping

  • Find an adapter HandlerAdapter for the current Handler; RequestMappingHandlerAdapter

  • The adapter executes the target method and determines each value of the method parameter

1. HandlerAdapter

0 - support annotation @ RequestMapping on method
1 - support for functional programming

2. Implementation objectives and methods

// Actually invoke the handler. // doDispatch method in DispatcherServlet class mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mav = invokeHandlerMethod(request, response, handlerMethod); // Execution target method // ServletInvocableHandlerMethod actually executes the target method Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // Gets the parameter value of the method Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

3. Parameter resolver - HandlerMethodArgumentResolver

Determine the value of each parameter of the target method to be executed

How many parameter types the spring MVC target method can write depends on the parameter parser

Parameter parser interface:

  • Does the current parser support parsing such parameters
  • It supports calling resolveArgument (return value processor) on

4. Return value processor

5. Determine the value of each parameter of the target method

============InvocableHandlerMethod============ protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; }

6. Traverse to find which parameter parser can support parsing this parameter

@Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { if (resolver.supportsParameter(parameter)) { result = resolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }

7. Parse the value of this parameter

Call the resolveArgument method of the respective HandlerMethodArgumentResolver

8. User defined type parameter encapsulation POJO

ServletModelAttributeMethodProcessor this parameter is supported by the parser
Simple type:

public static boolean isSimpleValueType(Class<?> type) { return (Void.class != type && void.class != type && (ClassUtils.isPrimitiveOrWrapper(type) || Enum.class.isAssignableFrom(type) || CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Date.class.isAssignableFrom(type) || Temporal.class.isAssignableFrom(type) || URI.class == type || URL.class == type || Locale.class == type || Class.class == type)); }

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
Webdatabinder: a web data binder that binds the value of the request parameter to the specified JavaBean
WebDataBinder uses its Converters to convert the requested data into the specified data type. It is encapsulated into a JavaBean again

GenericConversionService: when setting each value, find all converter s in it, which can convert this data type (string with parameter brought by request) to the specified type (JavaBean – Integer)
For example: file upload: byte – > file

@FunctionalInterface public interface Converter<S, T>

In the future, we can put our own Converter in WebDataBinder;

private static final class StringToNumber<T extends Number> implements Converter<String, T>

Custom Converter:

//1. WebMvcConfigurer customizes the functionality of spring MVC @Bean public WebMvcConfigurer webMvcConfigurer(){ return new WebMvcConfigurer() { @Override public void configurePathMatch(PathMatchConfigurer configurer) { UrlPathHelper urlPathHelper = new UrlPathHelper(); // Not removed; What follows. The matrix variable function can take effect urlPathHelper.setRemoveSemicolonContent(false); configurer.setUrlPathHelper(urlPathHelper); } @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new Converter<String, Pet>() { @Override public Pet convert(String source) { // Ah, cat, 3 if(!StringUtils.isEmpty(source)){ Pet pet = new Pet(); String[] split = source.split(","); pet.setName(split[0]); pet.setAge(Integer.parseInt(split[1])); return pet; } return null; } }); } }; }

3.5 completion of target method implementation

Put all data in ModelAndViewContainer; Contains the page address to go. View. Also contains Model data

3.5.1 processing and distribution results

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

InternalResourceView: @Override protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Expose the model object as request attributes. exposeModelAsRequestAttributes(model, request); // Expose helpers as request attributes, if any. exposeHelpers(request); // Determine the path for the request dispatcher. String dispatcherPath = prepareForRendering(request, response); // Obtain a RequestDispatcher for the target resource (typically a JSP). RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!"); } // If already included or response already committed, perform include, else forward. if (useInclude(request, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including [" + getUrl() + "]"); } rd.include(request, response); } else { // Note: The forwarded resource is supposed to determine the content type itself. if (logger.isDebugEnabled()) { logger.debug("Forwarding to [" + getUrl() + "]"); } rd.forward(request, response); } }

Expose the model as a request domain attribute / / Expose the model object as request attributes

exposeModelAsRequestAttributes(model, request);

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception { // All data traversal in the model is placed in the request field one by one model.forEach((name, value) -> { if (value != null) { request.setAttribute(name, value); } else { request.removeAttribute(name); } }); }

4.0 data response and content negotiation

Data response

  • Response page
  • Response data
    • JSON
    • XML
    • xls
    • Pictures, audio and video
    • Custom protocol data

4.1 response JSON

1. jackson.jar + @ResponseBody

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

web scenarios automatically introduce json scenarios

Return value parser

try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); }
@Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // Decide by this method HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
================= RequestResponseBodyMethodProcessor class============== @Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); // Try even with null return value. ResponseBodyAdvice could get involved. // Write out using message converters writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }

Return value parser principle

  • The return value processor determines whether this type of return value supportsreturnantype is supported
  • The return value handler calls handleReturnValue for processing
  • RequestResponseBodyMethodProcessor can process the returned value marked with @ ResponseBody annotation
    • Use MessageConverters to process and write the data as json
      • Content negotiation (by default, the browser will tell the server what content type it can accept in the form of request header)
      • The server finally determines what kind of content type data the server can produce according to its own ability,
      • Spring MVC will traverse the HttpMessageConverter at the bottom of all containers one by one to see who can handle it
        • Get MappingJackson2HttpMessageConverter and write the object as json
        • Use MappingJackson2HttpMessageConverter to convert the object into json and write it out

The return values that spring MVC supports are determined by returnValueHandlers

ModelAndView Model View ResponseEntity ResponseBodyEmitter StreamingResponseBody HttpEntity HttpHeaders Callable DeferredResult ListenableFuture CompletionStage WebAsyncTask yes @ModelAttribute And is of object type @ResponseBody annotation ---> RequestResponseBodyMethodProcessor;

Principle of HTTPMessageConverter

  1. MessageConverter specification
    HttpMessageConverter: see whether it supports converting objects of this Class type into data of MediaType type

    For example, the Person object is converted to JSON or JSON is converted to Person

  2. Default MessageConverter

0 - only Byte type
1 - String
2 - String
3 - Resource
4 - ResourceRegion
5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \ StreamSource.class \ Source.class
6 - MultiValueMap
7 - true
8 - true
9 - supporting xml processing in annotation mode

Finally, the MappingJackson2HttpMessageConverter converts the object into JSON (converted by the object mapper of the underlying jackson)

4.2 content negotiation

Data of different media types are returned according to different receiving capabilities of the client

1. Introduce xml dependency

<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>

2. The postman test returns json and xml
You only need to change the Accept field in the request header. As specified in the Http protocol, tell the server the data type that the client can receive: application/xml

3. Enable the browser parameter mode negotiation function

spring: contentnegotiation: favor-parameter: true #Enable request parameter content negotiation mode

http://localhost:8080/test/person?format=json

http://localhost:8080/test/person?format=xml

Determine what content type the client receives;

  1. The Parameter policy preferentially determines whether to return json data (get the value of format in the request header)
  2. Finally, negotiate the content and return it to the client json.

Content negotiation principle

  • 1. Judge whether there is a determined media type in the current response header. MediaType
  • 2. Get the content type supported by the client (PostMan, browser) (get the client Accept request header field) [application/xml]
    • The content negotiation manager uses the request header based policy by default
    • HeaderContentNegotiationStrategy() determines the type of content that the client can receive
  • 3. Cycle through all the messageconverters of the current system to see who supports the operation of this object (Person)
  • 4. Find the converter that supports the operation Person and count the media types supported by the converter
  • 5. The client needs [application/xml]. The server needs [10 kinds, json, xml]
  • 6. Best matching media type for content negotiation
  • 7. Use a converter that supports converting objects to best match media types. Call it for conversion

4.3 custom MessageConverter

Realize multi protocol data compatibility. json,xml,x-guigu

  1. @The ResponseBody response data goes out and calls the RequestResponseBodyMethodProcessor for processing

  2. The return value of the Processor processing method. Processing via MessageConverter

  3. All messageconverters together can support data operations (read and write) of various media types

  4. Find the final messageConverter through content negotiation;

What is the function of spring MVC. Add a WebMvcConfigurer to the container with an entry

@Bean public WebMvcConfigurer webMvcConfigurer(){ return new WebMvcConfigurer() { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new GuiguMessageConverter()); } } } ============ GuiguMessageConverter ============ public class GuiguMessageConverter implements HttpMessageConverter<Person> { @Override public boolean canRead(Class<?> clazz, MediaType mediaType) { return false; } @Override public boolean canWrite(Class<?> clazz, MediaType mediaType) { return clazz.isAssignableFrom(Person.class); } /** * The server needs to count what content types can be written out by all messageconverters * * application/x-guigu * @return */ @Override public List<MediaType> getSupportedMediaTypes() { return MediaType.parseMediaTypes("application/x-guigu"); } @Override public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return null; } @Override public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //Writing out custom protocol data String data = person.getUserName()+";"+person.getAge()+";"+person.getBirth(); //Write it out OutputStream body = outputMessage.getBody(); body.write(data.getBytes()); } } ================== WebConfig ================= /** * Custom content negotiation policy * @param configurer */ @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { //Map<String, MediaType> mediaTypes Map<String, MediaType> mediaTypes = new HashMap<>(); mediaTypes.put("json",MediaType.APPLICATION_JSON); mediaTypes.put("xml",MediaType.APPLICATION_XML); mediaTypes.put("gg",MediaType.parseMediaType("application/x-guigu")); //Specify which media types correspond to which parameters support resolution ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaTypes); // parameterStrategy.setParameterName("gg"); HeaderContentNegotiationStrategy headeStrategy = new HeaderContentNegotiationStrategy(); configurer.strategies(Arrays.asList(parameterStrategy,headeStrategy)); }

It is possible that the custom functions we added will override many default functions, resulting in some default functions invalid.

Let's consider, in addition to our complete customization of the above functions? Does SpringBoot provide us with the ability to quickly modify media types based on configuration files? How to configure it? (Note: refer to the section on web development content negotiation in the official SpringBoot document)

5.0 view parsing and template engine

View parsing: SpringBoot does not support JSP by default. It needs to introduce third-party template engine technology to realize page rendering

5.1 view analysis

1. View analysis

  • forward
  • redirect
  • Custom view

1. View analysis principle and process

1. During the processing of the target method, all data will be placed in the ModelAndViewContainer. Include data and view addresses
2. ModelAndView (data and view address) will be returned after the execution of any target method
3. processDispatchResult() handles the dispatch result (how should the page respond)

  • 1,render(mv, request, response); Page rendering logic

    • 1. Get the View object according to some values (ViewName,) [defines the rendering logic of the page]

      • 1. All View parsers try to find the View object based on the current return value

      • 2. Get redirect: / main. HTML -- > thymeleaf new redirectview()

      • 3. The content negotiation view resolver contains all of the following view parsers. Internally, it uses all of the following view parsers to get view objects

      • 4,view.render(mv.getModelInternal(), request, response); The view object calls the custom render to render the page

      • How to render RedirectView [redirect to a page]

      • 1. Get destination url address

      • 2,response.sendRedirect(encodedURL);

ThymeleafViewResolver:

    • The return value starts with forward: new internalresourceview (forwardurl); -- > Forward request.getRequestDispatcher(path).forward(request, response);
    • The return value starts with redirect: new redirectview() -- > render is redirect
    • The return value is a normal string: new ThymeleafView() – > render

Custom view parser + custom view: Dachang University

5.2 template engine - Thymeleaf

Modern, server-side Java template engine

1. Basic grammar

expressiongrammarpurposeVariable value${...}Get request domain, session domain, object equivalentSelect variable*{...}Get context object valuenews#{...}Get international equivalentlink@{...}Generate linkFragment Expression~{...}jsp:include function to introduce public page fragments

Literal
Text values: 'one text', 'Another one!'
Numbers: 0, 34, 3.0, 12.3
Boolean: true, false
Null value: null
Variables: one and two variables cannot have spaces

Text operation
String splicing:+
Variable replacement: The name is $

Mathematical operation
+ , - , * , / , %

Boolean operation
and, or, !, not

Comparison operation
Comparison: >, <, > =, < = (GT, lt, Ge, le * *) * equation: = == ( eq , ne )

Conditional operation
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)

2. Set attribute value - th:attr

<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#,alt=#" /> Alternative writing <input type="submit" value="Subscribe!" th:value="#"/> <form action="subscribe.html" th:action="@{/subscribe}">

All h5 compatible label writing:
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes

3. Iteration

<tr th:each="prod : $"> <td th:text="$">Onions</td> <td th:text="$">2.41</td> <td th:text="$? # : #">yes</td> </tr>
<tr th:each="prod,iterStat : $" th:class="$? 'odd'"> <td th:text="$">Onions</td> <td th:text="$">2.41</td> <td th:text="$? # : #">yes</td> </tr>

4. Conditional operation

<a href="comments.html" th:href="@{/product/comments(prodId=$)}" th:if="$">view</a>
<div th:switch="$"> <p th:case="'admin'">User is an administrator</p> <p th:case="#">User is a manager</p> <p th:case="*">User is some other thing</p> </div>

5. Attribute priority
Priority is not defined in writing order. When there are multiple th: * attributes in the same tag, thyclear also has the problem of priority when dealing with these attributes

OrderFeatureAttributes1Fragment inclusionth:include th:replace2Fragment iterationth:each3Conditional evaluationth:if th:unless th:switch th:case4Local variable definitionth:object th:with5General attribute modificationth:attr th:attrprepend th:attrappend6Specific attribute modificationth:value th:href th:src ...7Text (tag body modification)th:text th:utext8Fragment specificationth:fragment9Fragment removalth:remove

5.3 use of thymeleaf

1. Introduce starter

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

thymeleaf is automatically configured

@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(ThymeleafProperties.class) @ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class }) @AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class }) public class ThymeleafAutoConfiguration { }

Automatically configured strategy

  • 1. All thymeleaf configuration values are in thymeleaf properties

  • 2. Spring template engine is configured

  • 3. With ThymeleafViewResolver

  • 4. We just need to develop the page directly

public static final String DEFAULT_PREFIX = "classpath:/templates/"; public static final String DEFAULT_SUFFIX = ".html"; // xxx.html

5.4 construction of background management system

1. Create a project and put all static resources under the static folder

2. Path construction
th:action="@{/login}"

3. Template extraction
th:insert/replace/include * * pay attention to the differences between the three. Wrap the content to be extracted with div**

<head th:fragment="commonheader"> <!--common--> <link href="css/style.css" rel="stylesheet" th:href="@{/css/style.css}"> <link href="css/style-responsive.css" rel="stylesheet" th:href="@{/css/style-responsive.css}"> <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> <!--[if lt IE 9]> <script src="js/html5shiv.js" th:src="@{/js/html5shiv.js}"></script> <script src="js/respond.min.js" th:src="@{/js/respond.min.js}"></script> <![endif]--> </head> <div id="leftmenu"> ... </div> ========= main.html ========= <div th:include="common :: commonheader"></div> <!--Because use id statement, So use # Selector to select -- > <div th:replace="common :: #leftmenu"></div>

4. Page Jump

@PostMapping("/login") public String main(User user, HttpSession session, Model model){ if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){ //Save the users who log in successfully session.setAttribute("loginUser",user); //Login successfully redirected to main.html; Redirection prevents duplicate form submissions return "redirect:/main"; }else { model.addAttribute("msg","Account password error"); //Return to the login page return "login"; } }

5. Data rendering

@GetMapping("/dynamic_table") public String dynamic_table(Model model){ //Traversal of table contents List<User> users = Arrays.asList(new User("zhangsan", "123456"), new User("lisi", "123444"), new User("haha", "aaaaa"), new User("hehe ", "aaddd")); model.addAttribute("users",users); return "table/dynamic_table"; }
<thead> <tr> <th>#</th> <th>user name</th> <th>password</th> </tr> </thead> <tbody> <tr th:each="user, userstat:$"> <td th:text="$">Trident</td> <td th:text="$">Internet</td> <td>[[$]]</td> </tr> </tbody>

6.0 interceptors

6.1 HandlerInterceptor interface

  • Write an interceptor to implement the HandlerInterceptor interface
  • Register the interceptor in the container (implement addinterceptors of webMvcConfigurer)
  • Specify the interception rule [if all static resources are intercepted, they will also be intercepted]
@Slf4j public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException { String requestURI = request.getRequestURI(); log.info("preHandle The intercepted request path is{}",requestURI); // Login judgment HttpSession session = request.getSession(); Object obj = session.getAttribute("user"); if(obj != null) { return true; } // Forward request to landing page request.setAttribute("msg", "Please log in first"); request.getRequestDispatcher("/").forward(request, response); return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { log.info("postHandle implement{}",modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) { log.info("afterCompletion Execution exception{}",ex); } } ============== AdminWebConfig ============== @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") .excludePathPatterns("/", "/login", "/css/**", "/fonts/**", "/images/**", "/js/**"); }

6.2 interceptor principle

1. According to the current request, find * * HandlerExecutionChain [* * handlers that can handle requests and all interceptors of handlers]

2. Execute the preHandle methods of all interceptors sequentially first

  • If the current interceptor prehandler returns true, the prehandler of the next interceptor will be executed
  • If the current interceptor returns false, execute the afterCompletion of all interceptors that have been executed in reverse order

3. If any interceptor returns false, it will jump out without executing the target method

4. All interceptors return True. Execute the target method

5. Execute the postHandle methods of all interceptors in reverse order.

6. Any exception in the previous steps will trigger afterCompletion in reverse order

7. After the page is successfully rendered, afterCompletion will also be triggered in reverse order

7.0 file upload

7.1 forms

<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data"> <div> <label for="exampleInputEmail1">mailbox</label> <input type="email" id="exampleInputEmail1" placeholder="Enter email"> </div> <div> <label for="exampleInputPassword1">name</label> <input type="text" name="username" id="exampleInputPassword1" placeholder="Password"> </div> <div> <label for="exampleInputFile">head portrait</label> <input type="file" name="headerImg" id="exampleInputFile"> </div> <div> <label for="exampleInputFile">Photo collection</label> <input type="file" name="photos" multiple> </div> <button type="submit">Submit</button> </form>

7.2 file upload code

/** * MultipartFile Automatically encapsulate uploaded files * @param email * @param username * @param headerImg * @param photos * @return */ @PostMapping("/upload") public String upload(@RequestParam("email") String email, @RequestParam("username") String username, @RequestPart("headerImg") MultipartFile headerImg, @RequestPart("photos") MultipartFile[] photos) throws IOException { log.info("Uploaded information: email={},username={},headerImg={},photos={}", email,username,headerImg.getSize(),photos.length); if(!headerImg.isEmpty()){ //Save to file server, OSS server String originalFilename = headerImg.getOriginalFilename(); headerImg.transferTo(new File("H:\\cache\\+originalFilename)); } if(photos.length > 0){ for (MultipartFile photo : photos) { if(!photo.isEmpty()){ String originalFilename = photo.getOriginalFilename(); photo.transferTo(new File(""H:\\cache\\"+originalFilename)); } } } return ""main""; }

7.3 automatic configuration principle

File upload autoconfiguration class multipartautoconfiguration multipartproperties

  • The standardservlet multipartresolver [file upload parser] is automatically configured

  • Principle steps

    • When the request comes in

15 October 2021, 16:22 | Views: 3069

Add new comment

For adding a comment, please log in
or create account

0 comments