分享

结合代码分析-BaseServlet

 liang1234_ 2018-08-01

看到一个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参数的值来调用不同的方法,类似这样:

  1. String method = request.getParameter("method");
  2. if(method.equals("add")) {
  3. add(...); //省略参数 下同
  4. } else if(method.equals("update")) {
  5. delete();
  6. } else if(method.equals("query")) {
  7. update(...);
  8. } else{
  9. query(...);
  10. }

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

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

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

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

  1. public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. //下面这个if语句可以不用看,这里用到了我项目中另一个类:GetRequest,该类继承了HttpRequestWrapper,将request进行了包装,主要是防止乱码
  3. if(((HttpServletRequest)request).getMethod().equalsIgnoreCase("get")) {
  4. if(!(request instanceof GetRequest)) {
  5. request = new GetRequest((HttpServletRequest)request);
  6. }
  7. } else {
  8. ((HttpServletRequest)request).setCharacterEncoding("utf-8");
  9. }
  10. //设置response的返回数据格式以及字符编码
  11. response.setContentType("text/html;charset=UTF-8");
  12. //在这里获取前台传来的method参数
  13. String methodName = ((HttpServletRequest)request).getParameter("method");
  14. /*
  15. * Method类,是java反射机制使用到的一个类
  16. * API对该类的解释是:提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
  17. * 看到这里是不是有点清楚BaseServlet是怎么调用方法的了?
  18. * 这里先定义了一个Method的引用,遵循编写代码的规范原则,在try-catch外定义引用,块内实现
  19. */
  20. Method method = null;
  21. try {
  22. /* 这行代码的意思是实例化一个以methodName命名,以HttpServletRequest对象和HttpServletResponse对象做参数的方法
  23. * 大家都知道this指的是调用当前方法的类的对象
  24. * 所以不要想当然的认为this指的是BaseServlet对象,而是继承自BaseServlet的、与你请求的url相匹配的Servlet
  25. * 这个Servlet当然由你自己实现
  26. */
  27. method = this.getClass().getMethod(methodName, new Class[]{HttpServletRequest.class, HttpServletResponse.class});
  28. } catch (Exception var10) {
  29. //你自定义的servlet中没有对应的方法时,处理异常。
  30. throw new RuntimeException("您要调用的方法:" + methodName + "它不存在!", var10);
  31. }
  32. try {
  33. //这行代码表示调用了刚才实例化的Method对象所对应的方法,并用e接收方法的返回值
  34. String e = (String)method.invoke(this, new Object[]{request, response});
  35. //e进行一些必要的判断
  36. if(e != null && !e.trim().isEmpty()) {
  37. /* 这里需要解释一下返回字符串的构成
  38. * 可以直接返回要跳转的视图名,这样将会跳转到对应视图
  39. * 若返回视图名的字符串前指定了转发的方式,如:f:list.jsp r:list.jsp
  40. * 那么下面语句会判断使用何种方式转发视图
  41. */
  42. int index = e.indexOf(":");
  43. if(index == -1) {
  44. //没有指定视图的转发方式 ((HttpServletRequest)request).getRequestDispatcher(e).forward((ServletRequest)request, response);
  45. } else {
  46. String start = e.substring(0, index);
  47. String path = e.substring(index + 1);
  48. if(start.equals("f")) {
  49. //如果指定了f,则使用forward方式跳转 ((HttpServletRequest)request).getRequestDispatcher(path).forward((ServletRequest)request, response);
  50. } else if(start.equals("r")) {
  51. //如果指定了r,则使用redirect方式重定向 response.sendRedirect(((HttpServletRequest)request).getContextPath() + path);
  52. }
  53. }
  54. }
  55. } catch (Exception var9) {
  56. throw new RuntimeException(var9);
  57. }
  58. }

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

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

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

参考资料:

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

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



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多