JavaWeb第五天--Servlet优化(MVC)

项目源码地址:https://gitee.com/qyh24/java-web.git

一、原有项目结构

  1. 最初的做法是: 一个请求对应一个Servlet,这样存在的问题是servlet太多了
  2. 把一些列的请求都对应一个Servlet, IndexServlet/AddServlet/EditServlet/DelServlet/UpdateServlet -> 合并成FruitServlet
    通过一个operate的值来决定调用FruitServlet中的哪一个方法
    使用的是switch-case
  3. 在上一个版本中,Servlet中充斥着大量的switch-case,试想一下,随着我们的项目的业务规模扩大,那么会有很多的Servlet,也就意味着会有很多的switch-case,这是一种代码冗余
    因此,我们在servlet中使用了反射技术,我们规定operate的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法进行响应,如果找不到对应的方法,则抛异常
  4. 在上一个版本中我们使用了反射技术,但是其实还是存在一定的问题:每一个servlet中都有类似的反射技术的代码。因此继续抽取,设计了中央控制器类:DispatcherServlet
    DispatcherServlet这个类的工作分为两大部分:
    1.根据url定位到能够处理这个请求的controller组件:
    1)从url中提取servletPath : /fruit.do -> fruit
    2)根据fruit找到对应的组件:FruitController , 这个对应的依据我们存储在applicationContext.xml中
      <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController/>
    
    通过DOM技术我们去解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件
    3)根据获取到的operate的值定位到我们FruitController中需要调用的方法
    2.调用Controller组件中的方法:
    1. 获取参数
      获取即将要调用的方法的参数签名信息: Parameter[] parameters = method.getParameters();
      通过parameter.getName()获取参数的名称;
      准备了Object[] parameterValues 这个数组用来存放对应参数的参数值
      另外,我们需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型
    2. 执行方法
      Object returnObj = method.invoke(controllerBean , parameterValues);
    3. 视图处理
      String returnStr = (String)returnObj;
      if(returnStr.startWith(“redirect:”)){

      }else if…

二、多个Servlet合并成一个Servlet

现在项目结构
最初的做法是: 一个请求对应一个Servlet,这样存在的问题是servlet太多了
image.png
期望结构
把一些列的请求都对应一个Servlet, IndexServlet/AddServlet/EditServlet/DelServlet/UpdateServlet -> 合并成FruitServlet
通过一个operate的值来决定调用FruitServlet中的哪一个方法
使用的是switch-case

54DAACE2-F50E-424E-A88A-F1ACE8DEE81B.png

@WebServlet("/fruit.do")
public class FruitServlet extends ViewBaseServlet {
    private FruitDAO fruitDAO = new FruitDAOImpl();
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       //1.设置编码
        request.setCharacterEncoding("UTF-8");
        //2.获取操作
        String operate = request.getParameter("operate");
        System.out.println("获取到的操作为---->"+operate);
        if (StringUtil.isEmpty(operate)){
            operate = "index";
        }
        switch (operate){
            case "index":
                index(request,response);
                break;
            case "add":
                add(request,response);
                break;
            case "del":
                del(request,response);
                break;
            case "edit":
                edit(request,response);
                break;
            case "update":
                update(request,response);
                break;
        }
    }
    private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");

        //2.获取参数
        String fidStr = request.getParameter("fid");
        Integer fid = Integer.parseInt(fidStr);
        String fname = request.getParameter("fname");
        String priceStr = request.getParameter("price");
        int price = Integer.parseInt(priceStr);
        String fcountStr = request.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        String remark = request.getParameter("remark");

        //3.执行更新
        fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));

        //4.资源跳转
        //super.processTemplate("index",request,response);
        //request.getRequestDispatcher("index.html").forward(request,response);
        //此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
        response.sendRedirect("fruit.do");
    }
    public void edit(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            Fruit fruit = fruitDAO.getFruitByFid(fid);
            request.setAttribute("fruit",fruit);
            super.processTemplate("edit",request,response);
        }
    }
    private void del(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            fruitDAO.delFruit(fid);

            //super.processTemplate("index",request,response);
            response.sendRedirect("fruit.do");
        }
    }
    private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");

        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price")) ;
        Integer fcount = Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");

        Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ;

        fruitDAO.addFruit(fruit);

        response.sendRedirect("fruit.do");

    }
    private void index(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        HttpSession session = request.getSession() ;
        Integer pageNo = 1 ;

        String oper = request.getParameter("oper");
        //如果oper!=null 说明 通过表单的查询按钮点击过来的
        //如果oper是空的,说明 不是通过表单的查询按钮点击过来的

        String keyword = null ;
        if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
            //说明是点击表单查询发送过来的请求
            //此时,pageNo应该还原为1 , keyword应该从请求参数中获取
            pageNo = 1 ;
            keyword = request.getParameter("keyword");
            if(StringUtil.isEmpty(keyword)){
                keyword = "" ;
            }
            session.setAttribute("keyword",keyword);
        }else{
            //说明此处不是点击表单查询发送过来的请求(比如点击下面的上一页下一页或者直接在地址栏输入网址)
            //此时keyword应该从session作用域获取
            String pageNoStr = request.getParameter("pageNo");
            if(StringUtil.isNotEmpty(pageNoStr)){
                pageNo = Integer.parseInt(pageNoStr);
            }
            Object keywordObj = session.getAttribute("keyword");
            if(keywordObj!=null){
                keyword = (String)keywordObj ;
            }else{
                keyword = "" ;
            }
        }

        session.setAttribute("pageNo",pageNo);

        FruitDAO fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList(keyword , pageNo);

        session.setAttribute("fruitList",fruitList);

        //总记录条数
        int fruitCount = fruitDAO.getFruitCount(keyword);
        //总页数
        int pageCount = (fruitCount+5-1)/5 ;
        /*
        总记录条数       总页数
        1               1
        5               1
        6               2
        10              2
        11              3
        fruitCount      (fruitCount+5-1)/5
         */
        session.setAttribute("pageCount",pageCount);

        //此处的视图名称是 index
        //那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称 :   index
        //物理视图名称 :   view-prefix + 逻辑视图名称 + view-suffix
        //所以真实的视图名称是:      /       index       .html
        super.processTemplate("index",request,response);
    }
}

