java学习笔记-JavaWeb篇二
JavaWEB篇二
45 HttpSession概述
46 HttpSession的生命周期
47 HttpSession常用方法示例
48 HttpSessionURL重写
49 HttpSession小结(1)
50 HttpSession之简易购物车
51 JavaWEB中的相对路径和绝对路径
52 HttpSession之表单的重复提交
53 HttpSession之验证码
54 HttpSession小结(2)
55 使用JavaBean
56 EL语法
57 简单标签的HelloWorld
58 带属性的自定义标签
59 带标签体的自定义标签
60 带父标签的自定义标签
61 EL自定义函数
62 简单标签小结
45 HttpSession概述
HttpSession是一个接口 在服务器端保持HTTP状态信息
第一次请求后,在服务器端创建一个httpSession对象,然后响应给客户端时,在响应头中setcookie为httpSession值将session放进cookie中;之后客户端的每一次请求都会带上这个cookie。
通过禁用浏览器cookie,并且使用:http://127.0.0.1:8080/进行请求,会发现每一次请求都会有1个新的session值。
session cookie存在浏览器内存中,不保存在客户端硬盘上(不同于持久化cookie,session cookie仅针对某一次会话而言)
46 HttpSession的生命周期
1)什么时候创建HttpSession
① 是否客户端访问服务端的任何一个jsp或servlet,服务器就会立即创建一个HttpSession对象?
不一定。若当前的jsp(或servlet)是客户端访问的当前web应用的第一个资源,且jsp的page指令的session属性值为false,则服务器不会为jsp创建一个httpSession对象;若当前jsp不是客户端访问的当前web应用的第一个资源,且其他页面已经创建一个HttpSession对象,会把和当前会话关联的HttpSession对象返回给jsp页面,而不会创建一个新的HttpSession对象。
② page指令中的session=false表示什么意思
当前jsp页面禁用session隐含变量,但可以使用其他的显示的HttpSession对象
③ 在servlet中如何获取HttpSession对象
对于servlet而言,若servlet是客户端访问的第一个web应用的资源,则只有调用了request.getSession()或request.getSession(true)才会创建HttpSession对象
→request.getSession(boolean create):
create为false,若没有和当前JSP页面关联的HttpSession对象,则返回null;若有则返回true
create为true,一定返回一个HttpSession对象。若没有和当前JSP页面关联的HttpSession对象,则服务器创建一个新的HttpSession对象返回,若有,则直接返回关联的。
→request.getSession()等同于request.getSession(true)
2)什么时候销毁HttpSession
① 直接调用session.invalidate():该方法使HttpSession失效
② 服务器卸载了当前web应用、
③ 超出HttpSession的过期时间
—设置HttpSession的过期时间:session.setMaxInactiveInternal(5);单位为秒
—在web.xml文件中设置HttpSession的过期时间:单位为 分钟
<session-sonfig>
<session-timeout>30</session-timeout>
</session-sonfig>
47 HttpSession常用方法示例
太无聊没有练习
session.getAttribute()
session.setAttribute()
48 HttpSessionURL重写
HttpServletRequest中定义了2个方法:
encode
encodeURL()
49 HttpSession小结(1)
1 httpsession
1) HttpSession:在服务器端保持HTTP状态的方案。和其对应的是cookie
2) 当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否包含一个session标识(即sessionId),如果已经包含一个sessionId则说明已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个,这种情况可能出现在服务器端已经删除了该用户对应的session对象,但用户人为地在请求的URL后面附上一个JSESSION的参数)。如果客户端请求不包含SessionId,则为此客户端新建一个session并且生成一个与此session相关联的sessionId,这个session id将在本次响应中返回给客户端保存。
3)使用cookie来跟踪Session:session通过SessionId来区分不同的客户,session是以cookie或URL重写为基础的,默认使用cookie来实现,系统会创造一个名为JsessionId的输出cookie,这称之为Session Cookie,Session Cookie是存储于浏览器内存中的,并不是卸载硬盘上的,通常看不到JSessionId。
2 HttpSession的生命周期
1)创建一个HttpSession对象:
一个常见的错误是以为session在有客户端访问时就被创建(若第一次访问某WEB应用的一个JSP页面,且该JSP页面的page指令的session属性为false)
① 某server端程序(如servlet)调用HttpServletrequest.getSession(true)或HttpServletRequest.getSession()这样的语句时才会被创建
② 销毁HttpSession对象:
A. 程序调用HttpSession.invalidate()
B. 距离上一次收到客户端发送的session id时间间隔超过了session的最大有效时间
C. 服务器进程被停止(或当前web应用被卸载)
注意:关闭浏览器只会使存储在客户端浏览器内存中的session cookie失效,而不会使服务器端的session对象失效
3. HttpSession的相关的API
1)获取Session对象:request.getSession() request.getSession(boolean create)
2) 属性相关:setAttribute getAttribute、removeAttribute
3) 使HttpSession失效的:invalidate()方法
4) 设置其最大失效的 setMaxInactiveInterval
4. URL重写
1)Servlet规范中引入了一种补充的会话管理机制,他允许不支持cookie的浏览器也可以与WEB服务器保持持续的会话
2)将会话标识号以参数形式附加在超链接的URL地址后面的技术称为URL重写
3)代码:
<a href="<%= response.encodeURL("login.jsp")%>">重新登录</a>
50 HttpSession之简易购物车
first.jsp
<body>
<h4>step1:选择要购买的书籍</h4>
<form action="<%= request.getContextPath() %>/addressInfo" method="post">
<table cellspacing="0" cellpadding="10" border="1">
<tr>
<td>书名</td>
<td>是否购买</td>
</tr>
<tr>
<td>Java</td>
<td><input type="checkbox" name="book" value="Java"></td>
</tr>
<tr>
<td>Oracle</td>
<td><input type="checkbox" name="book" value="Oracle"></td>
</tr>
<tr>
<td>Struts</td>
<td><input type="checkbox" name="book" value="Struts"></td>
</tr>
<tr>
<td><input type="submit" name="submit" value="继续"></td>
</tr>
</table>
</form>
</body>
第一个servlet,处理上一个表单的submit
@WebServlet("/addressInfo") public class AddressInfo extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1 得到请求中的参数 String [] books = request.getParameterValues("book"); //2.将参数放入session request.getSession().setAttribute("books", books); //3.重定向到second.jsp页面 response.sendRedirect(request.getContextPath() + "/ShopCart/second.jsp"); } }
第二个jsp页面 second.jsp
<body> <h4>Step2:请输入寄送地址与信用卡信息</h4> <form action="<%= request.getContextPath() %>/orderSubmit" method="post"> <!--如果当前jsp文件不在项目根目录下,必须加上request.getContextPath()> <table cellspacing="0" cellpadding="10" border="1"> <tr>基本信息</tr> <tr> <td>姓名</td> <td><input type="text" name="username"></td> </tr> <tr> <td>寄送地址</td> <td><input type="text" name="address"></td> </tr> </table> <table cellspacing="0" cellpadding="10" border="1"> <tr>信用卡信息</tr> <tr> <td>种类</td> <td> <input type="radio" name="kinds" id="Visa" value="Visa">Visa <!-- 必须写value值 否则radio名称取不出> <br> <input type="radio" name="kinds" id="Master" value="Master">Master </td> </tr> <tr> <td>卡号</td> <td><input type="text" name="cardNum"></td> </tr> </table> <br> <input type="submit" name="submitBtn" value="继续"> </form> </body>
第二个servlet,处理上一个表单的提交
@WebServlet("/orderSubmit") public class OrderSubmit extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); //取中文参数值时 防止乱码 request.getRequestDispatcher("/ShopCart/third.jsp").forward(request, response); //请求转发 } }
第3个jsp页面
<body>
<h4>Step3:订单确认</h4>
<table cellspacing="0" cellpadding="10" border="1">
<tr>
<td>姓名</td>
<td><%=request.getParameter("username") %></td>
</tr>
<tr>
<td>寄送地址</td>
<td><%=request.getParameter("address") %></td>
</tr>
</table>
<table cellspacing="0" cellpadding="10" border="1">
<tr>付款信息</tr>
<tr>
<td>信用卡类型</td>
<td><%=request.getParameter("kinds") %></td>
</tr>
<tr>
<td>卡号</td>
<td><%=request.getParameter("cardNum") %></td>
</tr>
<tr>
<td>订货项目</td>
<td>
<%
String [] books = (String[])request.getSession().getAttribute("books");
for(String book:books){
%> <%=book %>
<br>
<%
}
%>
</td>
</tr>
</table>
</body>
51 JavaWEB中的相对路径和绝对路径
1. 绝对路径的问题
1)开发时建议编写“绝对路径”,写绝对路径肯定没有问题,但写相对路径可能会有问题
在由servlet转发到JSP页面时,此时浏览器地址栏上显示的是servlet的路径,而若jsp页面的超链接还是相对于该jsp页面的地址,则可能出现路径混乱的问题
/a.jsp
-path
/b.jsp
/c.jsp
a.jsp ->servlet-转发->b.jsp(有一个超链接:和b.jsp在同一路径下的c.jsp)->无法得到c页面
2)编写绝对路径可以避免上述问题:
① 在JavaWeb中什么叫“绝对路径”,相对于当前WEB应用的根路径(而非站点的根路径)的路径,则任何的路径都必须带上ContextPath
② 如何完成编写
3)JavaWEB开发中的/到底代表什么?
① 当前WEB应用的根路径:http://localhost:8989/ContextPath/
> 请求转发时:request.getRequestDispatcher("/ShopCart/third.jsp").forward(request, response)
> web.xml文件中映射Servlet访问路径:
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>
> 各种定制标签中的 /
② 当前WEB站点的根路径:http://localhost:8989/
>超链接:<a href="/TestServlet">To b Page</a>
>表单中的action:<form action="/login.jsp">
>做请求重定向的时候:response.sendRedirect("/")
52 HttpSession之表单的重复提交
1. 表单的重复提交
1)重复提交的情况:
① 在表单提交到一个servlet,而servlet又通过请求转发的方式响应一个jsp或html页面,此时地址栏还保留着servlet的那个路径,在响应页面点击“刷新”,会导致重复提交
② 在响应页面没有到达时,多次点击提交按钮,会导致重复提交
③ 返回表单页面,直接再次点击提交,也算重复提交
2)如何避免表单重复提交
在表单中做一个标记,提交到servlet中时,检查标记是否存在且是否和预定义的标记一致,若一致,则受理请求,并销毁标记,若不一致或没有标记,则直接响应提示信息:“重复提交”
① 仅提供一个隐藏域:<input type="hidden" name="token" value="tokenName"> 行不通,没有方法清除固定的请求参数
② 把标记放在request中,行不通,因为表单页面刷新后,request已经被销毁,再提交表单是一个新的request
③ 把标记放在session中 可以
>在原表单页面,生成一个随机值token
>在原表单页面,把token值放入session属性中
>在原表单页面,把token放入到隐藏域中
>在目标的servlet中,获取session和隐藏域中的token值
>比较两个值是否一致,若一致,受理请求,且把session域中的token属性清除
>若不一致,则直接响应提示页面:“重复提交”
a.jsp
<body> <h4>aaa page...</h4> <% String tokenValue = new Date().getTime() + ""; //生成一个随机值 session.setAttribute("token", tokenValue); //将随机值放进属性中 %> <form action="<%=request.getContextPath() %>/tokenServlet" method="post"> <input type="hidden" name="token" value=<%=tokenValue %> > //隐藏域中放入随机值 姓名: <input type="text" name="name"></input> <input type="submit" value="提交"> </form> </body>
tokenServlet
@WebServlet("/tokenServlet") public class TokenServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String hiddenToken = request.getParameter("token"); Object sessionToken =request.getSession().getAttribute("token"); if(sessionToken != null && sessionToken.equals(hiddenToken)){ //比较两者是否相同 request.getSession().removeAttribute("token"); }else{ response.sendRedirect(request.getContextPath() + "/token/error.jsp"); //提示重复提交了 return; } String name = request.getParameter("name"); System.out.println(name); request.getRequestDispatcher("token/b.jsp").forward(request, response); //进入到提交成功页面 } }
在structs框架中,就有token相关的组件,但是这里我没有详细了解怎么使用这个组件
53 HttpSession之验证码
1)基本原理:和表单重复提交一致:
>在原表单页面,生成一个验证码的图片,生成图片的同时,需要把该图片中的字符串放入到session中
>在原表单页面,定义一个文本域,用于输入验证码
>在目标的servlet中,获取session和表单域中的验证码的值
>比较两个值是否一致,若一致,受理请求,且把session域中的验证码属性清除
>若不一致,则直接通过重定向的方式返回原页面,并提示:验证码输入错误
验证码图片生成的servlet,本部分代码是网络上某位兄弟的,多谢:
/** * 生成随机图片,用来作为验证码 */ public class ValidateCodeServlet extends HttpServlet { private static final long serialVersionUID = 3038623696184546092L; public static final int WIDTH = 120;//生成的图片的宽度 public static final int HEIGHT = 30;//生成的图片的高度 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //在刷新页面后不会一直出现“验证码错误”信息 if(request.getSession() != null && request.getSession().getAttribute("message") != null){ request.getSession().removeAttribute("message"); } String createTypeFlag = request.getParameter("createTypeFlag");//接收客户端传递的createTypeFlag标识 //1.在内存中创建一张图片 BufferedImage bi = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB); //2.得到图片 Graphics g = bi.getGraphics(); //3.设置图片的背影色 setBackGround(g); //4.设置图片的边框 setBorder(g); //5.在图片上画干扰线 drawRandomLine(g); //6.写在图片上随机数 //String random = drawRandomNum((Graphics2D) g,"ch");//生成中文验证码图片 //String random = drawRandomNum((Graphics2D) g,"nl");//生成数字和字母组合的验证码图片 //String random = drawRandomNum((Graphics2D) g,"n");//生成纯数字的验证码图片 //String random = drawRandomNum((Graphics2D) g,"l");//生成纯字母的验证码图片 String random = drawRandomNum((Graphics2D) g,createTypeFlag);//根据客户端传递的createTypeFlag标识生成验证码图片 //7.将随机数存在session中 request.getSession().setAttribute("checkcode", random); //8.设置响应头通知浏览器以图片的形式打开 response.setContentType("image/jpeg");//等同于response.setHeader("Content-Type", "image/jpeg"); //9.设置响应头控制浏览器不要缓存 response.setDateHeader("expries", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); //10.将图片写给浏览器 ImageIO.write(bi, "jpg", response.getOutputStream()); } /** * 设置图片的背景色 * @param g */ private void setBackGround(Graphics g) { // 设置颜色 g.setColor(Color.WHITE); // 填充区域 g.fillRect(0, 0, WIDTH, HEIGHT); } /** * 设置图片的边框 * @param g */ private void setBorder(Graphics g) { // 设置边框颜色 g.setColor(Color.BLUE); // 边框区域 g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2); } /** * 在图片上画随机线条 * @param g */ private void drawRandomLine(Graphics g) { // 设置颜色 g.setColor(Color.GREEN); // 设置线条个数并画线 for (int i = 0; i < 5; i++) { int x1 = new Random().nextInt(WIDTH); int y1 = new Random().nextInt(HEIGHT); int x2 = new Random().nextInt(WIDTH); int y2 = new Random().nextInt(HEIGHT); g.drawLine(x1, y1, x2, y2); } } /** * 画随机字符 * @param g * @param createTypeFlag * @return * String... createTypeFlag是可变参数, * Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。注意:可变参数必须位于最后一项 */ private String drawRandomNum(Graphics2D g,String... createTypeFlag) { // 设置颜色 g.setColor(Color.RED); // 设置字体 g.setFont(new Font("宋体", Font.BOLD, 20)); //常用的中国汉字 String baseChineseChar = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6"; //数字和字母的组合 String baseNumLetter = "0123456789ABCDEFGHJKLMNOPQRSTUVWXYZ"; //纯数字 String baseNum = "0123456789"; //纯字母 String baseLetter = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; //createTypeFlag[0]==null表示没有传递参数 if (createTypeFlag.length > 0 && null != createTypeFlag[0]) { if (createTypeFlag[0].equals("ch")) { // 截取汉字 return createRandomChar(g, baseChineseChar); }else if (createTypeFlag[0].equals("nl")) { // 截取数字和字母的组合 return createRandomChar(g, baseNumLetter); }else if (createTypeFlag[0].equals("n")) { // 截取数字 return createRandomChar(g, baseNum); }else if (createTypeFlag[0].equals("l")) { // 截取字母 return createRandomChar(g, baseLetter); } }else { // 默认截取数字和字母的组合 return createRandomChar(g, baseNumLetter); } return ""; } /** * 创建随机字符 * @param g * @param baseChar * @return 随机字符 */ private String createRandomChar(Graphics2D g,String baseChar) { StringBuffer sb = new StringBuffer(); int x = 5; String ch =""; // 控制字数 for (int i = 0; i < 4; i++) { // 设置字体旋转角度 int degree = new Random().nextInt() % 30; ch = baseChar.charAt(new Random().nextInt(baseChar.length())) + ""; sb.append(ch); // 正向角度 g.rotate(degree * Math.PI / 180, x, 20); g.drawString(ch, x, 20); // 反向角度 g.rotate(-degree * Math.PI / 180, x, 20); x += 30; } return sb.toString(); } }
前台页面:
<font color="red"> <a><%=session.getAttribute("message")==null ? "" : session.getAttribute("message") %></a> </font> <body> <form action="<%= request.getContextPath()%>/formServlet" method="post"> 姓名:<input type="text" name="name"> 验证码:<input type="text" name="validateCode"> <img alt="" src="<%= request.getContextPath()%>/validateCodeServlet"> <input type="submit" name="submit" value="提交"> </form> </body>
表单处理的servlet:
/** * 有了下面这一行代码可以不用在web.xml中配置servlet * <servlet> * <servlet-name>ValidateCodeServlet</servlet-name> * <servlet-class>com.test.token.ValidateCodeServlet</servlet-class> * </servlet> * <servlet-mapping> * <servlet-name>ValidateCodeServlet</servlet-name> * <url-pattern>/validateCodeServlet</url-pattern> * </servlet-mapping> * 但是以上两种不能同时存在 */ @WebServlet("/formServlet") public class FormServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取表单中提交的属性值 String checkCode = request.getParameter("validateCode"); //获取session中的checkcode属性值 Object sCheckCode = request.getSession().getAttribute("checkcode"); //比对,若一致则成功;不一致则提示验证码错误 if(sCheckCode !=null && sCheckCode.equals(checkCode)){ request.getSession().removeAttribute("checkcode"); }else{ //验证失败时,在session的message属性中放入验证错误提示信息 request.getSession().setAttribute("message", "验证码错误"); response.sendRedirect(request.getContextPath() + "/token/index.jsp"); return; } //验证码成功后的页面操作 response.sendRedirect(request.getContextPath() + "/token/b.jsp"); } }
54 HttpSession小结(2)
见以上蓝色字体
55 使用JavaBean
讲了javaBean在jsp页面使用时的3个标签,了解就可以了。
<jsp:useBean id=""></jsp:useBean>
<jsp:setProperty property="" name=""/>
<jsp:getProperty property="" name=""/>
56 EL语法
全称为Expression Language,格式为:${中间为表达式} 可以写在jsp页面中,在显示数据时特别简单
一般使用.或[]两种运算符:
${sessionScope.customer.age} 等同于${sessionScope.customer.["age"]}
EL的隐含对象:
pageScope
requestScope
sessionScope
applicationScope
EL可以进行自动的类型转换
57 简单标签的HelloWorld
常见的jstl标签库:jstl.jar和standard.jar,没有的话在网上下载后导入项目即可
我们希望jsp页面中只有标签,而没有Java代码(将前端设计和后端开发分离开来):自定义标签的诞生
开发自定义标签,核心就是编写标签处理器类,实现simpleTag接口
自定义标签的开发和应用步骤:
1)编写完成标签功能的Java类(标签处理器)
实现simpleTag接口即可
public class HelloSimpleTag implements SimpleTag {
2)编写标签库描述(tld)文件,在tld文件中对自定义标签进行描述
在WEB-INF文件夹下新建一个.tld(标准库描述文件)为扩展名的xml文件
<taglib> <jsp-version>1.2</jsp-version> <tlib-version>1.0</tlib-version> <description>my test tag</description> <display-name>my test tag</display-name> <!-- 建议在jsp页面使用的前缀 prefix --> <short-name>mytag</short-name> <!-- 作为tld文件的id 用来唯一标识当前的tld文件,多个tld文件的uri不能重复 通过jsp页面的taglib标签的uri属性来引用 --> <uri>http://www.baidu.com/testtag/mytag</uri> <tag> <!-- 标签名 --> <name>tag1</name> <!-- 标签处理类全名称 --> <tag-class>com.test.tag.HelloSimpleTag</tag-class> <!-- 标签体的类型 不能缺少 否则会报错 --> <body-content>empty</body-content> </tag> </taglib>
3)在JSP页面中导入和使用自定义标签
<%-- 引入标签类 --%> <%@ taglib prefix="mytag" uri="http://www.baidu.com/testtag/mytag" %>
<mytag:tag1></mytag:tag1>
58 带属性的自定义标签
实现SimpleTag接口的标签处理器类的生命周期:执行顺序
setJspContext—>serParent—>setXXX—>setJspBody—>doTag
setJspContext():这个方法一定会被jsp引擎所调用,先于doTag,把代表jsp引擎的pageContext对象传给jsp页面
private PageContext pageContext; //jsp引擎调用,把代表jsp页面的pageContext对象传入 //pageContext可以获取jsp页面的其他8个隐含对象 //凡是jsp页面可以做的 标签处理器内部都可以处理 @Override public void setJspContext(JspContext arg0) { this.pageContext = (PageContext) arg0; }
带属性的自定义标签:
① 先在标签处理器类中定义setter方法。建议把所有的属性类型都设置为String
private String tag1attr1; private String tag1attr2; public void setTag1attr1(String tag1attr1) { this.tag1attr1 = tag1attr1; } public void setTag1attr2(String tag1attr2) { this.tag1attr2 = tag1attr2; }
② 在tld描述文件中来描述属性,使用attribute标签
<attribute> <name>tag1attr1</name> <required>true</required> <!-- runtime expression value指当前属性是否能接受运行表达式的值 --> <rtexprvalue>true</rtexprvalue> </attribute>
③ 在页面中使用属性:属性名同tld文件中定义的名字
<mytag:tag1 tag1attr1="1111" tag1attr2="10"></mytag:tag1>
④ 通常情况下开发简单标签直接继承simpleTagSupport就可以了 可以直接调用其对应的getter方法来得到对应的API
59 带标签体的自定义标签
setJspContext——> setParent——> setXXX——> setJspBody——> doTag
setJspContext一定会被调用,setParent为带父标签时会被调用,setXXX为带属性时会被调用,setJspBody为带标签体时会被调用,doTag为标签处理器方法
1) 若一个标签有标签体:
<mytag:tag2>hello Hnini baba tired</mytag:tag2>
在自定义标签的标签处理器中使用JspFragment 对象封装标签体信息
2)若配置了标签含有标签体,则JSP引擎会调用setJspBody() 方法把JspFragment 传递给标签处理器类 ,在SimpleTagSupport中还定义了一个getJspBody() 方法,用于返回JspFragment 对象
若标签处理器类直接继承了simpleTagSupport,则可以直接引用getJspBody()而获得JspFragment 对象
3)JspFragment 的常用API:JspFragment 的invoke(Writer)方法:把标签体内容输出到Writer指定的字符流中,若为null,则等同于invoke(getJspContext().getOut()),即直接把标签体内容输出到页面上
有时,可以借助于StringWriter,可以在标签处理器类中先得到标签体的内容:
//1.利用StringWriter得到标签体的问题内容 StringWriter sw = new StringWriter(); bodyContent.invoke(sw); //2.把标签体的内容转换为大写 String content = sw.toString().toUpperCase();
4) 在tld文件中,使用body-content节点来描述标签体的类型:取值可能有3种类型,一般情况下都为scriptless
empty:没有标签体
scriptless:标签体可以包含el表达式和JSP动作元素,但不能包含JSP的脚本元素
tagdepedent:表示标签体交由标签本身去解析处理。若指定tagdependent,在标签体中的所有代码都会原封不动地交给标签处理器,而不是将执行结果传给标签处理器类
简单的一个将标签体内容转化为大写并写到页面上的doTag() 重写代码为:
public class TestJspFragment extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { JspFragment bodyContent = getJspBody(); // JspFragment.invoke(Writer):Writer即为标签体内容输出的字符流,若为null, //则输出到getJspContext().getOut(),即输出到页面上 //bodyContent.getJspContext(); //1.利用StringWriter得到标签体的问题内容 StringWriter sw = new StringWriter(); bodyContent.invoke(sw); //2.把标签体的内容转换为大写 String content = sw.toString().toUpperCase(); //3.获取JSP页面的out隐含对象,输出到页面上 getJspContext().getOut().print(content); } }
5)定义一个自定义标签:练习
jsp页面中这样使用自定义标签 <mytag:tag3 time="8">Hi Hnini</mytag:tag3>
标签这样定义:
<tag> <name>tag3</name> <tag-class>com.test.tag.TestJspFragment2</tag-class> <body-content>scriptless</body-content> <attribute> <name>time</name> <required>true</required> </attribute> </tag>
标签处理器类这样实现:
public class TestJspFragment2 extends SimpleTagSupport{ //设置一个属性time private String time; public void setTime(String time) { this.time = time; } @Override public void doTag() throws JspException, IOException { //1.得到标签体的内容 JspFragment bodyContent = getJspBody(); StringWriter sw = new StringWriter(); bodyContent.invoke(sw); //2.变为大写 String body = sw.toString().toUpperCase(); //3.得到out隐含变量 //4.循环输出 int count = 1; try { count = Integer.parseInt(time); } catch (NumberFormatException e) { e.printStackTrace(); } for(int i = 0;i<count;i++){ bodyContent.getJspContext().getOut().print( (i+1) + ":" + body + "<br>"); } } }
6)实现forEach标签:练习
jsp页面这样写:
<body> <% List<Customer> customers = new ArrayList<Customer>(); customers.add(new Customer(11,"AAA")); customers.add(new Customer(22,"BBB")); customers.add(new Customer(33,"CCC")); customers.add(new Customer(44,"DDD")); customers.add(new Customer(55,"EEE")); request.setAttribute("customers", customers); %> <mytag:tag4 items="${requestScope.customers }" var="cust"> ${cust.id}--${cust.name} <br> </mytag:tag4> </body>
标签这样定义:
<tag> <name>tag4</name> <tag-class>com.test.tag.TestJSPFragment3</tag-class> <body-content>scriptless</body-content> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>var</name> <required>true</required> </attribute> </tag>
标签处理器类这样实现:
public class TestJSPFragment3 extends SimpleTagSupport { private Collection<?> items; public void setItems(Collection<?> items) { this.items = items; } private String var; public void setVar(String var) { this.var = var; } @Override public void doTag() throws JspException, IOException { if(items != null){ for(Object obj:items){ getJspContext().setAttribute(var, obj); getJspBody().invoke(null); } } } }
60 带父标签的自定义标签
1)父标签无法获取子标签的引用,父标签仅把子标签作为标签体来使用
2)子标签可以通过getParent()方法来获取父标签的引用(需继承simpleTagSupport或自实现simpleTag接口的该方法)
若子标签的确有父标签,JSP引擎会把代表父标签的引用通过 setParent(JspTag parent)赋给标签处理器
3)注意:父标签的类型时JspTag类型,该接口是一个空接口,但是来统一simpleTag和Tag的,实际使用需要进行类似的强制转转
4)在tld配置文件中,无需为父标签进行额外配置,但子标签是以标签体的形式存在的,所以父标签的<body-content></body-content>
需设置为scriptless
5)练习:实现如下标签
<mytag2:choose> <mytag2:when comparation="${param.age > 24.0 }">你大学毕业了...</mytag2:when> <mytag2:when comparation="${param.age > 20.0 }">你只是高中毕业...</mytag2:when> <mytag2:otherwise>高中还没毕业,小屁孩一个...</mytag2:otherwise> </mytag2:choose>
> 开发3个标签:choose when otherwise
> 其中when标签有一个boolean类型的属性:test
> choose是when和otherwise 的父标签,when在otherwise之前使用
> 在父标签choose中定义一个“全局”的boolean类型的flag:用于判断子标签在满足条件的情况下是否执行
* 若when的test为true 且when的父标签的flag也为true 则执行when的标签体 同时把flag设置为false
* 若when的test为true 且when的父标签的flag为false 则不执行标签体
* 若flag为true otherwise执行标签体
第一步:tld文件中配置标签
<tag> <name>choose</name> <tag-class>com.test.tag.ChooseTag</tag-class> <body-content>scriptless</body-content> </tag> <tag> <name>when</name> <tag-class>com.test.tag.WhenTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>comparation</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> <tag> <name>otherwise</name> <tag-class>com.test.tag.OtherWiseTag</tag-class> <body-content>scriptless</body-content> </tag>
第二步:写标签处理器类 ChooseTag WhenTag OtherWiseTag
public class ChooseTag extends SimpleTagSupport { private boolean flag = true; //设置标识 配备标识的读写方法 public void setFlag(boolean flag) { this.flag = flag; } public boolean isFlag() { return flag; } @Override public void doTag() throws JspException, IOException { getJspBody().invoke(null); //直接显示标签体到页面上 子标签也可以当做标签体 } }
public class WhenTag extends SimpleTagSupport { private boolean comparation; //该标签有一个属性 public void setComparation(boolean comparation) { this.comparation = comparation; } @Override public void doTag() throws JspException, IOException { ChooseTag chooseTag = (ChooseTag)getParent(); //获得父标签的引用,需要强制转换 if(chooseTag.isFlag()){ //标识未被标记 需要继续判断when标签 if(comparation){ //是否符合when标签的执行条件 getJspBody().invoke(null); chooseTag.setFlag(false); //已经找到适合的条件 给整体做标记 } } } }
public class OtherWiseTag extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { ChooseTag chooseTag = (ChooseTag)getParent(); if(chooseTag.isFlag()){ getJspBody().invoke(null); } } }
61 EL自定义函数
第一步:定义一个自定义函数的处理类 必须为public 其中的自定义方法必须为public static
public class FunctionTag { public static String concat(String str1,String str2){ return str1 + str2; } }
第二步:在标签库描述文件tld文件中描述function标签
<function> <name>testfunc</name> <function-class>com.test.tag.FunctionTag</function-class> <function-signature>java.lang.String concat(java.lang.String,java.lang.String)</function-signature> </function>
第三步:在jsp页面引入标签库描述文件,引用自定义函数
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="myfunc" uri="http://www.baidu.com/myfunction" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> ${myfunc:testfunc("AA","BB") } </body> </html>
62 简单标签小结
以上红色字体重点掌握
未完待续......