书城第三阶段以及第四阶段:
主要内容(关键字):
* 代码优化
* 错误提示,信息回显
* 反射
* EL表达式修改表单回显
第三阶段代码优化
1. 页面jsp动态化
1. 将原来的HTML页面变为jsp页面
1. 在html里面添加page指令
2. 将文件的后缀由 .html变为.jsp
2. 抽取页面中相同的内容
1. head中css、jquery、base标签
* 这个抽取里面的原来的base 标签<base href="http://localhost:8080/工程路径/">,css,jquery文件的相对路径都是相对base标签的。
* 如果将来部署到服务器,其它远程的浏览器访问时,呢些css,jquery它们的路径就会发生错误,所以要将base 的href 属性的值动态获取(可以直接获得准确的ip地址)
* 代码如下:
<%
String basspath = request.getScheme()
+"://"
+request.getServerName()
+":"
+request.getServerPort()
+request.getContextPath()
+"/";
%>
<base href="<%=basspath%>">
* 此时如果远程浏览器使用 http://192.168.12.12:8080/book访问则得到的basspath=http://192.168.12.12:8080/book/
2. 每个页面的页脚
* 抽取这些页面时候,都是取出一个<div>标签对
3. 登录成功后的菜单
* 抽取这些页面时候,都是取出一个<div>标签对
4. manager模块的菜单
* 抽取这些页面时候,都是取出一个<div>标签对
3. 登录,注册错误提示,及表单回显
1. 在登录或者注册的时候,如果失败(比如登录的时候密码或者用户名输入错误)应该有提示信息,同时在跳转回原来的注册或登录页面的时候一部分之前输入的信息应该回显出来(比如:如果注册失败,应该将用户名,邮箱,或者验证码回显出来,或者登录失败的时候应该将用户名回显出来)
2. 实现方法:
* Servlet程序端将需要回显的信息保存到request域中,在jsp页面上输出回显信息
* 一般是把值放在jsp需要回显标签的value属性上,这样就可以在跳转回去的时候显示在界面上
* 示例:登录时候的错误提示信息,以及页面信息回显
* servlet程序:
if(login==null) {
//把错误信息,回传的表单项信息,保存到request域中
req.setAttribute("msg","用户名或密码错误!");
req.setAttribute("username",username);
System.out.println("登录失败");
req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
}else{
* jsp页面:
<div class="msg_cont">
<b></b>
<%--错误回显--%>
<span class="errorMsg"><%=(request.getAttribute("msg"))==null?"请输入用户名和密码":request.getAttribute("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="<%=request.getAttribute("username")==null?"":request.getAttribute("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" />
4. BaseServlet的抽取
1. 在实际的项目开发中一个模块一般只使用一个Servlet程序
2. 在图书管理模块就需要将loginServlet和registServlet程序合并为一个UserServlet程序,因为他们都属于用户模块
* 实现方法: 使用表单标签里面的 隐藏域:,将属性action的值赋予不同的值,然后用ig-else语句根据action的值来判断执行UseServlet程序里面的regist方法还是login方法()或者用户模块里面的其他方法
* 实现代码:
* 隐藏域 <input type="hidden" name="action" value="login"/>
* 用户模块里面可能出了登录和注册还有其他大量的功能,如果依然使用if-else的方法来根据action的值选择执行的方法。呢就会使用大量的 else -if d代码
* 这时候就用到***反射***
* 代码:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
try {
//根据action业务鉴别字符串,获取响应的业务,方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
//调用目标业务方法
method.invoke(this, req, resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
3. 一般一个工程里面有很多模块,一个模块一般需要一个Servlet程序,然后Servlet程序里面他们的大致执行步骤是一样的(起码书城项目里面的用户模块和图书模块是这样的),为了实现代码的复用,可以写一个定义为的抽象BaseServlet父类来实现这些功能,然后每个模块再继承这个父类
* 每个模块的Servlet的执行步骤(或实现方法)
1. 获取action参数的值,
2. 通过反射获取action对应的业务
3. 通过反射调用业务方法
* BaseServlet 代码:
public abstract class BaseServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
try {
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this, req, resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
* UserServlet 则只需要继承BaseServlet类,它的里面则可以只用写这个模块所需的各种方法。如 login()和regist()
5. 数据的封装和抽取BeanUtils的使用
1. 使用原因:BeanUtils工具类可以一次把所有请求的参数都注入到JavaBean中
2. 使用方法:
1. 它是第三方工具类,所以需要导包
* 需要导入的包:
commons-beanutils-1.8.0.jar
commons-logging-1.1.1.jar
2. 编写WebUtils 工具类
1. 初始工具类全部代码:
public class WebUtils {
public static void copyParamToBean(HttpServletRequest req,Object bean){
try {
BeanUtils.populate(bean , req.getParameterMap());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
* 底层实现原理
* 获得参数对之后,查找相应参数的Set方法,(如username 则查找方法SetUsername 如果找到则注入,找不到则不注入。)这个地方要注意,比如 如果把方法改为"SetUsername1",则依然注入不了,必须是Set加上首字母大写的参数作为Set方法才能保证注入
2. 改进一次之后的工具类代码:(将参数由“HttpServletRequest req”换位Map类型)
public class WebUtils {
public static void copyParamToBean(Map value, Object bean){
try {
//所有请求参数都注入bean对象中
BeanUtils.populate(bean , value );
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
* 这次改进的原因: 1.将Map的值注入到JavaBean对象里是很常见的。2.由于javaEE的分层,如果使用HttpServletRequest req作为参数,则在service层和dao 持久层,如果使用,就会造成web层耦合度高。而使用map 作为参数则在web层,service层,dao持久层都可以使用。
3. 再次改进的工具类
* 代码:
public class WebUtils {
/**
* 把Map中的值注入到对应的JavaBean属性中
* @param value
* @param bean
* @param <T>
* @return 返回注入之后的JavaBean对象
*/
public static <T> T copyParamToBean(Map value, T bean){
try {
//所有请求参数都注入bean对象中
BeanUtils.populate(bean , value );
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return bean;
}
}
* 改进之后的好处,使用了泛型。使用这个方法的时候更加简单(如: User user = WebUtils.copyParamToBean(req.getParameterMap,new User())
书城第四阶段。使用EL表达式修改表单回显
* 使用原因:使用更加方便,代码量少了一些
//原本
<%=(request.getAttribute("msg"))==null?"请输入用户名和密码":request.getAttribute("msg")%>
//使用EL表达式
${ empty requestScope.msg ?"请输入用户名和密码":requestScope.msg}