Пошаговое руководство по написанию простого Spring MVC

Spring

Если вы не понимаете основных принципов сервлета и Spring MVC, вы можете сначала прочитать другую мою статью.Шаг за шагом вы разорвете исходный код Spring MVC и нарисованную вручную блок-схему.. Вернитесь и прочитайте эту статью еще раз, и вы будете просветлены.

Эта статья в основном касается реализации кода,Многие моменты будут объяснены в комментариях к коду., Пожалуйста, прочитайте внимательно.

Весь код уже вhttps://github.com/FrancisQiang/spring-mvc-customНа хостинге заинтересованные студенты могут раскошелиться самостоятельно.

FrameworkServlet рукописного MVC

Я упомянул в предыдущем постеFrameworkServletЯвляется самым важным классом Spring MVC.DispatcherServletизпрямой родительский класс, его основные функции:Обеспечивает функцию загрузки соответствующей среды веб-приложения и передает GET, POST, DELETE, PUT и другие методы DispatcherServlet для обработки.

Короче говоря, некоторые методы doXxx равномерно передаются на обработку определенному методу DispatcherServlet.Что это за метод? На самом деле, это известноdoDispatch().

Мы можем добиться простейшегоFrameworkServletДобрый.

// 这里必须继承 HttpServlet 如果不懂的可以去看上一篇文章
public class FrameworkServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            // 使用doDispatch方法处理请求
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            // 使用doDispatch方法处理请求
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception{
        // 重点在这里,主要由 DispatcherServlet 实现
        // 当然你这里可以使用抽象方法并且将 FrameworkServlet 定义成抽象类
        // 在 Spring MVC 中的源码也是这么做的
    }
    
}

Конечно, чтобы наследовать HttpServlet, нужно импортировать пакет в pom.xml в проекте maven, а также можно импортировать пакет другими способами.

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

То есть,FrameworkServletпредоставить только одинМетод шаблона(Студенты, которые не понимают шаблоны проектирования, могут взглянуть. Это распространенный, но очень простой шаблон проектирования для фреймворков), и окончательная реализация все еще находится в разработке.DispatcherServletсерединаdoDispatch()метод.

DispatcherServlet оригинальная версия рукописного MVC

Сначала нам нужно наследовать класс FrameworkServlet

public class DispatcherServlet extends FrameworkServlet{
    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 处理请求
    }
}

Это очень просто наследовать, а затем реализовать, но мы, кажется, забываем одну вещь, мыНам нужно настроить наш DispatcherServlet в web.xml.

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!-- 配置我们的DispatcherServlet -->
  <servlet>
    <!-- 指定类 -->
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>com.lgq.servlet.DispatcherServlet</servlet-class>
    <!-- 配置初始化参数,这里需要标注我们的spring mvc配置文件路径,后面我会讲 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>springmvc.xml</param-value>
    </init-param>
    <!-- 初始化策略 -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!-- DispatcherServlet核心 拦截所有请求 -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

На этот раз нам нужно создать файл конфигурации MVC.springmvc.xml(Обратите внимание, что путь очень важен), на самом деле этофайл конфигурации весны, основная причина, по которой он нам нужен, этоНам нужно загрузить карту процессора, которую мы написали позже, адаптер процессора в контейнер IOC, и мы инициализируем контейнер при инициализации сервлета, чтобы получить соответствующий компонент..

<?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">
        <!-- 很简单,现在什么都没实现 -->
</beans>

Чтобы использовать функцию IOC Spring, нам нужно импортировать соответствующий пакет в Maven POM.xml. Импорт основных и контекстных пакетов

Помните: функции MVC основаны на IOC, поэтому IOC является основной функцией 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>

HandlerMapping рукописного MVC

Для вашего лучшего понимания я сначала вставлю картинку из моего последнего поста в блоге.

