在Servlet开发的工程实践中,为了减少过多的业务Servlet编写,会采用构建公共Servlet的方式,通过反射来搭建轻量级的MVC框架,从而加快应用开发。
关于Servlet开发的基础知识,请看:JavaWeb开发之详解Servlet及Servlet容器
前后端交互的基本形式
一般来说,前端提交数据请求有三种基本方式,分别是表单、链接和Ajax
1. 按钮
1 <form action="/BaseServlet/ServletDemo02?method=addStu" method="post"> 2 用户<input type="text" name="username"/><br/> 3 <button>提交</button> 4 </form>
2. 链接
<a href="/BaseServlet/ServletDemo02?method=delStu">删除学生</a><br/>
3. Ajax
1 <button onclick="fn()">按钮</button> 2 <script> 3 function fn(){ 4 $.post("/BaseServlet/ServletDemo02",{"method":"checkStu","user":"tom"},function(data){ 5 alert(data); 6 }); 7 }
在Servlet开发的语境中,它们的共同点都是:指定处理的Servlet类路径(在web.xml中指定)以及附带在请求中的“method”参数
通过调用request参数匹配业务处理逻辑
前端按照以上方法发起请求,Servlet容器就会把请求交给对应的Servlet处理,并且附带上形如 method=delStu 的参数,利用这个原理,就可以构建一个基础Servlet类,用来优化开发:
1 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 //获取客户端提交到服务端的method对应的值 3 String md=request.getParameter("method"); 4 //定义变量,存放功能执行完毕之后要转发的路径 5 String path=null; 6 7 //通过判断md中不同的内容来决定本次功能 8 if("addStu".equals(md)){ 9 path=addStu(request, response); 10 }else if("delStu".equals(md)){ 11 path=delStu(request, response); 12 }else if("checkStu".equals(md)){ 13 path=checkStu(request, response); 14 }else if("".equals(md)){ 15 16 } 17 if(null!=path){ 18 //服务端的请求转发 19 request.getRequestDispatcher(path).forward(request, response); 20 } 21 } 22 23 24 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 25 doGet(request, response); 26 } 27 28 protected String addStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 29 System.out.println("添加学生"); 30 return "/test.html"; 31 32 } 33 protected String delStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 34 System.out.println("删除学生"); 35 return "/test.html"; 36 37 } 38 protected String checkStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 39 System.out.println("检查学生"); 40 response.getWriter().println("DDDDDD"); 41 return null; 42 }
以上的Servlet对请求进行了处理,通过获取index.html的请求,最后请求转发至目标页面,其核心思想是:
1. 提取request的method参数的值;
2. 定义变量,存储请求转发的路径;
3. 通过判断method参数中的值的内容,来决定调用哪个业务功能。
4. 完成业务功能后,使用请求转发处理
通过反射匹配业务处理逻辑
当业务量多的时候,以上实践仍是不够,此时比较好的方式就是采用反射。
1 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 doGet(request, response); 3 } 4 5 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 6 //获取客户端提交到服务端的method对应的值 7 String md=request.getParameter("method"); 8 //定义变量,存放功能执行完毕之后要转发的路径 9 String path=null; 10 //获取到当前字节码对象(ServletDemo02.class在内存中对象) 11 Class<? extends ServletDemo02> clazz = this.getClass(); 12 try { 13 //获取clazz上名称为md, 参数为HttpServletRequest和HttpServletResponse的方法 14 Method method=clazz.getMethod(md, HttpServletRequest.class,HttpServletResponse.class); 15 if(null!=method){ 16 //调用找到的方法 17 path=(String)method.invoke(this, request,response); 18 } 19 if(null!=path){ 20 //服务端的转发 21 request.getRequestDispatcher(path).forward(request, response); 22 } 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 27 } 28 29 public String addStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 30 System.out.println("添加学生"); 31 return "/test.html"; 32 33 } 34 public String delStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 35 System.out.println("删除学生"); 36 return "/test.html"; 37 38 } 39 public String checkStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 40 System.out.println("检查学生"); 41 response.getWriter().println("DDDDDD"); 42 return null; 43 }
1. 提取request的method参数的值;
2. 定义变量,存储请求转发的路径;
3. 获取到当前Servlet对象在内存中的Class对象
4. 获取Class对象上名称为md, 参数为HttpServletRequest和HttpServletResponse的方法
5. 根据是否返回方法对象,调用业务功能并使用请求转发处理
Java HttpServlet的父类GenericServlet,就是通过这个方法来调取HttpServlet的doGet或doPost方法的。
最佳实践
在实践开发中,一般会搭建一个BaseServlet,继承了HttpServlet并重写其Service方法,通过反射来找到业务功能子类对应的业务方法。
1 public class BaseServlet extends HttpServlet { 2 private static final long serialVersionUID = 12197442526341123L; 3 4 @Override 5 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 6 System.out.println("service....."); 7 //获取客户端提交到服务端的method对应的值 8 String md=request.getParameter("method"); 9 //定义变量,存放功能执行完毕之后要转发的路径 10 String path=null; 11 //获取到当前字节码对象(ServletDemo02.class在内存中对象) 12 Class<? extends BaseServlet> clazz = this.getClass(); 13 try { 14 //获取clazz上名称为md方法 15 Method method=clazz.getMethod(md, HttpServletRequest.class,HttpServletResponse.class); 16 if(null!=method){ 17 //调用找到的方法 18 path=(String)method.invoke(this, request,response); 19 } 20 if(null!=path){ 21 //服务端的转发 22 request.getRequestDispatcher(path).forward(request, response); 23 } 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } 27 } 28 29 }
而业务功能子类,则继承BaseServlet,专注于业务逻辑的开发
1 public class ServletDemo03 extends BaseServlet { 2 3 private static final long serialVersionUID = 11248215356242123L; 4 5 public ServletDemo03() { 6 System.out.println("没有参数的构造函数"); 7 } 8 9 public String addStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 10 System.out.println("添加学生"); 11 return "/test.html"; 12 13 } 14 public String delStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 15 System.out.println("删除学生"); 16 return "/test.html"; 17 18 } 19 public String checkStu(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 20 System.out.println("检查学生"); 21 response.getWriter().println("DDDDDD"); 22 return null; 23 } 24 25 }