JavaWeb-Servlet优化02(MVC思想)

1. class.forName()

   作用是按参数中指定的字符串形式的类名去搜索并加载相应的类,如果该类字节码已经被加载过,则返回代表该字节码的Class实例对象,否则,按类加载器的委托机制去搜索和加载该类,如果所有的类加载器都无法加载到该类,即抛出ClassNotFoundException。加载完这个Class字节码后,接着就可以使用Class字节码的newInstance方法去创建该类的实例对象了。

A a = (A)Class.forName("pacage.A").newInstance();

等价于

A a = new A()

Class.forName(xxx.xx.xx) 返回的是一个类
Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。

有时候,我们程序中所有使用的具体类名在设计时(即开发时)无法确定,只有程序运行的时候才能确定,这时候就需要使用Class.forName去动态加载该类,这个类名通常是在配置文件中配置的。

2. 优化处理-提取返回值

将FruitController中的返回值

(如:response.sendRedirect 改为 return “redirect:fruit.do”  

将  super.processTemplate("edit",request,response);  改为  return "edit";)

private String delete(HttpServletRequest request) throws ServletException{
    String fidstr = request.getParameter("fid");
    int fid = Integer.parseInt(fidstr);
    FruitDAO fruitDAO = new FruitDAOImpl();
    boolean b = fruitDAO.deleteFruitById(fid);
    if(b){
        System.out.println("删除成功!");
    }
    else{
        System.out.println("删除失败!");
    }
    //response.sendRedirect("fruit.do");
    return "redirect:fruit.do";
}

//  将  super.processTemplate("edit",request,response);  改为  return "edit";

在 DispatcherServlet中的设置

//DispatcherServlet
try {
        Method method = controllerBeanObj.getClass().getDeclaredMethod(operate,HttpServletRequest.class);
        if(method!=null){
            //controller组件中的方法调用
            method.setAccessible(true);
            Object returnObj = method.invoke(controllerBeanObj, request);

            //视图处理
            String methodReturnStr = (String)returnObj;
            if(methodReturnStr.startsWith("redirect")){ //如果是以redirect开头的 比如:return "redirect:fruit.do";
                String redirectStr = methodReturnStr.substring("redirect:".length()); //截取剩下 fruit.do
                response.sendRedirect(redirectStr); //跳转到 fruit.do
            }
            else{
                super.processTemplate(methodReturnStr,request,response); // 比如:return "index";
            }
        }else{
            throw new RuntimeException("operate值非法!");
        }
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

3. 优化处理-提取参数

在 DispatcherServlet中获取参数

try {
        //获取所有的方法
        Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
        //参数是由自己的方法决定
        for(Method method :methods){
            if(operate.equals(method.getName())){
                //统一获取请求参数
                //获取当前方法的参数,返回参数数组
                Parameter[] parameters = method.getParameters();
                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)){ //guo
                                parameterObj = Integer.parseInt(parameterValue);
                            }
                        }
                        parameterValues[i]=parameterObj;
                    }
                }
                //controller组件中的方法调用
                method.setAccessible(true);
                Object returnObj = method.invoke(controllerBeanObj, parameterValues);

                //视图处理
                String methodReturnStr = (String)returnObj;
                if(methodReturnStr.startsWith("redirect")){ //如果是以redirect开头的 比如:return "redirect:fruit.do";
                    String redirectStr = methodReturnStr.substring("redirect:".length()); //截取剩下 fruit.do
                    response.sendRedirect(redirectStr); //跳转到 fruit.do
                }
                else{
                    super.processTemplate(methodReturnStr,request,response); // 比如:return "index";
                }
            }

        }
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

FruitController中传入参数

public class FruitController {
    private FruitDAO fruitDAO = new FruitDAOImpl();

    private String index( String oper,String keyword,Integer pageNo,HttpServletRequest request) {
        HttpSession session=request.getSession();
        if(pageNo == null){
            pageNo = 1;
        }
        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.getListbypageNo(keyword, pageNo);
        session.setAttribute("fruitList", fruitList);
        int count = fruitDAO.getCount(keyword);
        int pageCount = count/5+1;
        session.setAttribute("pageCount", pageCount);

        return "index";
    }

    private String add(Integer fid,String fname, Integer price ,Integer fcount,String remark) {

        FruitDAO fruitDAO = new FruitDAOImpl();
        boolean b = fruitDAO.addFruits(new Fruit(fid,fname,price,fcount,remark));
        if(b){
            System.out.println("添加成功");
        }
        else{
            System.out.println("添加失败");
        }
        return "redirect:fruit.do";
    }