То есть, когда пользователь отправляет запрос, нам нужен диспетчер в DispatcherServlet.doDispatch()серединаПройдите коллекцию HandlerMapping, а затем получите обработчик, который может обработать запрос из одного сопоставления, и, наконец, вызовите метод обработки для возврата. отложить в сторонуHandlerНесмотря на это, теперь все, что нам нужно сделать, это создатьHandlerMappingкласс (этот класс должен содержать метод для получения обработчика и отношения сопоставления), а затем вDispatcherServletопределитьКоллекция HandlerMapping.

public interface HandlerMapping {
    // 获取处理器
    // 映射关系需要子类自己去实现,有些映射关系就不是一个字段 如SimpleHandlerMapping
    Object getHandler(HttpServletRequest request) throws Exception;
}

Изменить код DispatcherServlet

public class DispatcherServlet extends FrameworkServlet{
    // 定义一个 HandlerMapping 集合
    private List<HandlerMapping> handlerMappings = null;

    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       // 处理请求
    }

}

Инициализация рукописного MVC

Если вы не читали мою последнюю статью, у вас обязательно возникнут вопросы.Откуда взялась эта коллекция HandlerMapping?

Ответ лежит в сервлете, мы знаем, что в сервлете есть функция инициализации, а его подкласс GenericServlet реализует метод init() без параметров, нам нужно только реализовать этот метод инициализации, тогда мы можем запросить или веб-приложение контейнер инициализируется (в зависимости от вашего параметра конфигурации загрузки при запуске в web.xml).

Давайте реализуем этоКоллекция HandlerMappingинициализация.

public class DispatcherServlet extends FrameworkServlet{

    private List<HandlerMapping> handlerMappings = null;
    
    // 初始化 IOC 容器
    private void initBeanFactory(ServletConfig config) {
        // 获取web.xml中配置的contextConfigLocation 即spring-mvc文件路径
        String contextLocation = config.getInitParameter("contextConfigLocation");
        // 初始化容器
        BeanFactory.initBeanFactory(contextLocation);
    }

    // 初始化handlerMappings
    private void initHandlerMappings() {
        // 通过类型去获取实例集合 你可以看下面BeanFactory的源码
        handlerMappings = BeanFactory.getBeansOfType(HandlerMapping.class);
    }
    
    // 在初始化servlet的时候进行 工厂的初始化 handlerMapping初始化
    @Override
    public void init(ServletConfig config) throws ServletException {
        initBeanFactory(config);
        initHandlerMappings();
    }

