JavaWEB十一:Servlet优化2 实现一个Servlet组件调度所有的controller

Servlet优化2:实现一个Servlet组件调度所有的controller

一、优化
  1. 优化逻辑图在这里插入图片描述
  2. 将原先的Servlet组件简化为controller
    /*
    	1.去除Controller的继承
    	2.将各方法内,都有通过req.getParameter获取的参数的相同功能的代码块,将这些参数变量全部放在方法
    	  头的参数列表上,在dispatcher内通过反射统一获取,使controller类尽量只实现业务逻辑功能
    */
    public class CustomersController {
        private ListIpm listIpm = new ListIpm();
        
        protected String index(String oper,String keyword,String page,HttpServletRequest req) {
            Connection conn = null;
            try {
                conn = JdbcUtils.getConnection();
                HttpSession session = req.getSession();
                int pageNo = 1;
                // 1.此处的oper来自参数列表
                if (StringUtil.isNotEmpty(oper) && "search".equals(oper)) {
                    pageNo = 1;
                    // 2.此处的keyword来自参数列表
                    if (StringUtil.isEmpty(keyword)) {
                        keyword = "";
                    }
                    session.setAttribute("k",keyword);
                }else {
                    Object keyObj = session.getAttribute("k");
                    if (keyObj == null) {
                        keyword =  "";
                    }else {
                        keyword = (String) keyObj;
                    }
                }
                if (page != null) {
                    // 3.此处的page来自参数列表
                    pageNo = Integer.parseInt(page);
                }
                session.setAttribute("pageOn",pageNo);
                List<Customers> custList = listIpm.getList(conn,"%"+ keyword +"%",pageNo);
                session.setAttribute("cl",custList);
                
                long count = 0L;
                count = listIpm.getCount(conn,"%"+keyword+"%");
                int max = (int) (count+2-1)/2;
                session.setAttribute("max",max);
                // 4.将父类的模板处理方法返回,在dispatcher上进行处理
                return "index";
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.closeResource(conn,null);
            }
            return null;
        }
        
        private String add(String name,String email,String birth)  {
            Connection conn = null;
            try {
                // 此处的birth、name、email来自参数列表
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                Date date = format.parse(birth);
                conn = JdbcUtils.getConnection();
                listIpm.addCustomer(conn,name,email,date);
                // 将请求路径返回,在dispatcher上进行处理
                return "redirect:customers.do";
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.closeResource(conn,null);
            }
            return null;
        }
        
        private String delete(String sid)  {
    		// 此处的sid来自参数列表
            if (StringUtil.isNotEmpty(sid)) {
                Connection conn = null;
                try {
                    int id = Integer.parseInt(sid);
                    conn = JdbcUtils.getConnection();
                    listIpm.delById(conn,id);
                    // 将请求路径返回,在dispatcher上进行处理
                    return "redirect:customers.do";
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    JdbcUtils.closeResource(conn,null);
                }
            }
            return null;
        }
        
        private String edit(String cid,HttpServletRequest req)  {
            Connection conn = null;
            try {
                conn = JdbcUtils.getConnection();
                // 此处的cid来自参数列表
                if (StringUtil.isNotEmpty(cid)) {
                    int id = Integer.parseInt(cid);
                    Customers customer = listIpm.getCustomerById(conn, id);
                    req.setAttribute("cu" ,customer);
                    // 将模板返回,在dispatcher上进行处理
                    return "edit";
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.closeResource(conn,null);
            }
            return null;
        }
        private String update(String id,String name,String email,String birth) {
            Connection conn = null;
            try {
                // 此处的id、birth、name、email来自参数列表
                int idInt = Integer.parseInt(id);
    			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                Date date = format.parse(birth);
                conn = JdbcUtils.getConnection();
                listIpm.updateById(conn,name,email,date,idInt);
                // 将请求路径返回,在dispatcher上进行处理
                return "redirect:customers.do";
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.closeResource(conn,null);
            }
            return null;
        }
    }
    
  3. 新建一个Servlet组件,实现dispatcher调度功能
    // 注解处的*代表任意值
    @WebServlet("*.do")
    public class DispatcherServlet extends ViewBaseServlet{
        // 创建HashMap集合,将bean内的元素以键值对的形式存储在该集合内
        private Map<String,Object> beanMap = new HashMap<>();
        // 一、在init方法中,解析相关联的xml配置文件
        @Override
        public void init() throws ServletException {
            super.init();
            try {
                // 1.getResourceAsStream():以流的形式获取一个资源,即将cml文件转换为输入流
                InputStream is = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
                // 2.DocumentBuilderFactory类:使应用程序能够从XML文档中获取生成DOM对象树的解析器
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                /*
                    3.可以从DocumentBuilderFactory.newDocumentBuilder()方法获得该类的实例。
                      获得此类的实例后,可以从各种输入源解析XML。 这些输入源是InputStreams,
                      Files,URL和SAX InputSources
                */
                DocumentBuilder builder = factory.newDocumentBuilder();
                /*
                   4.从资源转化为的输入流中提取Document对象,Document接口表示整个HTML或XML文档。
                     从概念上讲,它是文档树的根,并提供对文档数据的主要访问。
                */
                Document document = builder.parse(is);
                // 5.通过Document对象,提取xml内指定标签的元素
                NodeList beanNodeList = document.getElementsByTagName("bean");
                for (int i = 0; i < beanNodeList.getLength() ; i++) {
                    Node beanNode = beanNodeList.item(i);
                    if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
                        /*
                          6.将Node类型的数据转换为Element类型
                            Element接口表示HTML或XML文档中的元素。元素可能具有与之关联的属性;
                            由于Element接口继承自Node,因此通用Node接口属性attributes可用于检索元
                            素的所有属性的集合。
                            Element接口上有方法可以按名称检索Attr对象,或按名称检索属性值。
                            在XML中,属性值可能包含实体引用,应检索Attr对象以检查表示属性值的可能相当
                            复杂的子树。
                            另一方面,在HTML中,所有属性都具有简单的字符串值,可以安全地使用直接访问
                            属性值的方法作为方便。
                        */
                        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 (SAXException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws  IOException {
            req.setCharacterEncoding("UTF-8");
            // 二、解析出请求路径中的请求标识
            /*
                1.获取servlet请求路径,来自tomcat的url配置或html/js文件内的url配置
                  将设:url = "http://localhost.8080/optimization_2/customers.do"
                  那么:ServletPath = "/customers.do"
            */
            String servletPath = req.getServletPath();
            // 2.获取最后出现该后缀名字符串的索引值
            int lastDotIndex = servletPath.lastIndexOf(".do");
            /*
                3.使用String substring(int beginIndex, int endIndex)方法,将servletPath头部
                  的斜杠和后缀的.do去除
                  servletPath.substring(1):表示从索引1处开始,故索引0处的值就被截取掉了
            */
            servletPath = servletPath.substring(1,lastDotIndex);
    
            // 三、指定具体的controller
            // 通过从在请求路径中解析出的请求标识,传入beanMap中,即可指定具体的controller        
            Object controllersObj = beanMap.get(servletPath);
            
            // 四、匹配controller内方法
            // 1.通过req获取html/js文件内的operate的值
            String operate = req.getParameter("operate");
            // 2.如果operate的值为空,就赋初始值index
            if (StringUtil.isEmpty(operate)) {
                operate = "index";
            }
            // 3.使用反射,解析出指定controller内的所有方法
            try {
                Method[] methods = controllersObj.getClass().getDeclaredMethods();
                // 4.使用增强for循环,解析出每个方法的参数
                for (Method method : methods) {
                    // 4.1 使用operate值,指定具体的方法
                    if (operate.equals(method.getName())) {
                        // 4.2 利用反射,获取指定方法内的所有参数
                        Parameter[] parameters = method.getParameters();
                        // 4.3 创建一个Object数组,用于存放通过反射得到的各参数的对应值
                        Object[] parameterValues = new Object[parameters.length];
                        // 4.4 将所有参数存入Object数组
                        for (int i = 0; i < parameters.length; i++) {
                            Parameter p = parameters[i];
                            // 4.5 获取每个参数的名称
                            String parameterName = p.getName();
                            // 4.6 将这些参数名分情况赋值给parameterValues数组
                            if ("req".equals(parameterName)) {
                                parameterValues[i] = req;
                            } else if ("resp".equals(parameterName)) {
                                parameterValues[i] = resp;
                            } else if ("session".equals(parameterName)) {
                                parameterValues[i] = req.getSession();
                            } else {
                                String parameterValue = req.getParameter(parameterName);
                                parameterValues[i] = parameterValue;
                            }
                        }
                        method.setAccessible(true);
                        Object returnObj = method.invoke(controllersObj, parameterValues);
                        // 5.视图处理(重定向及模板处理)
                        // 5.1 获取该方法返回的重定向字符串
                        String retrunStr = (String) returnObj;
                        // 5.2 截取重定向内字符串内包含的请求路径(例如:index.do)
                        if (retrunStr.startsWith("redirect:")) {
                            String redirectStr = retrunStr.substring("redirect:".length());
                            resp.sendRedirect(redirectStr);
                        } else {
                            // 5.3 将controller内返回的模板传入父类的模板处理方法
                            super.processTemplate(retrunStr,req,resp);
                        }
    
                    }
                }
            }  catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    
    
  4. html/js/css文件不变
二、错误提示
  1. 参数类型不匹配
    // 控制台错误提示
    java.lang.IllegalArgumentException: argument type mismatch
    
    // 错误代码,将字符串req赋给了参数数组,导致参数错误
    for (int i = 0; i < parameters.length; i++) {
        Parameter p = parameters[i];   
        String parameterName = p.getName();   
        if ("req".equals(parameterName)) {
            parameterValues[i] = “req”;    
    
    /* 
    	正确写法:
    	应该将service方法头部的形参HttpServlet req 赋值给参数数组,才能通过req.getParameter()方法
    	实现对index方法头其它参数的正确调用
    */
    	if ("req".equals(parameterName)) {
            parameterValues[i] = req;   
    
  2. 在Controller类上加了@WebServlet注解,导致404错误
三、设置:debug运行时,显示参数的具体名称
  1. Setting - > Bulid… -> java compiler

    在这里插入图片描述

  2. 删除out文件夹中的所有文件

    在这里插入图片描述

  3. 创建指定module的对应的production及artifacts文件

    在这里插入图片描述

    在这里插入图片描述

  4. 生成后的效果

    在这里插入图片描述

四、参考资料:
  1. Java中getResourceAsStream的用
  2. DocumentBuilderFactory 包/类/方法中文说明 - Java 11 API中文版,时代Java (nowjava.com)
  3. 全类名与路径的区别
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

e_nanxu

感恩每一份鼓励-相逢何必曾相识

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值