文章目录
第三阶段(对第一第二阶段的代码进行优化)
把HTML页面jsp动态化
idea中的替换方法;按shift
+command
+R
三个按钮调出替换页面并替换
抽取页面中相同的内容
需要替换的内容如下
-
页面中的base标签,css样式,jQuery文件一般都会放在common中作为其他页面的引用
<% String scheme = request.getScheme(); // 获取协议 String serverName = request.getServerName(); // 获取ip地址 int serverPort = request.getServerPort(); // 获取端口号 String contextPath = request.getContextPath(); // 获取工程名 String basePath = scheme + "://" + serverName + ":" + serverPort + contextPath + "/"; //别忘了加 / pageContext.setAttribute("basePath", basePath); %> <base href="<%=basePath%>"> <link type="text/css" rel="stylesheet" href="static/css/style.css" > <script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
- 注意base标签应当动态获取,不能默认写死,不然一旦更换登录地址,可能出现页面无法访问
-
页面脚部的版权信息
<div id="bottom"> <span> 尚硅谷书城.Copyright ©2015 </span> </div>
动态显示登陆失败时的错误信息
登陆错误回显示有如下两个需求以及对应的处理流程
登陆失败页面设置
如果无法顺利登陆,返回登陆失败的原因,同时保留登陆时的账号信息,因此在Servlet页面中应当有如下判断
因此,分析可知:当访问login.jsp页面时有两种情况,需要在jsp以及servlet中同时考虑如下两种情况
- 情况一:第一次请求访问
- 情况二:登陆失败请求重定向至login.jsp页面
if (loginUser == null) {
// 把错误信息,和回显的表单项信息,保存到Request域中
req.setAttribute("msg","用户或密码错误!");
req.setAttribute("username", username);
// 跳回登录页面
req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
} else {
// 登录 成功
//跳到成功页面login_success.html
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req, resp);
}
在对应的jsp页面中也应当有如下内容
<div class="tit">
<h1>尚硅谷会员</h1>
<a href="pages/user/regist.jsp">立即注册</a>
</div>
<div class="msg_cont">
<b></b>
<span class="errorMsg">
${ empty requestScope.msg ? "请输入用户名和密码":requestScope.msg }
</span>
</div>
<div class="form">
<form action="userServlet" method="post">
<input type="hidden" name="action" value="login" />
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名"
autocomplete="off" tabindex="1" name="username"
value="${requestScope.username}" />
<br />
<br />
<label>用户密码:</label>
<input class="itxt" type="password" placeholder="请输入密码"
autocomplete="off" tabindex="1" name="password" />
<br />
<br />
<input type="submit" value="登录" id="sub_btn" />
</form>
</div>
⭐️ 对应的页面信息与说明如上图所示,
⭐️ 很明显request域(该次请求有效)充当了信息传递的桥梁,重载了错误信息以及用户名称的回显。
注册失败页面设置
注册页面jsp代码与对应效果图如下
<div class="login_form">
<div class="login_box">
<div class="tit">
<h1>注册会员</h1>
<span class="errorMsg"></span>
</div>
<div class="form">
<form action="regietServlet" method="post">
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名"
autocomplete="off" tabindex="1" name="username" id="username"/>
<br/>
<br/>
<label>用户密码:</label>
<input class="itxt" type="password" placeholder="请输入密码"
autocomplete="off" tabindex="1" name="password" id="password"/>
<br/>
<br/>
<label>确认密码:</label>
<input class="itxt" type="password" placeholder="确认密码"
autocomplete="off" tabindex="1" name="repwd" id="repwd"/>
<br/>
<br/>
<label>电子邮件:</label>
<input class="itxt" type="text" placeholder="请输入邮箱地址"
autocomplete="off" tabindex="1" name="email" id="email"/>
<br/>
<br/>
<label>验证码:</label>
<input class="itxt" type="text" name="code" style="width: 150px;" id="code"/>
<img id="code_img" alt="" src="kaptcha.jpg" style="float: right; margin-right: 40px">
<br/>
<br/>
<input type="submit" value="注册" id="sub_btn"/>
</form>
</div>
</div>
</div>
和登陆的设置一样,request与用于处理输出错误信息errorMsg
,同时登陆失败时需要回显用户名称,电子邮件,验证码。两种注册失败的原因以及对应回显信息如下:
- **注册失败原因一:**验证码错误,回显用户名、电子邮件、验证码(可选)
- **注册失败原因二:**用户名已存在,回显用户名,电子邮件,验证码(可选)
对应的registServlet源码如下:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
String code = request.getParameter("code");
if ("abcd".equalsIgnoreCase(code)) { // 这里写死code用于测试
if (userService.existUsername(username)) {
// 将可以回显的数据写回request域中
request.setAttribute("msg","该用户名已存在!");
request.setAttribute("username",username);
request.setAttribute("email",email);
request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);
}else {
userService.login(new User(null, username, password, email));
// System.out.println("好 注册OK! 正在跳转成功页面·····");
request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request, response);
}
}else {
request.setAttribute("msg","验证码错误!");
request.setAttribute("username",username);
request.setAttribute("email",email);
request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);
}
}
对应的jsp页面源码如下
<div class="login_form">
<div class="login_box">
<div class="tit">
<h1>注册会员</h1>
<span class="errorMsg">
${ requestScope.msg } <!-- 这里相对于前端页面做了修改。 -->
</span>
</div>
<div class="form">
<form action="registServlet" method="post">
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名"
value="${requestScope.username}"
autocomplete="off" tabindex="1" name="username" id="username"/>
<!-- 这里value值相对于前端页面做了修改。 -->
<br/>
<br/>
<label>用户密码:</label>
<input class="itxt" type="password" placeholder="请输入密码"
autocomplete="off" tabindex="1" name="password" id="password"/>
<br/>
<br/>
<label>确认密码:</label>
<input class="itxt" type="password" placeholder="确认密码"
autocomplete="off" tabindex="1" name="repwd" id="repwd"/>
<br/>
<br/>
<label>电子邮件:</label>
<input class="itxt" type="text" placeholder="请输入邮箱地址"
value="${requestScope.email}"
autocomplete="off" tabindex="1" name="email" id="email"/>
<!-- 这里email值相对于前端页面做了修改。 -->
<br/>
<br/>
<label>验证码:</label>
<input class="itxt" type="text" name="code" style="width: 150px;" id="code"/>
<img id="code_img" alt="" src="kaptcha.jpg" style="float: right; margin-right: 40px">
<br/>
<br/>
<input type="submit" value="注册" id="sub_btn"/>
</form>
</div>
</div>
</div>
登陆和注册的页面优化(合并)
我们希望将loginServlet和registServlet合并成为UserServlet,实现对页面的优化,可以通过如下过程实现
jsp页面的更改
需要在两个jsp页面中增加隐藏域
-
login.jsp:
<input type="hidden" name="action" value="login" />
-
regist.jsp
<input type="hidden" name="action" value="regist" />
UserServlet页面的更改
public class UserServlet01 extends HttpServlet
{
private UserService userService = new UserServiceImpl();
@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);
}
}
/**
* 处理登陆请求
* @param req
* @param resp
*/
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取请求的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
// 调用 userService.login()登录处理业务
User loginUser = userService.login(new User(null, username, password, null));
// 如果等于null,说明登录 失败!
if (loginUser == null) {
// 把错误信息,和回显的表单项信息,保存到Request域中
req.setAttribute("msg","用户或密码错误!");
req.setAttribute("username", username);
// 跳回登录页面
req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
} else {
// 登录 成功
//跳到成功页面login_success.html
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req, resp);
}
}
/**
* 处理注册请求
* @param req
* @param resp
*/
protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
String code = request.getParameter("code");
System.out.println(code);
if ("abcd".equalsIgnoreCase(code)) {
if (userService.existUsername(username)) {
// System.out.println("存在了!不能注册 正在跳转注册页面·····");
request.setAttribute("msg","该用户名已存在!");
request.setAttribute("username",username);
request.setAttribute("email",email);
request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);
}else {
userService.login(new User(null, username, password, email));
// System.out.println("好 注册OK! 正在跳转成功页面·····");
request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request, response);
}
}else {
// System.out.println("不争气,,验证码不对!");
request.setAttribute("msg","验证码错误!");
request.setAttribute("username",username);
request.setAttribute("email",email);
request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);
}
}
}
使用反射以及BaseServlet优化
注意到如下代码段中从request域中得到的内容其实和方法名相同,因此考虑使用反射机制,写一段通用代码,直接获取对应的方法
String action = req.getParameter("action");
if("login".equals(action)){
login(req,resp);
}else if("regist".equals(action)){
regist(req,resp);
}
具体过程见如下代码
String action = request.getParameter("action");
// 获取action业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class); // 使用反射获取目标业务名
// 调用目标业务 方法
method.invoke(this, request, response);
进一步,由于该过程是每一个模块都可能会用到的过程,因此考虑将其按照如下的方式抽取出来
构建一个BaseServlet程序,让各个模块继承该程序,对应代码如下:
public abstract class BaseServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 解决post请求中文乱码问题
// 一定要在获取请求参数之前调用才有效
request.setCharacterEncoding("UTF-8");
// 解决响应中文乱码
response.setContentType("text/html; charset=UTF-8");
String action = request.getParameter("action");
try {
// 获取action业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
// 调用目标业务 方法
method.invoke(this, request, response);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);//抛给Filter过滤器
}
}
}
然后在对应的其他模块servlet中写业务逻辑方法
bookservlet模块
UserServlet模块
数据的封装和抽取BeanUtils的使用
BeanUtils工具类可以一次性地把所有请求的参数都注入到JavaBean中。出现这种需求的原因是:
- 某个业务框中存在很多用户输入的数据内容,此时我们就一条一条的获取并注入JavaBean对象中,这太过于麻烦,于是BeanUtils诞生了。
BeanUtils不是jdk的类,是第三方的工具类,所以需要导包,导包内容如下
可以使用BeanUtils包将用户的输入数据注册至JavaBean对象中,因此可以写一个WebUtils类来完成该操作:
WebUtils工具类
public class WebUtils {
/**
* 把Map中的值注入到对应的JavaBean属性中。
* @param value
* @param bean
*/
public static<T> T copyParamToBean(Map value, T bean){
try {
System.out.println("BeanUtils.populate()上一句······");
/**
* 把所有请求的参数都注入到user对象中
*/
BeanUtils.populate(bean, value); // 核心语句
System.out.println("BeanUtils.populate()下一句······");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return bean;
}
}
关于populate
方法的说明:
- 源码中对给方法有如下注释
- 其需要给定待注入的JavaBean对象,以及对应的properties(键值对参数)。该方法利用反射调用了对应javaBean中的
setxxx()
方法,因此一定要保证传递进入的paraName和javaBean中的属性值相同,这样才能成功调用setxxx()
方法。
因此,在同时考虑泛型的情况下在该自定义的工具类中要从UserServlet
中传递的参数应当为(Map value, T bean)
,而不是(HttpServletRequest req, Object bean)
。
这么做的原因:将Map的值注入JavaBean的需求很常见,不只有Web层有这种需求,DAO层,Service层,Web层都可能会有这种需求,如果这里写成了(HttpServletRequest req, Object bean)
,DAO层和Service层就无法使用该方法,如果写成前者的话,三层都可以使用。使用后者就会使得代码的耦合度过高——优雅。
对应的UserServlet类中也需要做如下修改
// 1、获取请求的参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
String code = request.getParameter("code");
// 上述四行代码可以用如下一行代码完成替换
User user = WebUtils.copyParamToBean(request.getParameterMap(), new User());
// 这里传递的参数为参数的键值对,原因如上
- 在上述代码中,即可获得成功注入请求数据的user对象。
第四阶段(使用EL修改修改表单回显)
修改登陆页面
<!-- 不使用EL表达式 -->
<% request.getAttribute("msg")==null?"请输入用户名和密码":request.getAttribute("msg")%>
<!-- 使用EL表达式输入错误信息 -->
${ empty requestScope.msg ? "请输入用户名和密码":requestScope.msg }
<!-- 使用EL表达式回显用户名 -->
<input class="itxt" type="text" placeholder="请输入用户名"
autocomplete="off" tabindex="1" name="username"
value="${requestScope.username}" />
修改注册页面
<!-- 使用EL表达式输入错误信息 -->
${ requestScope.msg }
<!-- 使用EL表达式回显用户名 -->
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名"
value="${requestScope.username}"
autocomplete="off" tabindex="1" name="username" id="username"/>
<!-- 使用EL表达式回显电子邮件 -->
<label>电子邮件:</label>
<input class="itxt" type="text" placeholder="请输入邮箱地址"
value="${requestScope.email}"
autocomplete="off" tabindex="1" name="email" id="email"/>