    private Object getHandler(HttpServletRequest request) throws Exception {
        if (handlerMappings != null && handlerMappings.size() > 0) {
            // 遍历处理器映射集合并获取相应的处理器
            for (HandlerMapping handlerMapping: handlerMappings) {
                Object handler = handlerMapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       // 首先我们要遍历 handlerMapping 集合并获取相应的处理器
       // 获取处理器
       Object handler = getHandler(request);
       // 处理请求。。。。
       // 如果没有适配器就是上面的图的话 我们可以直接使用
       // handler.handleRequest() 这样的方法直接处理请求
    }
}

Мы обнаружили, что проводились до инициализации коллекции по руководствам.Инициализация контейнера IOC, цель состоит в том, чтобыПредварительно определенное сопоставление процессоров и другие компоненты передаются в контейнер IOC для управления, а затем напрямую извлекаются при необходимости., тут я прямо даюBeanFactoryВесь исходный код в , среди которых я полагаюсь на собственный IOC-контейнер Spring для его реализации.

public class BeanFactory {

    // 这个就是 Spring 中很重要的 上下文类 其中包含了 Bean工厂 
    private static ClassPathXmlApplicationContext context;

    // 通过配置文件路径进行 IOC 容器的初始化
    public static void initBeanFactory(String contextConfigLocation) {
        context = new ClassPathXmlApplicationContext(contextConfigLocation);
    }
    // 通过类型获取bean集合
    public static <T> List<T> getBeansOfType(Class<?> clazz) {
        ArrayList<T> result = new ArrayList<>();
        Map<String, ?> beansOfType = context.getBeansOfType(clazz);
        for (Object object : beansOfType.values()) {
            result.add((T) object);
        }
        return result;
    }
    // 通过类型获取beanName集合
    public static List<String> getBeanNamesOfType(Class<Object> objectClass) {
        String[] beanNamesForType = context.getBeanNamesForType(objectClass);
        if (beanNamesForType == null) {
            return null;
        } else {
            return new ArrayList<>(Arrays.asList(beanNamesForType));
        }
    }
    // 通过beanName获取bean类型
    public static Class<?> getType(String beanName) {
        return context.getType(beanName);
    }
    // 通过类型获取实例bean
    public static Object getBean(Class<?> clazz) {
        return context.getBean(clazz);
    }

}

Конечно, не только у нас.handlerMappings, нам также нужно инициализироватьHandlerAdapterСбор, на рисунке выше мы обрабатываем запрос напрямую через обработчик, а в MVC необходимо пройтиHandlerAdapterСредний слой, конкретная причина, по которой я нахожусь впредыдущий постКак упоминалось в , вы можете просмотреть его, и в основном это связано с реализацией.

Рукописный MVC HandlerAdapter

Прежде всего, мы должны четко понимать, что этоадаптер, если студенты, изучавшие шаблоны проектирования, смогут понятьШаблон адаптера необходимо адаптировать к объекту посредством наследования или удержания, и Spring MVC также является лучшей практикой для адаптеров, я также реализовал один после имитации его кода, вы можете взглянуть.

public interface HandlerAdapter {

    // 处理请求返回视图信息
    // 你可以看见这里传入了一个 handler 这里就是适配器模式的一种实现
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;
    
    // 判断该适配器是否支持该处理器
    boolean support(Object handler);

}

Здесь ModelAndView — это то, что будет возвращено после окончательной обработки.просмотреть модель, Люди, которые играли в Spring MVC, должны быть знакомы с ним. Вот как я определяю ModelAndView.

public class ModelAndView {
    // 里面的model其实就是一个 map
    private Map<String,Object> model = new HashMap<>();
    
    public Map<String, Object> getModel() {
        return model;
    }
    // 添加属性
    public void addAttribute(String attributeName, String attributeValue) {
        model.put(attributeName, attributeValue);
    }
}

Теперь мы можем подумать об этом

Эта идея очень важна! ! !

  • Во-первых, мы инициализируем сервлет, когдаИнициализируем наш контейнер IOCи получитьКоллекция HandlerMapping и HandlerAdapter.
  • Когда пользователь отправляет запрос, он достигает нашего метода doDispatch, в котором мы сначалаПросмотрите коллекцию HandlerMapping, чтобы получить обработчик, который может обработать запрос..
  • тогда мы сноваПросмотрите коллекцию HandlerAdapter, чтобы получить адаптер обработчика, который может адаптироваться к обработчику..
  • тогда мы будемВызовите handleRequest адаптера и верните соответствующую информацию о представлении ModelAndView..

Полный процесс инициализации и полный процесс обработки рукописного MVC

после осознанияHandlerAdapterПосле процессора и разъяснения вышеизложенных идей потока обработки мы можем достичьПолный DispatcherServlet.

public class DispatcherServlet extends FrameworkServlet{
    // 两个重要的组件
    private List<HandlerMapping> handlerMappings = null;
    private List<HandlerAdapter> handlerAdapters = null;
    
    // 初始化 IOC 容器
    private void initBeanFactory(ServletConfig config) {
        // 获取web.xml中配置的contextConfigLocation 即spring-mvc文件路径
        String contextLocation = config.getInitParameter("contextConfigLocation");
        // 初始化容器
        BeanFactory.initBeanFactory(contextLocation);
    }

    // 初始化handlerMappings
    private void initHandlerMappings() {
        // 通过类型去获取实例集合 你可以看下面BeanFactory的源码
        handlerMappings = BeanFactory.getBeansOfType(HandlerMapping.class);
    }
    // 初始化handlerAdapters
    private void initHandlerAdapters() {
        handlerAdapters = BeanFactory.getBeansOfType(HandlerAdapter.class);
    }
    
    // 在初始化servlet的时候进行 工厂的初始化 handlerMapping
    // 和HandlerAdapter的初始化
    @Override
    public void init(ServletConfig config) throws ServletException {
        initBeanFactory(config);
        initHandlerMappings();
        initHandlerAdapters();
    }
    // 获取处理器
    private Object getHandler(HttpServletRequest request) throws Exception {
        if (handlerMappings != null && handlerMappings.size() > 0) {
            // 遍历处理器映射集合并获取相应的处理器
            for (HandlerMapping handlerMapping: handlerMappings) {
                Object handler = handlerMapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
    // 通过处理器获取相应的处理器适配器
    private HandlerAdapter getHandlerAdapter(Object handler) {
        if (handlerAdapters != null && handlerAdapters.size() > 0) {
            // 遍历处理器适配器集合获取能够适配处理器的适配器
            for (HandlerAdapter handlerAdapter: handlerAdapters) {
                if (handlerAdapter.support(handler)) {
                    return handlerAdapter;
                }
            }
        }
        return null;
    }

    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       // 首先我们要遍历 handlerMapping 集合并获取相应的处理器
       // 获取处理器
       Object handler = getHandler(request);
       if (handler != null) {
            // 获取处理器对应的处理器适配器
            HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
            if (handlerAdapter != null) {
                // 调用适配器的处理方法返回ModelAndView对象
                ModelAndView modelAndView = handlerAdapter.handleRequest(request, response, handler);
                // 这里简单实现了一下将ModelAndView对象中的model
                // 作为字符串返回页面
                if (modelAndView != null) {
                    Map<String, Object> model = modelAndView.getModel();
                    PrintWriter writer = response.getWriter();
                    for (String string: model.keySet()) {
                        writer.write(string);
                    }
                    writer.close();
                }

            }
        }
    }
}

Мы можем взглянуть на структуру классов DispatcherServlet, чтобы лучше понять

Рукописный MVC для упрощения процесса обработки.

Написав здесь, мы найдем,У нас пока нет конкретных HandlerMapping, Handler, HandlerAdapterКласс реализации, поэтому нам нужно создать несколько простых классов для реализации запроса.

SimpleHandler

Сначала мы реализуемSimpleHandler.

public interface SimpleHandler {
    // 很简单的处理请求
    void handleRequest(HttpServletRequest request, HttpServletResponse response)throws Exception;
}

Нам также нужен класс реализации, который очень прост, вы можете понять его с первого взгляда.

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("添加书本成功!");
        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("删除书本成功!");
        writer.close();
    }
}

Вышеупомянутые два процессора реализованы просто для возврата двух разных сведений, таких как успешное добавление книги и успешное удаление книги. и мы знаем это, потому чтоПринцип единой ответственности, наш процессор используется только для обработки запросов, и нам также нужен класс планирования, то естьHandlerMapping.

SimpleHandlerMapping

public class SimpleHandlerMapping implements HandlerMapping {
    @Override
    public Object getHandler(HttpServletRequest request) throws Exception {
        // 获取请求的uri
        String uri = request.getRequestURI();
        // 根据映射规则获取对应的handler
        if ("/addBook".equals(uri)) {
            return new AddBookHandler();
        } else if ("/deleteBook".equals(uri)) {
            return new DeleteBookHandler();
        }
        return null;
    }
}

Как следует из названия, это очень простая карта-обработчик, в которой даже нет поля карты.Обработка сопоставления обрабатывается оператором if else..

Конечно, с картой процессора, процессор, у вас должен бытьадаптер процессора.

SimpleHandlerAdapter

public class SimpleHandlerAdapter implements HandlerAdapter {
    // 处理请求并返回结果,这里为null是因为在处理器处理请求的
    // 时候仅仅是写入一些字符串就返回了
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 强转为 SimpleHandler然后调用处理器的处理方法
        ((SimpleHandler)handler).handleRequest(request, response);
        return null;
    }
    // 判断是否是 SimpleHandler 然后返回
    @Override
    public boolean support(Object handler) {
        return handler instanceof SimpleHandler;
    }
}

Таким образом, мы написали весь простой поток обработки, но у нас осталось на несколько шагов меньше, т.е.Настройте соответствующие процессоры, адаптеры и сопоставления процессоров в файле конфигурации springmvc.xml, чтобы эти классы можно было передать в контейнер IOC для управления и автоматического подключения при инициализации нашего DispatcherServlet..

<?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>

В настоящее время мы можем настроить Tomcat для тестирования, здесь я использую плагин Tomcat Maven, просто настройте плагин в POM.xml Maven, а затем измените параметры запуска, здесь у меня нет больше введения, я не смогу использовать Baidu .

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

Таким образом, мы можем с радостью получить доступ к тестам.

Поток обработки формы аннотации рукописного MVC

При ежедневной разработке приложений мы все сталкиваемся с@Контроллер и @RequestMappingЭти аннотации, и как это реализовано? Я дам краткий обзор здесь, для деталей вы можете прочитать мойпредыдущий пост.

При инициализации сервлет сначала@ControllerАннотированный класс загружается в контейнер IOC, а затем оценивается метод этого класса, чтобы определить, есть ли какие-либо@RequestMappingАннотация, если она есть, этот метод будет инкапсулирован вHandlerMethod, обратите внимание, что этот HandlerMethod также является процессором, метод в этом HandlerMethod будет содержать метод, отмеченный аннотацией, и в конечном итоге пройдетотражениепозвонить.

Теперь, когда мы знаем процесс обработки, давайте начнем писать его вручную!

пользовательская аннотация

Сначала мы должны определить@Controllerа также@RequestMappingаннотация. Если вы не знакомы с пользовательскими аннотациями, вы можете вернуться и просмотреть их.

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

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

HandlerMethod

существуетHandlerMethodВ нем хранятся экземпляр компонента, тип компонента и ссылка на метод, аннотированные @RequestMapping, это три основных элемента отражения. Здесь я использую аннотацию Lombok напрямую, чтобы упростить метод установки геттера.

@Data
@AllArgsConstructor
public class HandlerMethod {
    private Object bean;
    private Class<?> beanType;
    private Method method;
}
<!-- lombok配置 -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.8</version>
  <scope>provided</scope>
</dependency>

RequestMappingHanlderMapping

Это очень важный класс, потому что он включает в себяПолучите соответствующий класс, помеченный аннотацией Controller, из контейнера IOC.

public class RequestMappingHandlerMapping implements HandlerMapping {
    // 存放了 @RequestMapping中的url和
    // 被@RequestMapping标注了的方法的封装类 HandlerMethod 实例
    private Map<String, HandlerMethod> urlMap = new HashMap<>();
    // 该初始化方法很重要,这个需要在 springmvc.xml 配置文件
    // 中配置 init-method 参数
    public void init() {
        // 首先从IOC容器中获取所有对象的beanNames
        List<String> beanNames = BeanFactory.getBeanNamesOfType(Object.class);
        // 遍历beanNames通过beanName获取相应的clazz
        if (beanNames != null) {
            for (String beanName: beanNames) {
                Class<?> clazz = BeanFactory.getType(beanName);
                // 判断clazz对象是否是处理器,如果是则处理
                if (clazz != null && isHandler(clazz)) {
                    // 获取该类所有方法
                    Method[] methods = clazz.getDeclaredMethods();
                    // 遍历所有方法并判断存在注解RequestMapping的方法
                    for (Method method : methods) {
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            // 获取该方法的注解并获取该注解的名称value 后面作为map的key
                            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                            String value = requestMapping.value();
                            // 通过beanName, clazz, method创将handlerMethod
                            HandlerMethod handlerMethod = new HandlerMethod(beanName, clazz, method);
                            // 存入映射
                            urlMap.put(value, handlerMethod);
                        }
                    }
                }
            }
        }

    }
    
