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

前言:

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

说明:

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

中央处理器使用步骤

  • 1. 根据url定位到controller组件
    • 1.1 创建DispatcherServlet获取servletPath
    • 1.2 创建application.xml配置文件
    • 1.3 将所有的bean节点存储到Map集合
    • 1.4 根据servletPath获得对应的Controller
  • 2. 调用Controller组件中的方法
    • 2.1 获取参数
    • 2.2 执行方法
    • 2.3 视图处理
  • 3. 优化后代码的对比
    • 3.1 优化前代码实现
    • 3.2 优化后代码实现

1. 根据url定位到controller组件

1.1 创建DispatcherServlet获取servletPath

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

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

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

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

String servletPath = request.getServletPath();

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

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

1.2 创建application.xml配置文件

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



<beans>
    
    <bean id="fruit" class="com.lei.fruit.dao.impl.controllers.FruitController"/>
beans>

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);
 

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

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

⑥通过一个循环逐个获取单个节点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) ;
            }
        }

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

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

1.4 根据servletPath获得对应的Controller

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

获取方法如下:

Object controllerBeanObj = beanMap.get(servletPath);

2. 调用Controller组件中的方法

说明:

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

2.1 获取参数

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

 String operate = request.getParameter("operate");
        if(StringUtil.isEmpty(operate)){
            operate = "index" ;
        }

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

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

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

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

实现具体过程的说明:

    首先通过调用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 ;
                            }
                        }

2.2 执行方法

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

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

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"
                }

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);
    }
}

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";
    }
}

你可能感兴趣的:(#,JavaWeb,servlet,java,开发语言,tomcat)