欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

BaseServlet类的介绍

程序员文章站 2022-05-08 13:15:37
...

结合代码分析--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参数的值来调用不同的方法,类似这样:

  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的理解,如有不同见解希望能一起讨论,如果写的有错欢迎指正