    // 判断类上的注解是否存在 Controller 或者 RequestMapping
    private boolean isHandler(Class<?> clazz) {
        return clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(RequestMapping.class);
    }

    @Override
    public Object getHandler(HttpServletRequest request) throws Exception {
        // 通过初始化方法调用的init生成的urlMap中寻找对应的处理器
        return urlMap.get(request.getRequestURI());
    }
}

Также необходимо настроить файл конфигурации springmvc.xml Здесь сначала поговорим о классе RequestMappingHandlerAdapter, который будет предоставлен вместе с файлом конфигурации.

RequestMappingHandlerAdapter

public class RequestMappingHandlerAdapter implements HandlerAdapter{
    // 处理相应的请求并返回 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) {
            // 通过反射调用方法,并将返回结果封装成ModelAndView对象
            modelAndView = (ModelAndView)method.invoke(BeanFactory.getBean(((HandlerMethod) handler).getBeanType()));
        }
        return modelAndView;
    }
    // 是否是HandlerMethod
    @Override
    public boolean support(Object handler) {
        return handler instanceof HandlerMethod;
    }
}

файл конфигурации springmvc.xml

После написания этих классов нам также необходимо настроить, т.к.Поместить их в МОК — очень и очень важная вещь..

<?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"/>
    <!-- 配置RequestMappingHandlerMapping -->
    <!-- 这里还配置了 init-method -->
    <!-- 你还需要注意的是 这里使用了懒加载,因为在初始化过程中我们 -->
    <!-- 需要使用 IOC 容器,所以需要等到IOC初始化完成再初始化这个bean -->
    <bean id="requestMappingHandlerMapping" name="requestMappingHandlerMapping"
          class="com.lgq.handler.RequestMappingHandlerMapping" lazy-init="true" init-method="init"/>
    <!-- 配置RequestMappingHandlerAdapter -->
    <bean id="requestMappingHandlerAdapter" name="RequestMappingHandlerAdapter"
          class="com.lgq.adapter.RequestMappingHandlerAdapter"/>

    <bean id="simpleHandlerAdapter" name="simpleHandlerAdapter"
          class="com.lgq.adapter.SimpleHandlerAdapter"/>
    <!-- 让IOC扫描我们后面需要写的测试Controller -->
    <context:component-scan base-package="com.lgq.handler"/>
    
