1. 一个模块对应一个Servlet
在实际的项目开发中,一个模块一般只使用一个 Servlet 程序。
而我们以前往往根据一个功能写一个 Servlet,比如给用户注册功能写一个 RegistServlet,再给用户登录功能写一个 LoginServlet,事实上,我们完全可以将 用户注册功能 和 用户登录功能 封装为一个用户模块,然后给这个模块写一个单独的 UserServlet,我们接下来看看如何操作。
这里我们需要在前端使用 hidden 属性的 <input> 标签:
在 login.html 页面中:
<form action="userServlet" method="post">
<!-- 核心:添加 input:hidden标签 -->
<input type="hidden" name="action" value="login">
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1" name="username" />
<label>用户密码:</label>
<input class="itxt" type="password" placeholder="请输入密码" autocomplete="off" tabindex="1" name="password" />
<input type="submit" value="登录" id="sub_btn" />
</form>
在 regist.html 页面中:
<form action="userServlet" method="post">
<!-- 核心:添加 input:hidden标签 -->
<input type="hidden" name="action" value="regist">
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名"
autocomplete="off" tabindex="1" name="username" id="username" />
<label>用户密码:</label>
<input class="itxt" type="password" placeholder="请输入密码"
autocomplete="off" tabindex="1" name="password" id="password" />
<label>确认密码:</label>
<input class="itxt" type="password" placeholder="确认密码"
autocomplete="off" tabindex="1" name="repwd" id="repwd" />
<label>电子邮件:</label>
<input class="itxt" type="text" placeholder="请输入邮箱地址"
autocomplete="off" tabindex="1" name="email" id="email" />
<label>验证码:</label>
<input class="itxt" type="text" name="code" style="width: 150px;" id="code"/>
<img alt="" src="static/img/code.bmp" style="float: right; margin-right: 40px">
<input type="submit" value="注册" id="sub_btn" />
</form>
将这两个功能统一封装用户模块中,对应到 UserServlet:
public class UserServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
/**
* 处理注册的功能
* @param req
* @param resp
*/
protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String code = req.getParameter("code");
// 2. 检查 验证码是否正确
if ("abcde".equalsIgnoreCase(code)) {
if (userService.existsUserName(username)) {
System.out.println("用户名[ " + username + " ]已存在!");
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);
} else {
userService.registerUser(new User(null, username, password, email));
req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req, resp);
}
} else {
System.out.println("验证码[ " + code + " ]错误");
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);
}
}
/**
* 处理登录的功能
* @param req
* @param resp
*/
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
User user = userService.login(new User(null, username, password, null));
if (user == null) {
System.out.println("用户名或密码错误,登录失败!");
req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
} else {
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req, resp);
}
}
// 一个函数进行不同任务派遣
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
if ("login".equals(action)) {
login(req, resp);
} else if ("regist".equals(action)) {
regist(req, resp);
}
}
}
2. 使用反射优化大量 else if 代码
但是,目前用户模块只有登录和注册两个功能,随着项目的跟进,以后用户模块可能还会有:添加用户、修改用户信息、修改密码、绑定手机号、绑定邮箱、注销用户…等等的方法,那么以后每新加一个方法,我们都要在 doPost 中增加一个 else if 的判断,那么我们是否可以想一种方法,doPost 中的代码我们就一次性写好,以后我们不管新加了什么功能,doPost 都可以自动调用相应的方法呢?
其实有一个规律,如果 action
的值为 login,那么 doPost 调用的方法就是 login(req, resp)
方法,如果 action
的值为 regist,那么 doPost 调用的方法就是 regist(req, resp)
方法。那么,我们可以直接利用反射机制,调用 action 对应的方法名的方法即可。
利用反射机制实现 doPost 自动派发任务:
public class UserServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
// 处理注册的功能
protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理注册的逻辑
}
// 处理登录的功能
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理登录的逻辑...
}
// 一个函数进行不同任务派遣
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
try {
// 获取aciton业务鉴别字符串,获取响应的业务方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
// 调用目标业务方法
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 抽取 BaseServlet 程序
事实上,我们目前仅仅写了一个包含自动派发任务的 doPost方法的 UserServlet 程序,而在一个项目中。除了用户模块,我们可能还会有 图书模块、订单模块 等不同的模块,我们也需要在这些模块中定义不同的功能方法。
那么,我们可以将 UserServlet 中的 doPost 方法逻辑复用,抽取成为一个公共的 BaseServlet 类,以后如果想要开发一个新的模块,只需要先继承这个类,然后直接写对应的方法即可。
提取出来的 BaseServlet 类:
// 这里作为一个抽象类,是为了防止被实例化
public abstract class BaseServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
try {
// 获取aciton业务鉴别字符串,获取响应的业务方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
// 调用目标业务方法
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
修改 UserServlet 程序继承 BaseServlet 类,以后只用专注于写业务代码即可:
public class UserServlet extends BaseServlet {
private UserService userService = new UserServiceImpl();
// 处理注册的功能
protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理注册的逻辑
}
// 处理登录的功能
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理登录的逻辑...
}
}