三、通过反射技术直接调用方法

通过反射技术获取所有方法名,根据operate匹配相应的方法,直接调用

  //获取当前类中所有的方法
    Method[] methods = this.getClass().getDeclaredMethods();
    for (Method m : methods) {
        //获取方法名称
        String methodName = m.getName();
        if (methodName.equals(operate)){
            try {
                m.invoke(this,request,response);
                return;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }throw new RuntimeException("operate值非法");
}

四、DispatcherServlet根据URL定位Controller

1.新建application

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="fruit" class="com.atguigu.fruit.servlets.FruitController"></bean>
</beans>

2.新建中央处理器

DBB05BFB-D69B-4290-B500-21E295089EED.png

  1. 解析配置文件
  2. 根据路径匹配对应的Controller
  3. 根据operate调用Controller相应的方法
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet{
    //beanMap 存放 id 和 class的关系
    private Map<String,Object> beanMap = new HashMap<>();
    //Servlet生命周期:实例化、初始化、服务、销毁
    public DispatcherServlet() {
        try {
            //解析配置文件,创建文件流,读取文件信息
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream("application.xml");
            //1.创建factory
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            //2.创建documentBuilder
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            //3.获取document对象
            Document document = documentBuilder.parse(inputStream);
            //4.获取所有的bean节点
            NodeList beanNodeList = document.getElementsByTagName("bean");
            for (int i = 0; i < beanNodeList.getLength(); i++) {
                Node beanNode = beanNodeList.item(i);
                if (beanNode.getNodeType() == Node.ELEMENT_NODE){
                    Element beanElement = (Element)beanNode;
                    String beanId = beanElement.getAttribute("id");
                    String className = beanElement.getAttribute("class");
                    Object beanObj = Class.forName(className).newInstance();
                    beanMap.put(beanId,beanObj);
                }
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("UTF-8");
        //2.想通过.do前面的字段来匹配Controller
        String servletPath = request.getServletPath();
        //servletPath = /index.do -> index -> IndexController
        System.out.println("servletPath = " + servletPath);
        //3.解析path,得到index
        servletPath = servletPath.substring(1,servletPath.length() - 3);
        //4.index对应到IndexController
        Object controllerBeanObj = beanMap.get(servletPath);
        //5.根据operate调用相应方法
        String operate = request.getParameter("operate");
        System.out.println("获取到的操作为---->"+operate);
        if (StringUtil.isEmpty(operate)){
            operate = "index";
        }
        //获取当前类中所有的方法
        Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
        for (Method m : methods) {
            //获取方法名称
            String methodName = m.getName();
            if (methodName.equals(operate)){
                try {
                    m.invoke(controllerBeanObj,request,response);
                    return;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }throw new RuntimeException("operate值非法");
    }

}

3.视图处理

  1. 通过每个方法返回的字符串决定重定向还是调用thymeleaf
    DispatcherServlet
//获取当前类中所有的方法
    Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
    for (Method m : methods) {
        //获取方法名称
        String methodName = m.getName();
        if (methodName.equals(operate)){
            try {
                //方法调用
                m.setAccessible(true);
                String methodReturnStr = (String) m.invoke(controllerBeanObj,request,response);
                //视图处理
                if (methodReturnStr.startsWith("redirect:")){
                    String redirectStr = methodReturnStr.substring("redirect:".length());
                    response.sendRedirect(redirectStr);
                }else {
                    super.processTemplate(methodReturnStr,request,response);
                }
                return;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }throw new RuntimeException("operate值非法");
}

Controller

 public String edit(HttpServletRequest request){
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            Fruit fruit = fruitDAO.getFruitByFid(fid);
            request.setAttribute("fruit",fruit);
//            super.processTemplate("edit",request,response);
            return "edit";
        }
        return null;
    }
    private String del(HttpServletRequest request){
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            fruitDAO.delFruit(fid);

            //super.processTemplate("index",request,response);
//            response.sendRedirect("fruit.do");
            return "redirect:fruit.do";
        }
        return null;
    }

四、统一获取参数

try {
    //1.获取参数
    //获取当前方法的参数,返回参数数组
    Parameter[] parameters = m.getParameters();
    //数组存放value的值
    Object[] parameterValues = new Object[parameters.length];
    for (int i = 0; i < parameterValues.length; i++) {
        Parameter parameter = parameters[i];
        String parameterName = parameter.getName();
        //参数名特殊处理
        if ("request".equals(parameterName)){
            parameterValues[i] = request;
        }else if ("response".equals(parameterName)){
            parameterValues[i] = response;
        }else if ("session".equals(parameterName)){
            parameterValues[i] = request.getSession();
        }else {
            //从请求中获取参数
            String parameterValue = request.getParameter(parameterName);
            String typeName = parameter.getType().getName();
            if ("java.lang.Integer".equals(typeName)){
                parameterValues[i] = Integer.parseInt(parameterValue);
            }
            parameterValues[i] = parameterValue;
        }
    }
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值