</beans>

Напишите тестовый контроллер и поэкспериментируйте

// 这个是spring本身的注解
// 这里主要就是将这个类加载到容器中
// 因为我们本身写的注解会跟spring本身
// 的@Controller有冲突,这里所以使用@Component
// 对应着上面配置文件中的
// <context:component-scan base-package="com.lgq.handler"/>
@Component
// 这是我们自己写的注解哦
@Controller
public class HelloWorldHandler {
    // 这是我们写的注解哦
    @RequestMapping(value = "/helloWorld")
    public ModelAndView helloWorld() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addAttribute("hello!!!", "World!!!");
        return modelAndView;
    }
}

Посетим тест

успех! ! !

Отлично, мы сами реализовали простой Spring MVC.

Это каталог всего нашего проекта

Суммировать

В этом проекте мы не реализоваликонвертер сообщенийЭта функция, на самом деле, когда мы пишем снова, мы незаметно реализовали преобразование сообщений (потому что мы отвечаем браузеру перед возвратом в ModelAndView). Конечно, в моей прошлой статье былоHttpMessageConvertЭтот класс выполняет анализ исходного кода, и заинтересованные студенты могут посмотреть.

На самом деле, основной процесс всего Spring MVC не сложен.Это не что иное, как диспетчер для обработки запросов, в котором разделены некоторые обязанности, такие как Handler, HandlerAdapter, HandlerMapping и т. д., удаление которых является процессом обработки запросов.. Я надеюсь, что после прочтения этой статьи у вас будет четкое представление об общем процессе Spring MVC.Здесь я повешу всю блок-схему для вашего понимания.

Весь код этого проекта уже находится вhttps://github.com/FrancisQiang/spring-mvc-customНа хостинге заинтересованные студенты могут раскошелиться самостоятельно.

Спасибо за прочтение!