16.4 解耦控制器代码
app16a
中的业务逻辑代码都写在了Servlet
控制器中,这个Servlet
类将随着应用复杂度的增加而不断膨胀。为避免此问题,我们应该将业务逻辑代码提取到独立的被称为controller
的类中。
在app16b
应用(app16a
的升级版)中,controller
目录下有两个controller
类,分别是InputProductController
和SaveProductController
。app16b
应用的目录结构如下所示:
这里有一张图
这两个controller
都实现了Controller
接口(见清单16.6)。Controller
接口只有handleRequest
一个方法。Controller
接口的实现类通过该方法访问到当前请求的HttpServletRequest
和HttpServletResponse
对象。
Controller接口
1 2 3 4 5 6 7 8
| package app16b.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface Controller { String handleRequest(HttpServletRequest request, HttpServletResponse response); }
|
InputProductController
类直接返回了ProductForm.jsp
的路径。
而SaveProductController
类则会读取请求参数来构造一个ProductForm
对象,之后用ProductForm
对象来构造一个Product
对象,并返回ProductDetail.jsp
路径。
1 2 3 4 5 6 7 8 9 10
| package app16b.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class InputProductController implements Controller { @Override public String handleRequest(HttpServletRequest request, HttpServletResponse response) { return "/WEB-INF/jsp/ProductForm.jsp"; } }
|
SaveProductController类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package app16b.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import app16b.domain.Product; import app16b.form.ProductForm; public class SaveProductController implements Controller { @Override public String handleRequest(HttpServletRequest request, HttpServletResponse response) { ProductForm productForm = new ProductForm(); productForm.setName( request.getParameter("name")); productForm.setDescription( request.getParameter("description")); productForm.setPrice(request.getParameter("price")); Product product = new Product(); product.setName(productForm.getName()); product.setDescription(productForm.getDescription()); try { product.setPrice(Float.parseFloat( productForm.getPrice())); } catch (NumberFormatException e) {} request.setAttribute("product", product); return "/WEB-INF/jsp/ProductDetails.jsp"; } }
|
将业务逻辑代码迁移到controller
类的好处很明显:ControllerServlet
变得更加专注。现在作用更像一个dispatcher
,而非一个controller
,因此,我们将其改名为DispatcherServlet
。DispatcherServlet
类(见清单16.9)检查每个URI
,创建相应的controller
,并调用其handleRequest
方法。
DispatcherServlet类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| package app16b.servlet; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import app16b.controller.InputProductController; import app16b.controller.SaveProductController; public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 748495L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException { process(request, response); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException { process(request, response); } private void process(HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException { String uri = request.getRequestURI();
int lastIndex = uri.lastIndexOf("/"); String action = uri.substring(lastIndex + 1); String dispatchUrl = null; if(action.equals("product_input.action")) { InputProductController controller = new InputProductController(); dispatchUrl = controller.handleRequest(request, response); } else if(action.equals("product_save.action")) { SaveProductController controller = new SaveProductController(); dispatchUrl = controller.handleRequest(request, response); } if(dispatchUrl != null) { RequestDispatcher rd = request .getRequestDispatcher(dispatchUrl); rd.forward(request, response); } } }
|
现在,可以在浏览器中输入如下URL
测试应用了:
http://localhost:8080/app16b/product_input.action