It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Time:2021-7-31

preface

As we all know, java development is jokingly called spring development by the old oil subroutine apes, which shows the position of spring in java development. If there is no spring framework, the code written by most people will be a shit.

Spring MVC is a very important module among the seven modules of spring.

MVC framework is a full-featured MVC implementation for building web applications. Through the policy interface, MVC framework becomes highly configurable

Friends who are interested in the other six modules of the spring framework can directly click to get the information I sorted outComplete spring learning notes, there are a lot of source code analysis and project practice, which can be exchanged together.

MVC Design Overview

In the early development of Java Web, the operations of display layer, control layer and data layer were uniformly handed over to JSP or JavaBean for processing, which is called  Model1:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

  • Disadvantages:
  • There is a serious coupling between JSP and java bean, and Java code and HTML code are also coupled together
  • Developers are required not only to master Java, but also to have a superb front-end level
  • The front end and the back end depend on each other. The front end needs to wait for the back end to complete, and the back end also depends on the front end to complete, so as to carry out effective testing
  • Code is difficult to reuse

Because of the above disadvantages, this method was soon replaced by servlet + jsp + java bean, the early MVC model(Model2)As shown in the figure below:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

First, the user’s request will reach the servlet, then call the corresponding java bean according to the request, and hand over all the display results to the JSP to complete. This mode is called MVC mode.

  • M stands for model
    What is the model? Models are data, Dao and bean
  • V stands for view
    What is the view? Is a web page, JSP, used to display the data in the model
  • C stands for controller
    What is the controller? The function of the controller is to display different data (models) on different views. Servlet plays such a role.

Spring MVC architecture

In order to solve the programming of database transactions that have not been handled well in the persistence layer, and to cater to the strong rise of NoSQL, spring MVC gives a scheme:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

The traditional model layer is divided into business layer (service) and data access layer (Dao, data access object).Under service, we can operate the data access layer through spring’s declarative transactions, and on the business layer, we are also allowed to access NoSQL. This can meet the use of NoSQL, which can greatly improve the performance of Internet systems.

  • characteristic:
    The structure is loose, and almost all kinds of views can be used in spring MVC
    Loose coupling, separate modules
    Seamless integration with spring
    • *

Hello Spring MVC

Let’s write our first spring MVC program:

Step 1: create a new spring MVC project in the idea

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

It is called hellosppring MVC, and click Finish:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Idea will automatically help us download the necessary jar packages and create some default directories and files for us. After creation, the project structure is as follows:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Step 2: modify web.xml

Open web.xml and complete the modification according to the following figure:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

hold<url-pattern>The value of the element is changed to /, which means that all requests should be intercepted and handed over to the background controller of spring MVC for processing. After the change:

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Step 3: edit dispatcher-servlet.xml

The beginning of the file name dispatcher is the same as that in web. XML above  <servlet-name>  The dispatcher of element configuration corresponds to the mapping configuration file of spring MVC (XXX servlet. XML). We edit it as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="simpleUrlHandlerMapping"
         class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
       <property name="mappings">
           <props>
               <!-- / The request for Hello path is sent to the controller with ID hellocontroller for processing -- >
               <prop key="/hello">helloController</prop>
           </props>
       </property>
   </bean>
   <bean id="helloController" class="controller.HelloController"></bean>
</beans>

Step 4: write hellocontroller

Create the hellocontroller class under package controller and implement the org.springframework.web.servlet.mvc.controller interface:

package controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloController implements Controller{
   @Override
   public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
       return null;
   }
}
  • There was a problem:  Javax.servlet package not found
  • solve:  Copy the servlet-api.jar package under the [lib] folder in the directory of the local Tomcat server to the [lib] folder of the project, and add the dependency

Spring MVC combines models and views through modelandview objects

ModelAndView mav = new ModelAndView("index.jsp");
mav.addObject("message", "Hello Spring MVC");

The view shown here is index. JSP
The message of the model data is “Hello spring MVC”

package controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloController implements Controller {

   public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
       ModelAndView mav = new ModelAndView("index.jsp");
       mav.addObject("message", "Hello Spring MVC");
       return mav;
   }
}

Step 5: prepare index.jsp

Modify the content of index.jsp to:

<%@ page language="java" contentType="text/html; charset=UTF-8"
   pageEncoding="UTF-8" isELIgnored="false"%>

<h1>${message}</h1>

The content is very simple. The El expression is used to display the content of message.

Step 6: deploy Tomcat and related environments

Find [edit configurations] under [run]

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

