For those of you who are not familiar with the basic principles of Servlets and Spring MVC, take a look at my other article which gives you a step-by-step guide to Spring MVC source code and a hand-drawn flow chart. Look back at this article and you’ll see.

This article focuses on the implementation of the code, and many of the points are explained in the code comments. Please read them carefully.

All code is hosted on https://github.com/FrancisQiang/spring-mvc-custom, anyone interested in to the fork.

Written MVC FrameworkServlet

As I mentioned in my previous article, FrameworkServlet is a direct parent of Spring MVC’s most important class, DispatcherServlet. Its main functions are: It provides the function of loading a corresponding Web application environment, as well as the unified GET, POST, DELETE, PUT and other methods to DispatcherServlet processing.

In short, some doXxx methods are assigned to a method of DispatcherServlet. What is this method? The famous doDispatch().

We can implement the simplest FrameworkServlet class.

// HttpServlet must be inherited
public class FrameworkServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            // Use the doDispatch method to process the request
            doDispatch(req, resp);
        } catch(Exception e) { e.printStackTrace(); }}@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            // Use the doDispatch method to process the request
            doDispatch(req, resp);
        } catch(Exception e) { e.printStackTrace(); }}public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception{
        // The main point here is that it is mainly implemented by DispatcherServlet
        Of course you can use abstract methods here and define frameworkServlets as abstract classes
        // The source code in Spring MVC does the same}}Copy the code

Of course, in order to inherit HttpServlet you need to package pom.xml in maven project, you can also package in other ways yourself.

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>
Copy the code

That is, the FrameworkServlet simply provides a template method (for those unfamiliar with design patterns, this is a common but very simple design pattern for frameworks) that ultimately implements the doDispatch() method in the DispatcherServlet.

Handwritten MVC DispatcherServlet primitive version

First we need to inherit the FrameworkServlet class

public class DispatcherServlet extends FrameworkServlet{
    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Process the request}}Copy the code

It’s easy to inherit and implement, but we seem to have forgotten that we need to configure our DispatcherServlet in web.xml.

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <! Configure our DispatcherServlet -->
  <servlet>
    <! -- Specify class -->
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>com.lgq.servlet.DispatcherServlet</servlet-class>
    <! We need to specify our spring MVC configuration file path, which I will talk about later.
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>springmvc.xml</param-value>
    </init-param>
    <! -- Initialize policy -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <! -- DispatcherServlet core intercepts all requests -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
Copy the code

At this point we need to create an MVC configuration file, springmVC.xml (note that the path is important), which is actually a Spring configuration file. The main reason we need it is that we need to load the processor mapping, processor adapter we wrote later into the IOC container. And we initialize the container to get the bean when the Servlet is initialized.

<?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 https://www.springframework.org/schema/context/spring-context.xsd">
        <! -- Quite simply, nothing has been implemented yet -->
</beans>
Copy the code

To use Spring’s IOC functionality we need to import the corresponding package in Maven’s POM.xml. Import core and Context packages

Remember: MVC functionality is built on IOC, so IOC is a core feature in Spring.

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>5.2.0. RELEASE</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.0. RELEASE</version>
</dependency>
Copy the code

HandlerMapping for handwritten MVC

To give you a better understanding, I first posted pictures from my last blog post.

That is to say, when the user sends a request, we need to traverse the collection of HandlerMapping in doDispatch() in DispatcherServlet, and then get the processor that can handle the request from the single map and finally call the processing method to return it. Leaving the Handler aside, all we need to do now is create a HandlerMapping class (which must contain a method to get the Handler and a mapping relationship) and define a HandlerMapping collection in the DispatcherServlet.

public interface HandlerMapping {
    // Get the handler
    // Some mappings are not a field, such as SimpleHandlerMapping
    Object getHandler(HttpServletRequest request) throws Exception;
}
Copy the code

Modify the DispatcherServlet code

public class DispatcherServlet extends FrameworkServlet{
    // Define a HandlerMapping collection
    private List<HandlerMapping> handlerMappings = null;

    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       // Process the request}}Copy the code

Initialization of handwritten MVC

Those of you who didn’t read my last article will have questions. Where did this HandlerMapping collection come from?

The answer is in servlets. We know that servlets have initialization functions, and its subclass GenericServlet implements an init() method with no arguments of its own. We just need to implement this initialization method, Then we can either request it or initialize the Web container (depending on your load-on-startup configuration parameter in web.xml).

Let’s initialize the HandlerMapping collection.

public class DispatcherServlet extends FrameworkServlet{

