16.4 解耦控制器代码

16.4 解耦控制器代码

app16a中的业务逻辑代码都写在了Servlet控制器中,这个Servlet类将随着应用复杂度的增加而不断膨胀。为避免此问题,我们应该将业务逻辑代码提取到独立的被称为controller的类中
app16b应用(app16a的升级版)中,controller目录下有两个controller类,分别是InputProductControllerSaveProductControllerapp16b应用的目录结构如下所示:
这里有一张图
这两个controller都实现了Controller接口(见清单16.6)。Controller接口只有handleRequest一个方法。Controller接口的实现类通过该方法访问到当前请求的HttpServletRequestHttpServletResponse对象。

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路径。

InputProductController类

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();
// populate form properties
productForm.setName(
request.getParameter("name"));
productForm.setDescription(
request.getParameter("description"));
productForm.setPrice(request.getParameter("price"));
// create model
Product product = new Product();
product.setName(productForm.getName());
product.setDescription(productForm.getDescription());
try {
product.setPrice(Float.parseFloat(
productForm.getPrice()));
} catch (NumberFormatException e) {}
// insert code to add product to the database
request.setAttribute("product", product);
return "/WEB-INF/jsp/ProductDetails.jsp";
}
}

将业务逻辑代码迁移到controller类的好处很明显:ControllerServlet变得更加专注。现在作用更像一个dispatcher,而非一个controller,因此,我们将其改名为DispatcherServletDispatcherServlet类(见清单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();
/*
* uri is in this form: /contextName/resourceName,
* for example: /app10a/product_input.
* However, in the event of a default context, the
* context name is empty, and uri has this form
* /resourceName, e.g.: /product_input
*/
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