To configure the Tomcat environment:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Select the local Tomcat server and change its name:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Under the deployment tab, complete the following operations:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Just click OK. Let’s click the triangle in the upper right corner to run the Tomcat server.

  • Problems:  The Tomcat server cannot start properly
  • reason:  The Tomcat server could not find the relevant jar package
  • resolvent:  Cut and paste the whole [lib] folder under [WEB-INF] and re-establish the dependency:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Step 7: restart the server

Restart the server and enter the address: localhost / Hello

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Click to receiveComplete spring learning notes

Track spring MVC requests

Every time a user clicks a link or submits a form in the web browser, the request starts to work. Like a postman, it will go through many sites from leaving the browser to getting a response. Each site will leave some information and bring other information. The following figure shows the request process of spring MVC:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

First stop: dispatcher Servlet

After the request leaves the browser, the first stop is the dispatcher servlet. According to the name, this is a servlet. Through J2EE learning, we know that the servlet can intercept and process HTTP requests. The dispatcher servlet will intercept all requests and send these requests to the spring MVC controller.

<servlet>
   <servlet-name>dispatcher</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
   <servlet-name>dispatcher</servlet-name>
   <!--  Block all requests -- >
   <url-pattern>/</url-pattern>
</servlet-mapping>
  • The task of the dispatcher servlet is to intercept requests sent to the spring MVC controller.

Second station: handler mapping

  • Question:A typical application may have multiple controllers. Which controller should these requests be sent to?

Therefore, the dispatcher servlet will query one or more processor mappings to determine where the next station of the request is, and the processor mapping willThe decision is made according to the URL information carried by the requestFor example, in the above example, we configure simpleurlhandlermapping to give the / Hello address to hellocontroller for processing:

<bean id="simpleUrlHandlerMapping"
     class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="mappings">
       <props>
           <!-- / The request for Hello path is sent to the controller with ID hellocontroller for processing -- >
           <prop key="/hello">helloController</prop>
       </props>
   </property>
</bean>
<bean id="helloController" class="controller.HelloController"></bean>

Third station: controller

Once the appropriate controller is selected, the dispatcher servlet will send the request to the selected controller. When the controller arrives, the request will unload its load (the request submitted by the user) and wait for the controller to process these information:

public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
   //Processing logic
   ....
}

Station 4: return to dispatcherservlet

When the controller completes the logic processing, it usually generates some information, which is the information that needs to be returned to the user and displayed on the browser. They are calledModel。 It’s not enough to just return the original information – it needs to be formatted in a user-friendly way, usually HTML, so the information needs to be sent to a userView, usually JSP.

The last thing the controller does is package the model data and represent the view name for rendering output(logical view name). It then sends the request back to the dispatcher servlet with the model and view names.

public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
   //Processing logic
   ....
   //Return to dispatcher Servlet
   return mav;
}

Fifth stop: View parser

In this way, the controller will not be coupled with a specific view, and the view name passed to the dispatcher servlet does not directly represent a specific JSP( In fact, it can’t even be sure that the view is JSP),It passes only a logical name that will be used to find the real view that produces the result.

The dispatcher servlet will use the view resolver to match the logical view name to a specific view implementation, which may or may not be a JSP

The above example is directly bound to the index. JSP view

Sixth station: View

Now that the dispatcher servlet knows which view renders the results, the requested task is basically completed.

Its last stop is the implementation of the view, where it delivers the model data and the requested task is completed. The view uses the model data to render the result, and the output result will be passed to the client through the response object.

<%@ page language="java" contentType="text/html; charset=UTF-8"
   pageEncoding="UTF-8" isELIgnored="false"%>

<h1>${message}</h1>

Configuring spring MVC with annotations

Above, we have a certain understanding of spring MVC and created the first spring MVC program through XML configuration. Let’s see how to complete the configuration of the above program based on annotations:

Step 1: add annotations for hellocontroller

package controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController{

   @RequestMapping("/hello")
   public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
       ModelAndView mav = new ModelAndView("index.jsp");
       mav.addObject("message", "Hello Spring MVC");
       return mav;
   }
}

Remove the implemented interface.

  • Briefly explain:
  • @Controller  Notes:
    Obviously, this annotation is used to declare the controller, but in fact, this annotation has little impact on spring MVC itself( Spring actually says that it is only used to assist in component scanning  @Component  Annotation instead, but I tried it myself, but it didn’t work, because the above example didn’t configure the JSP view parser, and I configured one myself, which still didn’t succeed…)
  • @RequestMapping  Notes:
    Obviously, this means the path  /hello  Will be mapped to this method

Step 2: cancel the previous XML comment

