结合代码分析--BaseServlet存在的意义(刚接触servlet的必看)



看到一个jsp/servlet实现MVC模式的项目中使用了BaseServlet,一开始并不理解这个类的作用,感觉是多此一举,但看了几篇文章后才发现原来是自己写的代码太low了,学识尚浅理解不了。但大牛总是从菜鸟走过来的嘛,虚心学习总没错。 故将自己的理解分享在博客上。

介绍

BaseServlet并不是什么很高大上的东西,它只是一个普通的、继承了HttpServlet类并重写了其中service方法的类。

使用方式

继承该类


存在的意义

对于刚刚接触编程的人来说,如果刚学完servlet,写一个增删改查的demo一般都会分别写四个servlet类,分别对应四种操作,就像这样:AddServlet DeleteServlet UpdateServlet QueryServlet,但这样不仅代码冗余的非常厉害,而且还需要对每个servlet一一配置,(特别是servlet3.0以前需要在web.xml中配置,会非常麻烦,web.xml文件也会变得很大,若修改配置很难找到对应)。相应的整个项目也会显得很大,为了解决这种问题就有了改进的版本。

--------------------------------------- 分割线 ---------------------------------------------------

一些已经接触编程一段时间的人会将增删改查四个操作写在一个servlet中,调用某个方法的时候只需要在servlet映射的url后面添加参数,比如:<a href="/testServlet?method=add">添加</a>,然后在servlet中添加if判断method参数的值来调用不同的方法,类似这样:

String method = request.getParameter("method");
if(method.equals("add")) {
   add(...);   //省略参数 下同
} else if(method.equals("update")) {
   delete();
} else if(method.equals("query")) {
   update(...);
} else{
   query(...);
}

这确实是个不错的办法(因为我在学习BaseServlet之前用的就是这种方式 哈哈哈),但我现在可以理解为:编写这段代码的人很有想法,但编程水平还仅仅在初级阶段。因为他只考虑到完成了增删改查的功能,并没有考虑到对功能的扩展。如果增加一个功能(假设一个功能对应一个方法),他要修改一次if语句的判断,那如果增加一百个功能呢?难道要改一百次代码?更何况有时候项目已经上线,难道要用户自己修改? 这还仅仅是一个servlet,对应一个domain类的操作,稍微大一点的项目至少就有一二十个domain类吧(我也没接触过,猜的,但绝对不会少),每个servlet都有着那么多操作,那要写多少if语句判断啊。

--------------------------------------- 分割线 ---------------------------------------------------

所以BaseServlet就应运而生了,它作为一个项目中所有servlet的基类,(个人感觉该类属于设计模式中的前端控制器,类似SpringMVC的DispatcherServlet),其中并没有任何的业务逻辑,只负责将请求处理后分发到不同的servlet进行不同的处理。

下面就结合代码理解一下该类(因为BaseServlet类只重写了service方法所以我只贴出service方法的代码,而且代码可能与你网上找到的有些细节方面不太相同,但基本原理是一样的),我会将解释写在代码的注释里:

 public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //下面这个if语句可以不用看,这里用到了我项目中另一个类:GetRequest,该类继承了HttpRequestWrapper,将request进行了包装,主要是防止乱码
    if(((HttpServletRequest)request).getMethod().equalsIgnoreCase("get")) {
        if(!(request instanceof GetRequest)) {
            request = new GetRequest((HttpServletRequest)request);
        }
    } else {
        ((HttpServletRequest)request).setCharacterEncoding("utf-8");
    }
    
    //设置response的返回数据格式以及字符编码
    response.setContentType("text/html;charset=UTF-8");
    //在这里获取前台传来的method参数
    String methodName = ((HttpServletRequest)request).getParameter("method");
    /*
     * Method类,是java反射机制使用到的一个类
     * API对该类的解释是:提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
     * 看到这里是不是有点清楚BaseServlet是怎么调用方法的了?
     * 这里先定义了一个Method的引用,遵循编写代码的规范原则,在try-catch外定义引用,块内实现
     */
    Method method = null;

    try {
        /* 这行代码的意思是实例化一个以methodName命名,以HttpServletRequest对象和HttpServletResponse对象做参数的方法
         * 大家都知道this指的是调用当前方法的类的对象
         * 所以不要想当然的认为this指的是BaseServlet对象,而是继承自BaseServlet的、与你请求的url相匹配的Servlet
         * 这个Servlet当然由你自己实现
         */
        method = this.getClass().getMethod(methodName, new Class[]{HttpServletRequest.class, HttpServletResponse.class});
    } catch (Exception var10) {
        //你自定义的servlet中没有对应的方法时,处理异常。
        throw new RuntimeException("您要调用的方法:" + methodName + "它不存在!", var10);
    }

    try {
        //这行代码表示调用了刚才实例化的Method对象所对应的方法,并用e接收方法的返回值
        String e = (String)method.invoke(this, new Object[]{request, response});
        //e进行一些必要的判断
        if(e != null && !e.trim().isEmpty()) {
            /* 这里需要解释一下返回字符串的构成
             * 可以直接返回要跳转的视图名,这样将会跳转到对应视图
             * 若返回视图名的字符串前指定了转发的方式,如:f:list.jsp  r:list.jsp
             * 那么下面语句会判断使用何种方式转发视图
             */
            int index = e.indexOf(":");
            if(index == -1) {
               //没有指定视图的转发方式 ((HttpServletRequest)request).getRequestDispatcher(e).forward((ServletRequest)request, response);
            } else {
                String start = e.substring(0, index);
                String path = e.substring(index + 1);
                if(start.equals("f")) {
                   //如果指定了f,则使用forward方式跳转 ((HttpServletRequest)request).getRequestDispatcher(path).forward((ServletRequest)request, response);
                } else if(start.equals("r")) {
                   //如果指定了r,则使用redirect方式重定向 response.sendRedirect(((HttpServletRequest)request).getContextPath() + path);
                }
            }
        }

    } catch (Exception var9) {
        throw new RuntimeException(var9);
    }
}

代码的执行流程如上。总结一下:

该方法中使用了反射机制来调用方法,不论你的Servlet中有多少方法,这段代码都不需要改变,只需要你提供方法名和参数列表就可以完成调用。若有别的domain类添加,只需要将对应的Servlet继承该类即可,不需要额外的代码。 继承后你只需要关心自己的逻辑即可,不用关心怎样被调用。

以上就是我对BaseServlet的理解,如有不同见解希望能一起讨论,如果写的有错欢迎指正

参考资料:

http://blog.csdn.net/sinat_34423196/article/details/52585648

http://www.runoob.com/design-pattern/front-controller-pattern.html



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值