• JavaWeb基础篇中央处理器处理冗余


    前言:

        在前面的学习过程中,写的代码都是一个操作operator对应着一个Servlet,这只是一个小的管理系统,所以看着代码不是很多,但是在一个大的系统中,显然有点不合适,所以进行了优化,一个JavaBean对应一个servlet,使用的是switch-case解决,但是随着项目的业务规模扩大,那么会有很多的Servlet,也就意味着会有很多的switch-case,这是一种代码冗余
        因此,我们在servlet中使用了反射技术,我们规定operate的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法进行响应,如果找不到对应的方法,则抛异常
        但是:每一个servlet中都有类似的反射技术的代码,因此继续抽取,设计了中央控制器类:DispatcherServlet。

    说明:

        在这里仍然使用的是水果管理系统,在这个项目的基础上做出相关优化,学习中央处理器的实现。

    1. 根据url定位到controller组件

    1.1 创建DispatcherServlet获取servletPath

        中央处理器的使用是的FruitServlet进行了转变,变成一个普通的Java类,Fruitcontroller,DiapatcherServlet继承ViewBaseServlet,进行接收和响应用户,将核心逻辑放在FruitController(调用底层具体的实现更新的方法)中。

    ①首先进行编码的设置,这是最基本的,浏览器传来一个中文字符时可以识别,实现写入数据库时不会出现乱码的现象。

    //设置编码
            request.setCharacterEncoding("UTF-8");
    
    • 1
    • 2

    ②调用getServletPath()方法从url中获取到servletPath:
    例如:
    url------------------------------------------------>servletPath
    http://localhost:8080/pro15/hello.do----->/hello.do

    String servletPath = request.getServletPath();
    
    • 1

    ③实现对servletPath的截取,获取到“hello”;【通过调用String类的subString()方法实现截取】
    操作过程:

    		servletPath = servletPath.substring(1);
            int lastDotIndex = servletPath.lastIndexOf(".do") ;
            servletPath = servletPath.substring(0,lastDotIndex);
    
    • 1
    • 2
    • 3

    1.2 创建application.xml配置文件

        文件的作用:实现servletPath中设置的名字(假设为fruit),可能映射到FruitController来处理(具体的JavaBean)【也即是说在中央处理器中建立一种映射关键字,从前端获取到要处理的类,可以找到对应的Controller】

    <?xml version="1.0" encoding="utf-8"?>
    
    <beans>
        <!--bean的作用是:servletpath中设计的名字对应的是fruit,那么就是FruitController来处理-->
        <bean id="fruit" class="com.lei.fruit.dao.impl.controllers.FruitController"/>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.3 将所有的bean节点存储到Map集合

        以键值对的形式进行存储,方便以后在使用的使用通过get(servletPath)来获取key对应的value,通过DOM技术我们去解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件

    实现存储的过程:
    ①创建一个输入流,将硬盘上的文件读入到内存,方便创建Document时使用
    ②创建DocumentBuilderFactory
    ③创建DocumentBuilder对象
    ④创建Document对象通过创建的输入流解析xml配置文件

    			InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
                //1.创建DocumentBuilderFactory
                DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                //2.创建DocumentBuilder对象
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder() ;
                //3.创建Document对象
                Document document = documentBuilder.parse(inputStream);
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ⑤获取所有的bean节点存储到集合

    NodeList beanNodeList = document.getElementsByTagName("bean");
    
    • 1

    ⑥通过一个循环逐个获取单个节点beanNode,对单个节点进行判断,是否是元素节点,是的话进行强制转化,得到beanElement,调用getAttribute()获取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");
                    Class controllerBeanClass = Class.forName(className);
                    Object beanObj = controllerBeanClass.newInstance() ;
                    beanMap.put(beanId , beanObj) ;
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    由于map集合中的value需要存储的是一个Class实例,所以在这是通过反射创建Class对象

    				Class controllerBeanClass = Class.forName(className);
                    Object beanObj = controllerBeanClass.newInstance() ;
    
    • 1
    • 2

    1.4 根据servletPath获得对应的Controller

        由于第三步已经将对应的关系存储到Map集合中,所以需要在这里通过获得的servletPath,利用get()方法,找到对应的Controller

    获取方法如下:

    Object controllerBeanObj = beanMap.get(servletPath);
    
    • 1

    2. 调用Controller组件中的方法

    说明:

        调用Controller组件的三个步骤,实际上实现的就是对原有的FruitServlet的优化,把里面的参数,重定向,processTemplate等进行的优化,减少了代码的冗余度

    2.1 获取参数

    在实现获取参数之前,需要有一些事情要做:
    (1)获得需要执行操作的名称
    (2)通过反射获得所有的方法集合

     String operate = request.getParameter("operate");
            if(StringUtil.isEmpty(operate)){
                operate = "index" ;
            }
    
    • 1
    • 2
    • 3
    • 4

    由于在前面通过Map集合的get()方法获得的Class实例是controllerBeanObj,所以在获取所有的方法使用的是:

    Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
    
    • 1

    通过判断operator和对应的操作方法名称来确定需要执行的方法,然后切入正题,获取参数

    if(operate.equals(method.getName()))
    
    • 1

    实现具体过程的说明:

        首先通过调用getParameters()得到当前调用的方法,需要使用的参数集合,然后创建一个Object类型的集合来承载对应的数据,(例如现在执行的是添加操作,所以会有对应的fid、fname、price…等等),利用parameters[i],获取到当前的一个参数,然后通过parameter.getName() 获得这个参数的名称,之后再分类:
        ①如果参数名是request,response,session,将对应的参数直接赋值给parameterValues
        ②普通的需要获取参数的值,通过request.getParameter(parameterName);获得前端传递过来的数值
        又因为在②中传递的类型有不同需要再次做出出理

     			 //1-1.获取当前方法的参数,返回参数数组
                        Parameter[] parameters = method.getParameters();
                        //1-2.parameterValues 用来承载参数的值
                        Object[] parameterValues = new Object[parameters.length];
                        for (int i = 0; i < parameters.length; i++) {
                            Parameter parameter = parameters[i];
                            String parameterName = parameter.getName() ;
                            //如果参数名是request,response,session 那么就不是通过请求中获取参数的方式了
                            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();
    
                                    Object parameterObj = parameterValue ;
    
                                    if(parameterObj!=null) {
                                        if ("java.lang.Integer".equals(typeName)) {
                                            parameterObj = Integer.parseInt(parameterValue);
                                        }
                                    }
    
                                    parameterValues[i] = parameterObj ;
                                }
                            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2.2 执行方法

        执行方法比较简单,就是利用反射获得实例来调用invoke()方法,实现实际的操作方法调用,例如:update…

     method.setAccessible(true);
    Object returnObj = method.invoke(controllerBeanObj,parameterValues);
    
    • 1
    • 2

    2.3 视图处理

        主要实现的就是对重定向sendRedirect以及processTemplate的处理,将Controller的操作进行优化,统一处理
        这里比较简单,主要实现的就是使用String类的方法startsWith()方法判断需要执行的不同操作。

    				 String methodReturnStr = (String)returnObj ;
                    if(methodReturnStr.startsWith("redirect:")){        //比如:  redirect:fruit.do
                        String redirectStr = methodReturnStr.substring("redirect:".length());
                        response.sendRedirect(redirectStr);
                    }else{
                        super.processTemplate(methodReturnStr,request,response);    // 比如:  "edit"
                    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3. 优化后代码的对比

    3.1 优化前代码实现

        最大的不同就是对FruitController实现类的优化,从对比中,我们可以看出使用中央处理器来做出优化很有必要

    FruitController

    public class FruitController extends ViewBaseServlet {
    
        //之前FruitServlet是一个Sevlet组件,那么其中的init方法一定会被调用
        //之前的init方法内部会出现一句话:super.init();
    
        private ServletContext servletContext ;
    
        public void setServletContext(ServletContext servletContext) throws ServletException {
            this.servletContext = servletContext;
            super.init(servletContext);
        }
    
        private FruitDAO fruitDAO = new FruitDAOImpl();
    
        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");
        }
    
        private 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.deleteFruit(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 {
            HttpSession session = request.getSession() ;
    
            // 设置当前页,默认值1
            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");
                //如果keyword为null,需要设置为空字符串"",否则查询时会拼接成 %null% , 我们期望的是 %%
                if(StringUtil.isEmpty(keyword)){
                    keyword = "" ;
                }
                //将keyword保存(覆盖)到session中
                session.setAttribute("keyword",keyword);
            }else{
                //说明此处不是点击表单查询发送过来的请求(比如点击下面的上一页下一页或者直接在地址栏输入网址)
                //此时keyword应该从session作用域获取
                String pageNoStr = request.getParameter("pageNo");
                if(StringUtil.isNotEmpty(pageNoStr)){
                    pageNo = Integer.parseInt(pageNoStr);   //如果从请求中读取到pageNo,则类型转换。否则,pageNo默认就是1
                }
                //如果不是点击的查询按钮,那么查询是基于session中保存的现有keyword进行查询
                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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143

    3.2 优化后代码实现

    FruitController

    public class FruitController  {
    
        private FruitDAO fruitDAO = new FruitDAOImpl();
    
        private String update(Integer fid,String fname,Integer price,Integer fcount,String remark, HttpServletRequest request) {
            fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));
            return "redirect:fruit.do";
        }
    
        private String edit(Integer fid ,HttpServletRequest request ) {
            if(fid != null){
                Fruit fruit = fruitDAO.getFruitByFid(fid);
                request.setAttribute("fruit",fruit);
                return "edit";
            }
            return "error";
        }
    
        private String del(Integer fid, HttpServletRequest request ){
            if(fid!=null){
                fruitDAO.deleteFruit(fid);
                return "redirect:fruit.do";
            }
            return "error";
        }
    
        private String add(String fname,Integer price,Integer fcount,String remark, HttpServletRequest request) {
            Fruit fruit = new Fruit(0,fname , price , fcount , remark ) ;
            fruitDAO.addFruit(fruit);
            return "redirect:fruit.do";
        }
    
        private String index(String oper, String keyword,Integer pageNo,HttpServletRequest request ) {
            if (pageNo==null){
                pageNo=1;
            }
    
            HttpSession session = request.getSession() ;
            if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
                pageNo = 1 ;
                if(StringUtil.isEmpty(keyword)){
                    keyword = "" ;
                }
                session.setAttribute("keyword",keyword);
            }else{
                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 ;
            session.setAttribute("pageCount",pageCount);
    
            return "index";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
  • 相关阅读:
    前端新手Vue3+Vite+Ts+Pinia+Sass项目指北系列文章 —— 第六章 样式格式化 (Sass配置)
    no declaration can be found for element ‘rabbit:connection-factory‘
    【操作系统】信号量机制(整型信号量、记录型信号量),用信号量实现进程互斥、同步、前驱关系
    UniRx 入门
    VUE+element可以为空不为空时只能为(正整数和0)的验证
    【AppLinking实战案例】通过AppLinking分享应用内图片
    网安学习-内网安全1
    ChatGPT 通过谷歌算法面试,年薪 18.3 万美金
    vue3 + elementPlus实现select下拉框插入确定和取消按钮。
    Worthington酒精脱氢酶的特异性和五个应用
  • 原文地址:https://blog.csdn.net/weixin_44606952/article/details/125474243