In the dispatcher-servlet.xml file, comment out the previous configuration, and then add a component scan:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

   <!--<bean id="simpleUrlHandlerMapping"-->
                                       <!--class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">-->
   <!--<property name="mappings">-->
           <!--<props>-->
               <!--& lt;!& ndash; / The request for the Hello path is sent to the controller with ID hellocontroller for processing & ndash& gt;-->
               <!--<prop key="/hello">helloController</prop>-->
           <!--</props>-->
       <!--</property>-->
   <!--</bean>-->
   <!--<bean id="helloController" class="controller.HelloController"></bean>-->

   <!--  Scan components under controller -- >
   <context:component-scan base-package="controller"/>
</beans>

Step 3: restart the server

When the configuration is complete, restart the server and enter  localhost/hello  The address can still see the effect:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

@Requestmapping annotation details

If  @RequestMapping  If it acts on a class, it is equivalent to adding an address before all configured mapping addresses of the class, for example:

@Controller
@RequestMapping("/wmyskxz")
public class HelloController {
    @RequestMapping("/hello")
    public ModelAndView handleRequest(....) throws Exception {
        ....
    }
}
  • Access address:  localhost/wmyskxz/hello
    • *

Configure view parser

Remember our spring MVC request process? The view parser is responsible for locating the view. It accepts a logical view name passed by the dispatcher servlet to match a specific view.

  • Requirements:  There are some pages that we don’t want users to access directly, such as pages with important data, such as pages supported by model data.
  • Problems caused by:
    We can put a [test. JSP] under the [web] root directory to simulate a page of important data. We don’t have to do anything. Restart the server and enter it in the web page  localhost/test.jsp  Can be accessed directly, which will causeData leakage
    In addition, we can input directly  localhost/index.jsp  Try it. According to our above program, this will be a blank page, because we didn’t get it  ${message}  Parameters are accessed directly, which willAffect user experience

Solution

We configure our JSP file under the [page] folder in the [WEB-INF] folder. The [WEB-INF] is the default security directory in the Java Web and is not allowed to be accessed directly by users(that is, you said you passed  localhost/WEB-INF/  Such a way is never accessible)

However, we need to tell the view parser about this. We configure it in the dispatcher-servlet.xml file as follows:

<bean id="viewResolver"
     class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/page/" />
   <property name="suffix" value=".jsp" />
</bean>

A view parser built in spring MVC is configured here. The parser follows a convention: YesAdd a prefix and suffix to the view name to determine the physical path of view resources in a web application.Let’s actually see the effect:

Step 1: modify hellocontroller

Let’s modify the code:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Step 2: configure the view parser:

According to the above configuration, complete:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--<bean id="simpleUrlHandlerMapping"-->
                                        <!--class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">-->
    <!--<property name="mappings">-->
            <!--<props>-->
                <!--& lt;!& ndash; / The request for the Hello path is sent to the controller with ID hellocontroller for processing & ndash& gt;-->
                <!--<prop key="/hello">helloController</prop>-->
            <!--</props>-->
        <!--</property>-->
    <!--</bean>-->
    <!--<bean id="helloController" class="controller.HelloController"></bean>-->

    <!--  Scan components under controller -- >
    <context:component-scan base-package="controller"/>
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

Step 3: clip index.jsp file

Create a [page] folder under the [WEB-INF] folder, and cut and paste the [index. JSP] file into it:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Step 4: update resources and restart the server

visit  localhost/hello  Path, see the correct effect:
It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

  • Principle:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

The logical view we passed in is called index, plus“/WEB-INF/page/”Prefix and“.jsp”Suffix, you can determine the path of the physical view, so that we can put all views into the [page] folder in the future!

  • be careful:The configuration at this time is only under dispatcher-servlet.xml
    • *

The controller receives the request data

Using the controller to receive parameters is often the first step in developing business logic for spring MVC. To explore the parameter transfer method of spring MVC, let’s first create a simple form for submitting data:

<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" import="java.util.*" isELIgnored="false"%>
<html>
<head>
    <meta charset="utf-8">
    < title > spring MVC parameter transfer method < / Title >
</head>
<body>
<form action="/param" role="form">
    User name: < input type = "text" name = "username" > < br / >
    Password: < input type = "text" name = "password" > < br / >
    < input type = "submit" value = "submit" >
</form>
</body>
</html>

Be ugly. Let’s test it:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Implemented using servlet native API:

It’s easy to know that the form will be submitted to  /param  For this directory, let’s use the servlet native API to see if we can get data:

@RequestMapping("/param")
public ModelAndView getParam(HttpServletRequest request,
                        HttpServletResponse response) {
   String userName = request.getParameter("userName");
   String password = request.getParameter("password");

   System.out.println(userName);
   System.out.println(password);
   return null;
}

Test successful:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Use matching rules with the same name

We can set the formal parameter name of the method definition to the same method as the parameter name passed in from the foreground to obtain data (matching rule of the same name):