    private List<HandlerMapping> handlerMappings = null;
    
    // Initialize the IOC container
    private void initBeanFactory(ServletConfig config) {
        // Get the contextConfigLocation configured in web. XML that is the path of the spring-mvc file
        String contextLocation = config.getInitParameter("contextConfigLocation");
        // Initialize the container
        BeanFactory.initBeanFactory(contextLocation);
    }

    // Initialize handlerMappings
    private void initHandlerMappings(a) {
        // To get a collection of instances by type you can see the source code for BeanFactory below
        handlerMappings = BeanFactory.getBeansOfType(HandlerMapping.class);
    }
    
    // Initialize the factory handlerMapping when initializing the servlet
    @Override
    public void init(ServletConfig config) throws ServletException {
        initBeanFactory(config);
        initHandlerMappings();
    }

    private Object getHandler(HttpServletRequest request) throws Exception {
        if(handlerMappings ! =null && handlerMappings.size() > 0) {
            // Iterate through the processor map collection and get the corresponding processor
            for (HandlerMapping handlerMapping: handlerMappings) {
                Object handler = handlerMapping.getHandler(request);
                if(handler ! =null) {
                    returnhandler; }}}return null;
    }

    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       // First we iterate through the handlerMapping collection and get the corresponding handler
       // Get the handler
       Object handler = getHandler(request);
       // Handle requests...
       // If there is no adapter as shown above, we can use it directly
       // handler.handlerequest () handles requests directly}}Copy the code

We found that BEFORE initializing the HandlerMapping set, I also initialized the IOC container. The purpose is to hand over the pre-defined processor mapping and other components to the IOC container for management, and then directly fetch them when needed. Here I directly give all the source code in The BeanFactory. I did this by relying on Spring’s own IOC container.

public class BeanFactory {

    // This is the important context class in Spring that contains the Bean factory
    private static ClassPathXmlApplicationContext context;

    // Use the configuration file path to initialize the IOC container
    public static void initBeanFactory(String contextConfigLocation) {
        context = new ClassPathXmlApplicationContext(contextConfigLocation);
    }
    // Get a collection of beans by type
    public static <T> List<T> getBeansOfType(Class
        clazz) {
        ArrayList<T> result = newArrayList<>(); Map<String, ? > beansOfType = context.getBeansOfType(clazz);for (Object object : beansOfType.values()) {
            result.add((T) object);
        }
        return result;
    }
    // Get the beanName collection by type
    public static List<String> getBeanNamesOfType(Class<Object> objectClass) {
        String[] beanNamesForType = context.getBeanNamesForType(objectClass);
        if (beanNamesForType == null) {
            return null;
        } else {
            return newArrayList<>(Arrays.asList(beanNamesForType)); }}// Get the bean type from beanName
    public staticClass<? > getType(String beanName) {return context.getType(beanName);
    }
    // Get the instance bean by type
    public static Object getBean(Class
        clazz) {
        returncontext.getBean(clazz); }}Copy the code

In addition to our handlerMappings, we also need to initialize a collection of HandlerAdapters. In the above diagram, we are processing requests directly through handler. But in MVC you need to go through the middle layer of HandlerAdapter, for the reason I mentioned in the last article, and you can go back to it, it’s all about implementation.

HandlerAdapter for handwritten MVC

First of all we need is clear this is an adapter, if studied design patterns, students can learn about the adapter pattern needs through inheritance or hold on an object to achieve adaptation, and also is a kind of adapter in Spring MVC best practice, I like it also implements a code, you can have a look.

public interface HandlerAdapter {

    // Processing requests returns view information
    // You can see that a handler is passed in here, which is an implementation of the adapter pattern
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;
    
    // Determine whether the adapter supports the processor
    boolean support(Object handler);

}
Copy the code

In this case, the ModelAndView is essentially the view model that will be returned after the final processing, which should be familiar to anyone who has played with Spring MVC. This is how I define ModelAndView.

public class ModelAndView {
    // The model inside is a map
    private Map<String,Object> model = new HashMap<>();
    
    public Map<String, Object> getModel(a) {
        return model;
    }
    // Add attributes
    public void addAttribute(String attributeName, String attributeValue) { model.put(attributeName, attributeValue); }}Copy the code

Now we can get our heads together

This idea is very important!!