    private String delete(Integer fid) {

        FruitDAO fruitDAO = new FruitDAOImpl();
        boolean b = fruitDAO.deleteFruitById(fid);
        if(b){
            System.out.println("删除成功!");
        }
        else{
            System.out.println("删除失败!");
        }
        return "redirect:fruit.do";
    }

    private String edit(Integer fid,HttpServletRequest request) {
        FruitDAO fruitDAO1=new FruitDAOImpl();
        Fruit fruit1 = new Fruit();
        fruit1= fruitDAO1.getFruitById(fid);
        request.setAttribute("fruit",fruit1);
        return "edit";
    }

    private String Update(Integer fid ,String fname,Integer price,Integer fcount,String remark) {
        FruitDAO fruitDAO=new FruitDAOImpl();
        boolean b = fruitDAO.updateFruitById(new Fruit(fid, fname, price, fcount, remark));
        if(b){
            System.out.println("修改成功");
        }
        else{
            System.out.println("修改失败");
        }
        return "redirect:fruit.do";
    }
}

 

小结:

1. 一个请求对应一个Servlet,(/index ----> IndexServlet、/update ---->UpdateServlet)servlet太多了

2. 优化一:把一些列的请求都对应一个Servlet:IndexServlet/AddServlet/EditServlet/DelServlet/UpdateServlet -> 合并成FruitServlet

      在FruitServlet中有add方法、update方法、index方法、delete方法等等,在界面中添加一个 name="operate",value="add/update..."的隐藏文本框,

      通过operate的值来决定调用FruitServlet中的哪一个方法  使用的是switch-case

                  switch (operate){    

        case "index":       

          Index(request,response);        

          break;   

      }

3.优化二:Servlet中充斥着大量的switch-case,若业务规模扩大,那么会有很多的Servlet,也就意味着会有很多的switch-case,会造成代码冗余

    因此,我们在servlet中使用了反射技术,规定operate的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法进行响应,

    如果找不到对应的方法,则抛异常。

      Method[] methods = this.getClass().getDeclaredMethods();  //获取当前类中所有的方法

    for(Method m : methods){    //遍历

      String name = m.getName();    //获得方法名称

      if(operate.equals(name)){        //若方法名称与operate的值一致

        try {            

           m.invoke(this,request,response);          //反射技术,跳转到对应的方法中

           return;        

         } catch (Exception e)

         {             e.printStackTrace();         } 

       throw new RuntimeException("operate不合法!");  //若不一致,则不合法

      }

 4. 优化三:在优化二中,若业务较大,涉及到的servlet不止一个(如还有Userservlet、Productservlet、Fruitservlet、Orderservlet....),则每一个servlet中都有类似的反射技术的代码。因此继续抽取,设计了中央控制器类:DispatcherServlet。

  DispatcherServlet这个类的工作分为两大部分:

   (1)根据url定位到能够处理这个请求的controller组件:

     1) 从url中提取servletPath : /fruit.do -> fruit

     提取方法:request.getServletPath();  通过substring()截取得到fruit

      2) 根据fruit找到对应的组件:FruitController , 这个对应的依据我们存储在applicationContext.xml中
        <bean id="fruit" class="com.fruit.controllers.FruitController/>
      通过DOM技术我们去解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件

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

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

       Class controllerBeanClass = Class.forName(className);        

       Object beanObj = controllerBeanClass.newInstance() ;        

       beanMap.put(beanId , beanObj) ; 

       3) 根据获取到的operate的值定位到我们FruitController中需要调用的方法

      Object controllerBeanObj = beanMap.get(servletPath);

   (2)调用Controller组件中的方法:

    1) 获取参数

    获取即将要调用的方法的参数签名信息:

    Parameter[] parameters = method.getParameters();   通过parameter.getName()获取参数的名称;准备了Object[] parameterValues 这个数组用来存放对应参数的参数值,需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型

     2) 执行方法

    Object returnObj = method.invoke(controllerBean , parameterValues);

     3) 视图处理

    String methodReturnStr = (String)returnObj;

    if(methodReturnStr.startsWith("redirect")){ //如果是以redirect开头的 比如:return "redirect:fruit.do";    

      String redirectStr = methodReturnStr.substring("redirect:".length()); //截取剩下 fruit.do    

      response.sendRedirect(redirectStr); //跳转到 fruit.do

    } else{    

    super.processTemplate(methodReturnStr,request,response); // 比如:return "index";

     }

posted @   浑浑噩噩一只小迷七  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示