@RequestMapping("/param")
public ModelAndView getParam(String userName,
                            String password) {
   System.out.println(userName);
   System.out.println(password);
   return null;
}

Test successful:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

  • Question:  This will have a strong coupling with the foreground, which we don’t want
  • solve:  use  @Requestparam ("foreground parameter name")  To inject:
    It may be a little long introduction to spring MVC, but learning may be wealth and freedom!
  • @RequestParam  Note details:
    The annotation has three variables:valuerequireddefaultvalue
  • value : appoint  name  What is the name of the property,value  Properties can be left blank by default
  • required : Whether this parameter is required can be set to true or false
  • defaultvalue : Set defaults

Use model to transfer parameters

  • Requirement: the foreground parameter name must be the same as the field name in the model

Let’s first create a user model for our form:

package pojo;

public class User {
   
   String userName;
   String password;

   /* getter and setter */
}

Then the test was still successful:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Chinese garbled code problem

  • be careful:  As in the servlet, this method is only valid for the post method (because it is a request processed directly)

We can do this by configuring the spring MVC character encoding filter and adding the following in web.xml:

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <!--  Set encoding format -- >
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Controller echo data

Through the above, we know how to accept the requested data and solve the problem of post garbled code. So how do we echo the data? Therefore, we create a [test2. JSP] under [page]:

<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8" import="java.util.*" isELIgnored="false" %>
<html>
<head>
   < title > spring MVC data echo < / Title >
</head>
<body>
<h1>Echo data: ${message}</h1>
</body>
</html>

It is implemented using servlet native API

Let’s test whether the servlet native API can complete this task:

@RequestMapping("/value")
public ModelAndView handleRequest(HttpServletRequest request,
                                 HttpServletResponse response) {
   Request. SetAttribute ("message", "success!");
   return new ModelAndView("test1");
}

Enter in the browser address bar:localhost/value  test

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Use the modelandview object provided by spring MVC

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

Using model objects

In spring MVC, we usually bind data in this way,

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

  • use  @ModelAttribute  Notes:

    @ModelAttribute
    public void model(Model model) {
    Model. AddAttribute ("message", "annotation succeeded");
    }
    
    @RequestMapping("/value")
    public String handleRequest() {
    return "test1";
    }

In this way, when accessing the controller method handlerequest (), the model () method will be called first  message  Add it to the page parameters, which can be called directly in the view, but this will cause all methods of the controller to call the model () method first, but it is also very convenient because a variety of data can be added.


Client jump

No matter the address in front  /hello  Jump to index.jsp or  /test  Jump to test.jsp. These are the jumps of the server, that is  Request. Getrequestdispatcher ("address"). Forward (request, response);

How do we jump to the client? We continue to write in hellocontroller:

@RequestMapping("/hello")
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
    ModelAndView mav = new ModelAndView("index");
    mav.addObject("message", "Hello Spring MVC");
    return mav;
}

@RequestMapping("/jump")
public ModelAndView jump() {
    ModelAndView mav = new ModelAndView("redirect:/hello");
    return mav;
}

We use  redirect:/hello  That means we’re going to jump to  /hello  For this path, we restart the server and enter in the address bar:localhost/jump , Will automatically jump to  /hello  Under path:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!

It can also be used as follows:

@RequestMapping("/jump")
public String jump() {
    return "redirect: ./hello";
}

File upload

Let’s review the traditional file upload and download:here

Let’s take a look at how to upload and download files in spring MVC

  • be careful:  Need to import first  commons-io-1.3.2.jar  and  commons-fileupload-1.2.1.jar  Two bags

Step 1: configure the upload parser

Add a new sentence in dispatcher-servlet.xml:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

Enable support for upload function

Step 2: write JSP

The file name is upload.jsp, which is still created under [page]:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
   < title > test file upload < / Title >
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
   <input type="file" name="picture">
   < input type = "submit" value = "Upload" >
</form>
</body>
</html>

Step 3: write controller

Create a new [uploadcontroller] class under package [controller]:

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class UploadController {

   @RequestMapping("/upload")
   public void upload(@RequestParam("picture") MultipartFile picture) throws Exception {
       System.out.println(picture.getOriginalFilename());
   }

   @RequestMapping("/test2")
   public ModelAndView upload() {
       return new ModelAndView("upload");
   }

}

Step 4: Test

Enter in the browser address bar:localhost/test2 , Select the file and click upload. The test is successful:

It may be a little long introduction to spring MVC, but learning may be wealth and freedom!


It’s almost enough to write here. This article is mainly for friends who don’t know spring MVC, so there must be a lot of things that haven’t been written, including other modules of the spring framework. I’ll update them when I have time, so interested friends can pay attention and praise by the way!

Of course, if you can’t wait, you can click to get itComplete spring learning notes

end