  • First we will initialize our IOC container and get the HandlerMapping and HandlerAdapter collection when the Servlet is initialized.
  • When a user sends a request to our doDispatch method, we first iterate through the HandlerMapping collection to get the handler that can handle the request.
  • We then iterate through the collection of HandlerAdapters again to get the processor adapters that fit that processor.
  • We then call the adapter’s handleRequest and return the corresponding ModelAndView view information.

Write MVC complete initialization process and complete processing process

Once we have implemented the HandlerAdapter processor and sorted out the above flow we can implement the complete DispatcherServlet.

public class DispatcherServlet extends FrameworkServlet{
    // Two important components
    private List<HandlerMapping> handlerMappings = null;
    private List<HandlerAdapter> handlerAdapters = null;
    
    // Initialize the IOC container
    private void initBeanFactory(ServletConfig config) {
        // Get the contextConfigLocation configured in web. XML that is the path of the spring-mvc file
        String contextLocation = config.getInitParameter("contextConfigLocation");
        // Initialize the container
        BeanFactory.initBeanFactory(contextLocation);
    }

    // Initialize handlerMappings
    private void initHandlerMappings(a) {
        // To get a collection of instances by type you can see the source code for BeanFactory below
        handlerMappings = BeanFactory.getBeansOfType(HandlerMapping.class);
    }
    // Initialize handlerAdapters
    private void initHandlerAdapters(a) {
        handlerAdapters = BeanFactory.getBeansOfType(HandlerAdapter.class);
    }
    
