• 手写 简易 Spring MVC xml


    手写实现简易的Spring MVC xml版本。

    代码:https://github.com/kuotian/springmvc_me

    1. 用户请求:localhostaddUser2
    2. DispatcherServlet接收请求,通过HandlerMapping去查找合适的Handler
    3. 根据处理器获取相应的适配器,执行相应的处理器的方法去处理请求
    4. 返回执行结果

    1. 使用Servlet实现

    localhost/addUser

    package com.hdu.springmvc.servlet;
    
    public class AddUserServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/plain;charset=utf8");
            resp.getWriter().write("添加成功");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

    localhost/queryUser

    package com.hdu.springmvc.servlet;
    
    public class QueryUserServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/plain;charset=utf8");
            resp.getWriter().write("查询成功");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

    2. 使用Servlet的弊端 及 解决方案

    分析使用Servlet实现BS结构服务处理的弊端
    1. 对于程序员来说,会编写大量的Servlet代码,很不爽。
    2. Servlet是由tomcat产生的,产生过多的Servlet对象,会导致tomcat本身启动的时候会有压力。
    解决方案
    1. 编写Servlet基类BaseServlet,抽取相同的逻辑。业务处理逻辑写在子类的Servlet。

      请求url:
      localhost/userSersvlet?method=add
      localhost/userSersvlet?method=query

      BaseServlet{
      	doGet(){
      		获取method参数值
      		根据参数值调用响应的方法
      	}
      }
      	
      UserServlet extends BasServlet{
      	add(){}
      	query(){}
      }
      
    2. 编写全局Servlet去接收所有Servlet请求,然后在该Servlet中进行请求分发,由处理器类去处理具体的请求。 ---> Spring MVC

      其中,处理器类不需要实现Servlet接口,它只是一个普通的JavaBean。不需要产生额外的Servlet类。

      DispatcherServlet{
      	doGet(){
      		url
      		if("/queryUser".equals(url)){
      			找相应的处理器去处理
      		}
      	}
      }
      //接口(标准或者规范)
      UserHandler{
      	xxx
      }
      

    3. 手写简易Spring MVC

    DispatcherServlet

    作用:

    ​ 接收请求、响应结果
    ​ 查找处理器
    ​ 执行相应的处理器(调用处理器的方法去处理请求)

    AbstractHttpServlet
    package com.hdu.springmvc.servlet;
    
    public abstract class AbstractHttpServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doDispatch(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
        
        /**
         * 抽象模板设计模式的钩子方法
         */
        public abstract void doDispatch(HttpServletRequest request, HttpServletResponse response);
    }
    
    DispatcherServlet
    package com.hdu.springmvc.servlet;
    public class DispatcherServlet extends AbstractHttpServlet {
    
            private DefaultListableBeanFactory beanFactory;
            // 处理器映射器的策略集合
            private List<HandlerMapping> handlerMappings;
            // 处理器适配器的策略集合
            private List<HandlderAdapter> handlerAdapters;
    
            public void init(ServletConfig config){
                // 从web.xml中获取springmvc的配置文件路径
                String contextConfigLocation = config.getInitParameter("contextConfigLocation");
                // 初始化spring容器,需要提前一次性初始化bean实例
                initSpringContainer(contextConfigLocation);
                // 初始化策略集合
                initHandlerMappings();
                initHandlerAdapters();
            }
    
            private void initSpringContainer(String contextConfigLocation) {
                beanFactory = new DefaultListableBeanFactory();
                XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
                Resource resource = new ClassPathResource(contextConfigLocation);
                reader.loadBeanDefinitions(resource);
            }
            private void initHandlerMappings() {
                // 从spring容器中,初始化所有的HandlerMapping的策略
                handlerMappings = new ArrayList<>(beanFactory.getBeansOfType(HandlerMapping.class).values());
            }
            private void initHandlerAdapters() {
                handlerAdapters = new ArrayList<>(beanFactory.getBeansOfType(HandlderAdapter.class).values());
            }
    
        @Override
        public void doDispatch(HttpServletRequest request, HttpServletResponse response) {
            try {
                // 根据请求查找处理器类(HandlerMapping)
                Object handler = getHandler(request);
                // 去执行处理器类的方法(HandlerAdapter)
    //            // 使用策略模式+适配器模式去优化以下代码
    //            if (handler instanceof HttpRequestHandler) {
    //                ((HttpRequestHandler) handler).handleRequest(request, response);
    //            } else if (handler instanceof SimpleControllerHandler) {
    //                ((SimpleControllerHandler) handler).handleRequest(request, response);
    //            }
                if(handler == null){
                    return;
                }
                // 先找处理器适配器
                HandlderAdapter ha = getHandlerAdapter(handler);
                if (ha == null) {
                    return;
                }
                // 再去调用处理器适配器的统一处理请求方法
                ModelAndView mv = ha.handleRequest(handler, request, response);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private Object getHandler(HttpServletRequest request) throws Exception {
            String uri = request.getRequestURI();
    
            // 使用策略模式
            if (handlerMappings != null && handlerMappings.size() > 0) {
                for (HandlerMapping handlerMapping : handlerMappings) {
                    Object handler = handlerMapping.getHandler(request);
                    if (handler != null) {
                        return handler;
                    }
                }
            }
            return null;
        }
    
        private HandlderAdapter getHandlerAdapter(Object handler) {
            // 如果HandlerAdapter1能处理handler,则返回HandlerAdapter1
            // 如果HandlerAdapter2能处理handler,则返回HandlerAdapter2
            // 如果HandlerAdapter3能处理handler,则返回HandlerAdapter3
            if (handlerAdapters != null && handlerAdapters.size() > 0) {
                for (HandlderAdapter handlderAdapter : handlerAdapters) {
                    // 判断该处理器适配器是否支持该处理器
                    if (handlderAdapter.supports(handler)) {
                        return handlderAdapter;
                    }
                }
            }
            return null;
        }
    }
    

    HandlerMapping

    作用:请求映射和查找请求

    建立映射的方式有多种,所以说HandlerMapping应该有多个。每个HandlerMapping负责的映射来源是不一样的。

    • BeanNameUrlHandlerMapping:通过bean标签的name属性和bean标签对应的实例建立映射关系
    • SimpleUrlHandlerMapping:手动建立映射。(充数用)

    HandlerMapping
    package com.hdu.springmvc.handlermapping.iface;
    
    // 定义处理器映射器的编写规范
    public interface HandlerMapping {
        /**
         * 根据请求查找处理器
         * @param request
         * @return
         * @throws Exception
         */
        Object getHandler(HttpServletRequest request) throws Exception;
    }
    
    BeanNameUrlHandlerMapping
    package com.hdu.springmvc.handlermapping;
    
    // 通过bean标签的name属性和bean标签对应的实例建立映射关系
    public class BeanNameUrlHandlerMapping implements HandlerMapping, BeanFactoryAware {
    
        private DefaultListableBeanFactory beanFactory;
        // uri和处理器对象的映射集合
        private Map<String, Object> mappings = new HashMap<>();
    
        /**
         * BeanNameUrlHandlerMapping的初始化方法
         */
        public void init(){
            // 从spring容器中(BeanFactory)获取所有的handler的BeanDefinition对象
            // Spring 容器中所有 JavaBean 的名称
            String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
            for (String beanDefinitionName : beanDefinitionNames) {
                // 判断bean的name属性,是否是以/开头
                if( beanDefinitionName.startsWith("/") ) {
                    // 取出bean的name属性,并且获取bean实例
                    Object bean = beanFactory.getBean(beanDefinitionName);
                    // 建立映射关系
                    mappings.put(beanDefinitionName, bean);
                }
            }
        }
    
        @Override
        public Object getHandler(HttpServletRequest request) throws Exception {
            String uri = request.getRequestURI();
            return mappings.get(uri);
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = (DefaultListableBeanFactory)beanFactory;
        }
    }
    
    SimpleUrlHandlerMapping
    package com.hdu.springmvc.handlermapping;
    
    public class SimpleUrlHandlerMapping implements HandlerMapping, BeanFactoryAware {
        private DefaultListableBeanFactory beanFactory;
        // uri和处理器对象的映射集合
        private Map<String, Object> mappings = new HashMap<>();
    	
        // 手动填充
        public void init() {
            mappings.put("/addUser3", new AddUserHandler());
            mappings.put("/queryUser3", new QueryUserHandler());
        }
    
        @Override
        public Object getHandler(HttpServletRequest request) throws Exception {
            String uri = request.getRequestURI();
            return mappings.get(uri);
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = (DefaultListableBeanFactory) beanFactory;
        }
    }
    

    HandlerAdapter

    作用:将DispatcherServlet类中使用的Handler类,适配成统一的HandlerAdapter类型。

    HttpRequestHandler ---> HttpRequestHandlerAdapter --->HandlerAdapter 类型

    HandlderAdapter
    package com.hdu.springmvc.handleradapter.iface;
    
    // DispatcherServlet类中希望见到的统一的目标接口
    public interface HandlderAdapter {
        /**
         * 处理请求
         * @param handler 处理器类
         */
        ModelAndView handleRequest(Object handler, HttpServletRequest request, HttpServletResponse response) throws Exception;
    
        /**
         * 就是判断该适配器是否适合该处理器,方便策略模式使用时进行调用
         * @param handler
         */
        boolean supports(Object handler);
    }
    
    HttpRequestHandlerAdapter
    package com.hdu.springmvc.handleradapter;
    
    public class HttpRequestHandlerAdapter implements HandlderAdapter {
        @Override
        public ModelAndView handleRequest(Object handler, HttpServletRequest request, HttpServletResponse response) throws Exception {
            try {
                ((HttpRequestHandler) handler).handleRequest(request, response);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        @Override
        public boolean supports(Object handler) {
            return (handler instanceof HttpRequestHandler);
        }
    }
    

    Handler

    是一个普通的Java类,它不是Servlet类。

    作用:处理请求。

    • HttpRequestHandler接口(规范大家如何编写Handler类去处理请求)

      ​ void handleRequest(request,response);

    • SimpleControllerHandler接口

      ​ ModelAndView handleRequest(request,response);

    对于处理器类的处理结果,有可能还需要处理,则把处理结果和最终显示的视图名称封装到一个对象中。Handler类的编写方式有多种,而且类型都无法统一。需要有个适配器处理。

    SimpleControllerHandler
    // 定义处理器的编写规范
    // 这种规范要求返回一个返回值ModelAndView对象
    public interface SimpleControllerHandler {
        ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
    }
    
    HttpRequestHandler
    public interface HttpRequestHandler {
        void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
    }
    
    AddUserHandler
    package com.hdu.springmvc.handler;
    
    public class AddUserHandler implements HttpRequestHandler {
        @Override
        public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.setContentType("text/plain;charset=utf8");
            response.getWriter().write("AddUserHandler添加成功");
        }
    }
    
    QueryUserHandler
    package com.hdu.springmvc.handler;
    
    public class QueryUserHandler implements HttpRequestHandler {
        @Override
        public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.setContentType("text/plain;charset=utf8");
            response.getWriter().write("QueryUserHandler添加成功");
        }
    }
    

    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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 首先配置handler,交给spring容器去管理该bean -->
        <!-- 其次配置name属性为uri,便于建立映射关系 -->
        <bean name="/addUser2"
              class="com.hdu.springmvc.handler.AddUserHandler"></bean>
        <bean name="/queryUser2"
              class="com.hdu.springmvc.handler.QueryUserHandler"></bean>
    
        <!-- 配置处理器映射器 -->
        <bean class="com.hdu.springmvc.handlermapping.BeanNameUrlHandlerMapping"
              init-method="init"></bean>
        <bean class="com.hdu.springmvc.handlermapping.SimpleUrlHandlerMapping"
              init-method="init"></bean>
    
        <!-- 配置处理器适配器 -->
        <bean class="com.hdu.springmvc.handleradapter.HttpRequestHandlerAdapter"></bean>
    </beans>
    

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <servlet>
            <servlet-name>AddUserServlet</servlet-name>
            <servlet-class>com.hdu.springmvc.servlet.AddUserServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>AddUserServlet</servlet-name>
            <url-pattern>/addUser</url-pattern>
        </servlet-mapping>
    
        <servlet>
            <servlet-name>QueryUserServlet</servlet-name>
            <servlet-class>com.hdu.springmvc.servlet.QueryUserServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>QueryUserServlet</servlet-name>
            <url-pattern>/queryUser</url-pattern>
        </servlet-mapping>
    
    
        <servlet>
            <servlet-name>DispatcherServlet</servlet-name>
            <servlet-class>com.hdu.springmvc.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>springmvc.xml</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>DispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
    

    测试处理请求流程:

    localhostaddUser 直接编写Servlet处理

    localhostaddUser2 --->DispatcherServlet --->BeanNameUrlHandlerMapping--->HttpRequestHandlerAdapter---> HttpRequestHandler 的 AddUserHandler

    localhostaddUser3 --->DispatcherServlet --->SimpleUrlHandlerMapping---> AddUserHandler

  • 相关阅读:
    整理了8个Python中既冷门又实用的技巧
    python中68个内置函数的总结
    Python中常见的8种数据结构的实现方法(建议收藏)
    python基础教程:dir()和__dict__属性的区别
    Python 优雅获取本机 IP 方法
    Python类中的self到底是干啥的
    python中反转列表的三种方式
    Flask学习笔记(2)-login_page
    利用Flask + python3.6+MYSQL编写一个简单的评论模块。
    最近写了个自动填写调查的问卷的简单爬虫
  • 原文地址:https://www.cnblogs.com/kuotian/p/13158868.html
Copyright © 2020-2023  润新知