    // Initialize handlerMapping for the factory when initializing the servlet
    // And HandlerAdapter initialization
    @Override
    public void init(ServletConfig config) throws ServletException {
        initBeanFactory(config);
        initHandlerMappings();
        initHandlerAdapters();
    }
    // Get the handler
    private Object getHandler(HttpServletRequest request) throws Exception {
        if(handlerMappings ! =null && handlerMappings.size() > 0) {
            // Iterate through the processor map collection and get the corresponding processor
            for (HandlerMapping handlerMapping: handlerMappings) {
                Object handler = handlerMapping.getHandler(request);
                if(handler ! =null) {
                    returnhandler; }}}return null;
    }
    // Get the corresponding processor adapter from the processor
    private HandlerAdapter getHandlerAdapter(Object handler) {
        if(handlerAdapters ! =null && handlerAdapters.size() > 0) {
            // Walk through the processor adapter collection to get the adapter that can fit the processor
            for (HandlerAdapter handlerAdapter: handlerAdapters) {
                if (handlerAdapter.support(handler)) {
                    returnhandlerAdapter; }}}return null;
    }

    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       // First we iterate through the handlerMapping collection and get the corresponding handler
       // Get the handler
       Object handler = getHandler(request);
       if(handler ! =null) {
            // Get the processor adapter corresponding to the processor
            HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
            if(handlerAdapter ! =null) {
                // Calling the adapter's handler returns the ModelAndView object
                ModelAndView modelAndView = handlerAdapter.handleRequest(request, response, handler);
                // Here is a simple implementation of the model in the ModelAndView object
                // Returns the page as a string
                if(modelAndView ! =null) {
                    Map<String, Object> model = modelAndView.getModel();
                    PrintWriter writer = response.getWriter();
                    for (String string: model.keySet()) {
                        writer.write(string);
                    }
                    writer.close();
                }

            }
        }
    }
}
Copy the code

We can look at the class structure of the DispatcherServlet to help understand

Handwritten MVC implementation of a simple process

Wrote here, we will find that we so far have not specific HandlerMapping Handler, HandlerAdapter implementation class, so need to handle the request, we have to construct some simple class to be realized.

SimpleHandler

Let’s start by implementing a SimpleHandler.

public interface SimpleHandler {
    // Very simple to handle requests
    void handleRequest(HttpServletRequest request, HttpServletResponse response)throws Exception;
}
Copy the code

We also need an implementation class, which is very simple, and you can see it.

public class AddBookHandler implements SimpleHandler{
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception{
        response.setContentType("text/html; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write("Add book successful!"); writer.close(); }}public class DeleteBookHandler implements SimpleHandler{
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setContentType("text/html; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write("Delete book successfully!"); writer.close(); }}Copy the code

The above two processors simply return two different messages such as add book success and delete book success. And we know that because of the single responsibility principle, our processor is only for processing requests, and we need a scheduling class, which is HandlerMapping.

SimpleHandlerMapping

public class SimpleHandlerMapping implements HandlerMapping {
    @Override
    public Object getHandler(HttpServletRequest request) throws Exception {
        // Get the request URI
        String uri = request.getRequestURI();
        // Get the corresponding handler according to the mapping rule
        if ("/addBook".equals(uri)) {
            return new AddBookHandler();
        } else if ("/deleteBook".equals(uri)) {
            return new DeleteBookHandler();
        }
        return null; }}Copy the code

As its name implies, this is a very simple processor mapping, not even a map field, and the mapping is handled using an if else statement.

Of course, with processor mapping, you also need a processor adapter.

SimpleHandlerAdapter

public class SimpleHandlerAdapter implements HandlerAdapter {
    // Process the request and return the result, null because the request is being processed by the handler
    // just write some strings
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Force to SimpleHandler and call the handler's processing method
        ((SimpleHandler)handler).handleRequest(request, response);
        return null;
    }
    // Check if it is SimpleHandler and return
    @Override
    public boolean support(Object handler) {
        return handler instanceofSimpleHandler; }}Copy the code

This completes the simple processing flow, but we are missing a few steps to configure the corresponding processor, adapter, and processor mapping in the SpringMVC.xml configuration file so that these classes can be handed over to the IOC container for management. And automatic assembly during our DispatcherServlet initialization.

<?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 https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="simpleHandlerMapping" name="simpleHandlerMapping" class="com.lgq.handler.SimpleHandlerMapping"/>
    
    <bean id="simpleHandlerAdapter" name="simpleHandlerAdapter"
          class="com.lgq.adapter.SimpleHandlerAdapter"/>
</beans>
Copy the code

At this point, we can configure Tomcat for the experiment. Here I use the Maven plugin for Tomcat. Just configure the plugin in Maven’s POM.xml and modify the startup parameters.

<plugin>
  <groupId>org.apache.tomcat.maven</groupId>
  <artifactId>tomcat7-maven-plugin</artifactId>
  <version>2.2</version>
  <configuration>
    <port>8080</port>
    <path>/</path>
  </configuration>
</plugin>
Copy the code

So we can have fun accessing the test.

Handwritten MVC annotation form processing flow

@Controller and @RequestMapping are annotations that we use in everyday application development. How does it work? I’m going to generalize a little bit here, but you can read my last post for details.

When the Servlet is initialized, the @Controller annotated class is first loaded into the IOC container, and then the class is evaluated for the @requestMapping annotation. If it does, it will wrap the method into a HandlerMethod. Note that this HandlerMethod is also a handler, and that the method in this HandlerMethod will hold the annotation annotation, which will eventually be called by reflection.

Now that you know the process, let’s start writing one by hand!

Custom annotations

First we’ll define @Controller and @RequestMapping annotations ourselves. If you’re not familiar with custom annotations, go back and review them.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
    String value(a) default "";
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String value(a) default "";
}
Copy the code

HandlerMethod

Holding the bean instance in the HandlerMethod, the bean type, and the method referenced by @RequestMapping are the three elements that reflection must have. Here I use Lombok annotations directly to simplify getter setter methods.

@Data
@AllArgsConstructor
public class HandlerMethod {
    private Object bean;
    privateClass<? > beanType;private Method method;
}
Copy the code
<! -- Lombok configuration -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.8</version>
  <scope>provided</scope>
</dependency>
Copy the code

RequestMappingHanlderMapping

This is a very important class because it involves retrieving the corresponding Controller annotated class from the IOC container

public class RequestMappingHandlerMapping implements HandlerMapping {
    // store the @requestMapping URL and
    // The enclosing HandlerMethod instance of the method annotated by @requestMapping
    private Map<String, HandlerMethod> urlMap = new HashMap<>();
    // This initialization method is important and is required in the springmVC.xml configuration file
    // Configure the init-method parameter
    public void init(a) {
        // First get the beanNames of all objects from the IOC container
        List<String> beanNames = BeanFactory.getBeanNamesOfType(Object.class);
        // Get the corresponding clazz from beanName
        if(beanNames ! =null) {
            for(String beanName: beanNames) { Class<? > clazz = BeanFactory.getType(beanName);// Determine whether the clazz object is a processor, and if so, process it
                if(clazz ! =null && isHandler(clazz)) {
                    // Get all methods of the class
                    Method[] methods = clazz.getDeclaredMethods();
                    // Iterate through all the methods and determine if there is a method annotating RequestMapping
                    for (Method method : methods) {
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            // Get the annotation of the method and the annotation name value followed by the map key
                            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                            String value = requestMapping.value();
                            // Create handlerMethod by beanName, clazz, method
                            HandlerMethod handlerMethod = new HandlerMethod(beanName, clazz, method);
                            // Store the map
                            urlMap.put(value, handlerMethod);
                        }
                    }
                }
            }
        }

    }
    
    // Check whether the annotation on the class exists in Controller or RequestMapping
    private boolean isHandler(Class
        clazz) {
        return clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(RequestMapping.class);
    }

    @Override
    public Object getHandler(HttpServletRequest request) throws Exception {
        // Find the corresponding handler in the urlMap generated by init in the initialization method call
        returnurlMap.get(request.getRequestURI()); }}Copy the code

There also needs to be configured for springmvc. XML configuration files, here speak RequestMappingHandlerAdapter this class first, later the configuration file is given together.

RequestMappingHandlerAdapter

public class RequestMappingHandlerAdapter implements HandlerAdapter{
    // Process the request and return ModelAndView
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        ModelAndView modelAndView = null;
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        Method method = handlerMethod.getMethod();
        if(method ! =null) {
            // Call the method through reflection and encapsulate the return result into a ModelAndView object
            modelAndView = (ModelAndView)method.invoke(BeanFactory.getBean(((HandlerMethod) handler).getBeanType()));
        }
        return modelAndView;
    }
    // Is it a HandlerMethod
    @Override
    public boolean support(Object handler) {
        return handler instanceofHandlerMethod; }}Copy the code

The springmVC.xml configuration file

We also need to configure these classes once they are written, because loading them into IOC is a very, very important thing.

<?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 https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="simpleHandlerMapping" name="simpleHandlerMapping" class="com.lgq.handler.SimpleHandlerMapping"/>
    <! - the configuration RequestMappingHandlerMapping -- -- >
    <! Init-method -->
    <! You also need to note that lazy loading is used here, because during initialization we -->
    <! We need to use the IOC container, so we need to wait until the IOC initialization is complete before we initialize the bean.
    <bean id="requestMappingHandlerMapping" name="requestMappingHandlerMapping"
          class="com.lgq.handler.RequestMappingHandlerMapping" lazy-init="true" init-method="init"/>
    <! - the configuration RequestMappingHandlerAdapter -- -- >
    <bean id="requestMappingHandlerAdapter" name="RequestMappingHandlerAdapter"
          class="com.lgq.adapter.RequestMappingHandlerAdapter"/>

    <bean id="simpleHandlerAdapter" name="simpleHandlerAdapter"
          class="com.lgq.adapter.SimpleHandlerAdapter"/>
    <! -- Let IOC scan the test Controller we need to write later -->
    <context:component-scan base-package="com.lgq.handler"/>
    
</beans>
Copy the code

Write a test Controller and experiment

// This is an annotation of Spring itself
// The main thing here is to load the class into the container
// Because the annotations we write will be the same as spring itself
// @Controller conflicts, so @Component is used
// corresponds to the configuration file above
// <context:component-scan base-package="com.lgq.handler"/>
@Component
// This is our own annotation
@Controller
public class HelloWorldHandler {
    // This is our annotation
    @RequestMapping(value = "/helloWorld")
    public ModelAndView helloWorld(a) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addAttribute("hello!!!"."World!!!");
        returnmodelAndView; }}Copy the code

Let’s visit and test it

Success!!

Great, we implemented a simple Spring MVC ourselves.

This is a catalog of our entire project

conclusion

In this project, we didn’t implement the message converter, but we actually did it stealthily when we wrote again (because the response was sent to the browser before we returned the ModelAndView). HttpMessageConvert class HttpMessageConvert class HttpMessageConvert class

In fact, the whole basic flow of Spring MVC is not complicated. It is nothing more than a distributor to handle requests. There are some separation of responsibilities, such as Handler, HandlerAdapter, HandlerMapping, etc. I hope you have a clear idea of the overall flow of Spring MVC by the end of this article. Here I will put up the whole flow chart for your understanding.

This project all code is hosted on https://github.com/FrancisQiang/spring-mvc-custom, anyone interested in to the fork.

Thanks for reading!