发布时间:2025-12-10 19:31:24 浏览次数:3
Head First Servlets & JSP学习笔记Servlet&JSPWeb服务器做些什么Web服务器接收客户请求,然后向客户返回一些结果。如果没有请求的资源,则返回404NotFound错误。Web客户端做些什么Web客户端允许用户请求服务器上的某个资源,并且向用户显示请求的结果。客户和服务器都知道HTML和HTTPHTML告诉浏览器怎样向用户显示内容。HTTP是Web上客户和服务器之间进行通信所
| 标记 | 描述 |
|---|---|
<!-- --> | 注释 |
<a> | 锚点——通常用来放一个超链接 |
<align> | 对齐 |
<body> | 定义文本体的边界 |
<br> | 行分隔 |
<center> | 将内容居中 |
<form> | 定义一个表单 |
<h1> | 一级标题 |
<head> | 定义文档首部的边界 |
<html> | 定义HTML文档的边界 |
<input type> | 在表单中定义一个输入组件 |
<p> | 一个新段落 |
<title> | HTML文档的标题 |
GET /socket.io/1/xhr-polling/cjJ4rr2VUk0aTC0JKKvG?t=1493719041095 HTTP/1.1\r\n Host: s1-im-notify.csdn.net\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/5.0.2.2000 Chrome/47.0.2526.73 Safari/537.36\r\nOrigin: http://write.blog.csdn.net\r\nAccept: */*\r\nDNT: 1\r\nReferer: http://write.blog.csdn.net/postlist\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN\r\n 是否还在为Ide开发工具频繁失效而烦恼,来吧关注以下公众号获取最新激活方式。亲测可用!
【正版授权,激活自己账号】:Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】:官方授权 正版激活 自己使用,支持Jetbrains家族下所有IDE…
GET /socket.io/1/xhr-polling/cjJ4rr2VUk0aTC0JKKvG?t=1493719041095 HTTP/1.1\r\n为请求行 /socket.io/1/xhr-polling/cjJ4rr2VUk0aTC0JKKvG:Web服务器上资源的路径。 ?t=1493719041095:在GET请求中,参数(如果有)会追加到请求URL第一部分的后面,以”?”开头。各参数之间用”&”分隔。 HTTP/1.1:Web浏览器所请求的协议的版本。 其余部分为请求首部:
Host: s1-im-notify.csdn.net\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/5.0.2.2000 Chrome/47.0.2526.73 Safari/537.36\r\nOrigin: http://write.blog.csdn.net\r\nAccept: */*\r\nDNT: 1\r\nReferer: http://write.blog.csdn.net/postlist\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN\r\n Accept表示可以接受的MIME类型。
POST /pollmessage HTTP/1.1\r\n Host: p.f.360.cn\r\nAccept: */*\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\nContent-Length: 358\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n [Full request URI: http://p.f.360.cn/pollmessage][HTTP request 1/1] [Response in frame: 150751] POST /pollmessage HTTP/1.1\r\n:请求行 POST:HTTP方法 /pollmessage:资源在Web服务器上的路径 HTTP/1.1:Web浏览器请求的协议的版本 请求首部:
Host: p.f.360.cn\r\nAccept: */*\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\nContent-Length: 358\r\nContent-Type: application/x-www-form-urlencoded\r\n 消息体(负载):
[Full request URI: http://p.f.360.cn/pollmessage][HTTP request 1/1] [Response in frame: 150751] 体包含了让浏览器显示的具体内容。
HTTP/1.1 200 OK\r\nServer: nginx\r\nDate: Tue, 02 May 2017 13:01:11 GMT\r\nContent-Type: application/octet-stream\r\nContent-Length: 160\r\nConnection: close\r\nCache-Control: no-cache\r\npragma: no-cache\r\n\r\n [HTTP response 1/1] [Time since request: 0.117763000 seconds][Request in frame: 150745] HTTP/1.1:Web服务器使用的协议的版本 200:响应的HTTP状态码 OK:状态码的响应文本 Content-Type: application/octet-stream:MIME类型。MIME类型告诉浏览器要接收的数据是什么类型,这样浏览器才能知道如何显示这些数据。响应中必须有的一个首部。 HTTP响应首部:
HTTP/1.1 200 OK\r\nServer: nginx\r\nDate: Tue, 02 May 2017 13:01:11 GMT\r\nContent-Type: application/octet-stream\r\nContent-Length: 160\r\nConnection: close\r\nCache-Control: no-cache\r\npragma: no-cache\r\n 体:
[HTTP response 1/1][Time since request: 0.117763000 seconds][Request in frame: 150745] http://code.thunisoft.com:80/media/asr-server/blob/master/asrServer/readme/interface/接口文档.md http://:协议,告诉服务器使用什么通信协议。 code.thunisoft.com:服务器:所请求物理服务器的唯一名。这个名字映射到一个唯一的IP地址。IP地址是一个数字,形式为”xxx.yyy.zzz.aaa”。在这里也可以指定一个IP地址而不是服务器名,不过服务器名更容易记。 80:端口,URL的这一部分是可选的。一个服务器可以支持多个端口。一个服务器应用由端口标识。如果在URL中没有指定的端口,默认端口则是端口80,而且很幸运,这正是Web服务器的默认端口。 media/asr-server/blob/master/asrServer/readme/interface/接口文档.md:路径:所请求资源在服务器上的路径。 接口文档.md:所请求内容的名字。这一部分是可选的,如果没有这一部分,大多数Web服务器都会默认地查找index.html。 常用服务器应用TCP端口号:
| 应用 | 端口 |
|---|---|
| FTP | 21 |
| Telnet | 23 |
| SMTP | 25 |
| Time | 37 |
| HTTP | 80 |
| POP3 | 110 |
| HTTPS | 443 |
创建一个目录树:
/project1 /src /classes /etc 编写一个Ch1Servlet.java的servlet,把它放在src目录中:
import javax.servlet.*;import javax.servlet.http.*;import java.io.*;public class Ch1Servlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { PrintWriter out = resp.getWriter(); java.util.Date today = new java.util.Date(); out.println("<html>" + "<body>" + "<h1 align=center>HF\'s Chapter1 Servlet</h1>" + "<br>" + today + "</body>" + "</html>"); }} 创建一个部署文件(deployment descriptor,DD),名为web.xml,把它放在etc目录中。(可以从tomcat的例子中拷贝一份出来修改)
<?xml version="1.0" encoding="ISO-8859-1"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>Chapter1 Servlet</servlet-name> <servlet-class>Ch1Servlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Chapter1 Servlet</servlet-name> <url-pattern>/Serv1</url-pattern> </servlet-mapping></web-app> 在以后的Tomcat目录下建立这个目录树:
/tomcat /webapps /ch1 /WEB-INF /classes 从project1目录编译servlet:
javac -classpath "G:\Tomcat 8.5\lib\servlet-api.jar" -d classes src/Ch1Servlet.java 把Ch1Servlet.class文件复制到WEB-INF/classes,再把web.xml文件复制到WEB-INF。
启动Tomcat打开浏览器,键入:
http://localhost:8080/ch1/Serv1 每次更新servlet类或部署文件都需要关闭Tomcat。
import javax.servlet.*;import javax.servlet.http.*;import java.io.*;// 99.9999%的servlet都是HttpServletpublic class Ch1Servlet extends HttpServlet { // 在实际中,99.9%的servlet都会覆盖doGet()或doPost()方法 // servlet从这里拿到容器创建的请求和响应对象的引用 public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { // 在servlet从容器得到的响应对象中,可以拿到一个PrintWriter。 // 使用这个PrintWriter能够将HTML文本输出到响应对象 PrintWriter out = resp.getWriter(); java.util.Date today = new java.util.Date(); out.println("<html>" + "<body>" + "<h1 align=center>HF\'s Chapter1 Servlet</h1>" + "<br>" + today + "</body>" + "</html>"); }} 建立servlet名的映射,这有助于改善应用的灵活性和安全性。
<servlet>: 内部名映射到完全限定类名 <servlet-mapping>: 内部名映射到公共URL名 <?xml version="1.0" encoding="ISO-8859-1"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!-- <servlet>元素告诉容器哪些类文件属于一个特定Web应用 --> <servlet> <!-- <servlet-name>元素用于把一个<servlet>元素绑定到一个 特定的<servlet-mapping>元素。最终用户绝对看不到这个名, 这个名只在这个部署描述文件的其他部分中使用。 --> <servlet-name>Chapter1 Servlet</servlet-name> <!-- 在<servlet-class>里放置类的完全限定名(但是不要加".class"扩展名) --> <servlet-class>Ch1Servlet</servlet-class> </servlet> <!-- <servlet-mapping>映射URL和servlet --> <servlet-mapping> <servlet-name>Chapter1 Servlet</servlet-name> <!-- <url-pattern>里是客户看到(并使用)的servlet名。 这是一个虚构的名字,并不是具体servlet的名字 --> <url-pattern>/Serv1</url-pattern> </servlet-mapping></web-app> 还可以使用部署描述文件对Web应用的其他方面进行定制,包括安全角色,错误页面,标记库,初始配置信息等。 部署描述文件(DD)提供了一种“声明”机制来定制Web应用,而无需修改源代码。 /projects /web /beerV1 /etc web.xml -- 配置文件 /lib -- 在这里放第三方JAR文件 /src -- 所有Java代码都放在src目录下 /com /cynhard /web -- 在这里放控制组件 BeerSelect.java /model -- 在这里放模型组件 BeerExpert.java /classes -- 与src对应,放置相应的.class文件 /com /cynhard /web BeerSelect.class /model BeerExpert.class /web -- 静态和动态视图组件放在这里 result.jsp form.html ---------------- 特定于Tomcat的部分 ----------------/Tomcat 8.5 -- Tomcat主目录 /webapps /Beer-v1 -- Web应用的名字,在Tomcat解析URL是用到。----------------- Servlet规范的部分 ---------------- form.html result.jsp /WEB-INF web.xml -- 必须放在WEB-INF中 /lib /classes----------------- 特定于应用的部分 ------------------ /com -- 这个包结构与开发环境中使用的结构完全相同 /cynhard /web BeerSelect.class /model BeerExpert.class 4.1.1 编写开始页面
<html> <body> <h1 align="center">Beer Selection Page</h1> <form method="POST" action="SelectBeer.do"> Select beer characteristics<p> Color: <!-- size表示下拉类表中可见选项的数目 --> <select name="color" size="1"> <option value="light"> light </option> <option value="amber"> amber </option> <option value="brown"> brown </option> <option value="dark"> dark </option> </select> <br><br> <center> <input type="SUBMIT"> </center> </form> </body></html> 4.1.2 部署和测试开始页面
在开发环境中创建HTML。将上面的HTML页面保存到开发环境中的 /beerV1/web/form.html文件中。 把这个文件复制到部署环境:/webapps/Beer-v1/form.html。在开发环境中创建DD,保存到开发环境中的 /beerV1/etc/web.xml中。
<?xml version="1.0" encoding="ISO-8859-1"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>Ch3 Beer</servlet-name> <servlet-class>com.cynhard.web.BeerSelect</servlet-class> </servlet> <servlet-mapping> <servlet-name>Ch3 Beer</servlet-name> <url-pattern>/SelectBeer.do</url-pattern> </servlet-mapping></web-app> 把DD文件拷贝到部署环境:/webapps/Beer-v1/WEB-INF/web.xml。
启动Tomcat。 测试页面。在浏览器输入:http://localhost:8080/Beer-v1/form.html。 4.2.1 控制器servlet的第1版
第一版只要确保HTML页面能适当的调用servlet,而且servlet能正确的接收HTML参数就可以了。在开发环境的src/com/cynhard/web目录下创建BeerSelect.java,内容如下:
package com.cynhard.web; // 确保与前面创建的开发结构和部署结构匹配import javax.servlet.*;import javax.servlet.http.*;import java.io.*;// HttpServlet扩展了GenericSevlet,GenericServlet则实现了Servlet接口public class BeerSelect extends HttpServlet { // 我们使用doPost来处理HTTP请求,因为HTTP表单指出:method=POST public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("Beer Selection Advice<br>"); String c = request.getParameter("color"); out.println("<br>Got beer color " + c); }} 4.2.2 编译、部署和测试控制器servlet
编译servlet
javac -classpath "G:\Tomcat 8.5\lib\servlet-api.jar";classes;. -d classes src\com\cynhard\web\BeerSelect.java -d 选项告诉编译器,把.class文件放在适当包结构中的classes目录下。
部署servlet
将开发环境的BeerSelect.class拷贝到部署环境中: webapps/Beer-v1/WEB-INF/classes/com/cynhard/web/BeerSelect.class
测试servlet
重启tomcat 浏览器中输入:http://localhost:8080/Beer-v1/form.html 选择一种啤酒颜色,提交。 如果servlet正常运行,就会在浏览器中看到结果。 4.3.3 构建和测试模型
package com.cynhard.model;import java.util.*;public class BeerExpert { public List getBrands(String color) { List<String> brands = new ArrayList<String>(); if (color.equals("amber")) { brands.add("Jack Amber"); brands.add("Red Moose"); } else { brands.add("Jail Pale Ale"); brands.add("Gout Stout"); } return brands; }} 编译 :
javac -d classes -Xlint:unchecked src\com\cynhard\model\BeerExpert.java 将边编译后的BeerExpert.class放到部署目录:
\webapps\Beer-v1\WEB-INF\classes\com\cynhard\model 第2版servlet代码
package com.cynhard.web;import com.cynhard.model.*;import javax.servlet.*;import javax.servlet.http.*;import java.io.*;import java.util.*;public class BeerSelect extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { String c = request.getParameter("color"); // 实例化BeerExpert类,调用getBrands() BeerExpert be = new BeerExpert(); List result = be.getBrands(c); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("Beer Selection Advice<br>"); // 打印建议 Iterator it = result.iterator(); while (it.hasNext()) { out.print("<br>try: " + it.next()); } }} 编译:
javac -classpath "G:\Tomcat 8.5\lib\servlet-api.jar";classes;. -d classes src\com\cynhard\web\BeerSelect.java 部署和测试
将编译的.class文件复制到部署环境:
\webapps\Beer-v1\WEB-INF\classes\com\cynhard\web 重启tomcat。 重新打开页面。 4.5.1 新建JSP,保存在开发环境下:\beerV1\web\result.jsp。
<!-- 这是一个“页面指令” --><%@ page import="java.util.*" %><html><body><h1 align="center">Beer Recommendations JSP</h1><p><!-- <% %> 标记里有一些标准Java代码(这称为scriptlet) --><% List styles = (List)request.getAttribute("styles"); Iterator it = styles.iterator(); while (it.hasNext()) { out.print("<br>try: " + it.next()); } %></body></html> 将其拷贝到部署目录
\webapps\Beer-v1 4.5.2 改进servelt,调用JSP
package com.cynhard.web;import com.cynhard.model.*;import javax.servlet.*;import javax.servlet.http.*;import java.io.*;import java.util.*;public class BeerSelect extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String c = request.getParameter("color"); // 实例化BeerExpert类,调用getBrands() BeerExpert be = new BeerExpert(); List result = be.getBrands(c); // 为请求对象增加一个属性,供JSP使用。 request.setAttribute("styles", result); // 为JSP实例化一个请求分派器。 RequestDispatcher view = request.getRequestDispatcher("result.jsp"); // 使用请求分派器要求容器准备好JSP,并向JSP发送请求和响应。 view.forward(request, response); }} 编译、部署和测试:和之前的一样,这里略过。
时序图
声明周期中的三大重要时刻
| 方法 | 何时调用 | 作用 | 是否覆盖 |
|---|---|---|---|
| init() | servlet实例创建后,并在 servlet能为客户请求提供 服务之前,容器对servlet 调用init()。 | 使你在sevlet处理客户请求 之前有机会对其初始化。 | 有可能。 |
| service() | 客户请求到来时,容器会 开始或分配一个新线程, 调用service()。 | 查看请求类型,决定调用 doGet()或者doPost()。 | 不需要。 |
| doGet()和/或doPost() | service()根据请求的HTTP 方法调用doGet()或doPost() | 在这里写业务逻辑代码。 | 至少覆盖其一。 |
service()方法总是在自己的栈中调用。容器运行多个线程来处理对一个servlet的多个请求。
method="POST" 是POST,method=”GET”或者没有method都是GET。 获取参数
获取单个参数:
String param1 = request.getParameter("param1");String param2 = request.getParameter("param2"); 如果参数是数组:
String[] param = request.getParameterValues("param") 客户的平台和浏览器信息
String client = request.getHeader("User-Agent"); 与请求相关的cookie
Cookie[] cookies = request.getCookies(); 与客户相关的会话(session)
HttpSession session = request.getSession(); 请求的HTTP方法
String method = request.getMethod(); 请求的输入流
InputStream input = request.getInputStream(); 可以用PrintWriter对象或OutputStream对象输出数据,PrintWriter是对OutputStream的封装,用来写入文本数据。OutputStream用来写入二进制数据。PrintWriter通过getWriter()方法得到。OutputStream对象由getOutputStream()方法得到:
PrintWriter writer = response.getWriter();OutputStream out = response.getOutputStream(); setHeader()设置响应头部,addHeader()增加响应头部。setHeader()会覆盖现有的值。addHeader()会增加另外一个值。
重定向是一个字符串,如果不以/开头,则重定向以当前目录为基路径。如果以/开头,则重定向以Web应用为基路径。
/开头 以/开头 不能在写到响应之后再调用sendRedirect()。
请求分派交给服务器上的其他部分处理。
RequestDispatcher view = request.getRequestDispatcher("result.jsp");view.forward(request, response); Servlet初始化参数:
在DD文件中:
<servlet> <servlet-name>Ch3 Beer</servlet-name> <servlet-class>com.cynhard.web.BeerSelect</servlet-class> <init-param> <param-name>myEmail</param-name> <param-value>cynhard85@126.com</param-value> </init-param> </servlet> 在servlet代码中:
writer.println(getServletConfig().getInitParameter("myEmail")); 在servlet初始化之前不能使用servlet初始化参数。
容器初始化一个servlet时,会为这个servlet建一个唯一的ServletConfig。容器从DD“读出”servlet初始化参数交给ServletConfig,然后把ServletConfig传递给servlet的init()方法。 servlet初始化参数只能读一次——就是在容器初始化servlet的时候。上下文初始化参数
DD中:
<servlet> <servlet-name>Ch3 Beer</servlet-name> <servlet-class>com.cynhard.web.BeerSelect</servlet-class></servlet><context-param> <param-name>myEmail</param-name> <param-value>cynhard85@126.com</param-value></context-param> Servlet中:
out.println(getServletContext().getInitParameter("myEmail")); <context-param>是针对整个应用的,因此放在所有<servlet>之外。
ServletContextListener使用
listener 代码:
package com.cynhard.web;import javax.servlet.*;public class MyServletContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent event) { ServletContext sc = event.getServletContext(); // ... } public void contextDestroyed(ServletContextEvent event) { // ... }} DD:
<listener> <listener-class> com.cynhard.MyServletContextListener </listener-class></listener> <listener>不放在<servlet>元素内部。
| 场景 | 监听者 | 事件类型 |
|---|---|---|
| 你想知道一个Web应用 上下文中是否增加、删 除或替换了一个属性 | javax.servlet.ServletContextAttributeListener | ServletContextAttributeEvent |
| 你想知道有多少个并发 用户。也就是说,你想 跟中活动的会话。 | javax.servlet.http.HttpSessionListener | HttpSessionEvent |
| 每次请求到来时你都想 知道,以便建立日志记 录。 | javax.servlet.ServletRequestListener | ServeltRequestEvent |
| 增加、删除或替换一个 请求属性时你希望能够 知道。 | javax.servlet.ServletRequestAttributeListener | ServletRequestAttributeEvent |
| 你有一个属性类(这个 类表示的对象将放在一 个属性中),而且你希 望这个类型的对象在绑 定到一个会话或从会话 删除时得到通知。 | javax.servlet.http.HttpSessionBindingListener | HttpSessionBindingEvent |
| 增加、删除或替换一个 会话属性时你希望能够 知道。 | javax.servlet.http.HttpSessionAttributeListener | HttpSessionBindingEvent |
| 你想知道是否创建或撤 销了一个上下文 | javax.servlet.ServletContextListener | ServletContextEvent |
| 你有一个属性类,而且希 望这个类型的对象在其 绑定的会话迁移到另一 个JVM时得到通知。 | javax.servlet.http.HttpSessionActivationListener | HttpSessionEvent |
属性和参数
| 2 | 属性 | 参数 |
|---|---|---|
| 类型 | 应用/上下文 请求 会话 | 应用/上下文初始化参数 请求参数 Servlet初始化参数 |
| 设置方法 | setAttribute(String name, Object value) | 不能设置应用和Servlet初始化参数。它们都在DD中设置。 |
| 返回类型 | Object | String |
| 获取方法 | getAttribute(String name) | getInitParameter(String name) |
属性的三个作用域:上下文、请求和会话
属性API
Object getAttribute(String name)void setAttribute(String name, Object value)void removeAttribute(String name)Enumeration getAttributeNames() 上下文作用域不是线程安全的。多个servlet可能有多个线程。 不能通过同步服务方法的方式保护上下文属性。而是对上下文加锁。
// 错误!上下文对象仍可以通过其他servlet访问public synchronized void doGet(...)// 正确,对上下文加锁synchronized(getServeltContext()) { //...} 会话属性同样不是线程安全的,需要保护: HttpSession session = request.getSession();synchronized(session) { // ...} 可以实现SingleThreadModel来保证servlet一次只得到一个请求。(SingleThreadModel已废弃) 只有请求属性和局部变量是线程安全的。 // 从ServletRequest得到RequestDispatcher// 参数如果以/开头,则从Web应用的根开始找// 如果不是以/开头,则从当前位置(请求发生的位置)开始找RequestDispatcher view = request.getRequestDispatcher("result.jsp");// 从ServletContext得到RequestDispatcher// 路径必须以/开头,只能从Web应用的根开始找ReqeustDispatcher view = getServletContext().getRequestDispaatcher("/result.jsp");// 在RequestDispatcher上调用forward()分派请求view.forward(request, response); request.getSession()获取Session。 session.isNew()返回是否新建的Session。 request.getSession(fase)获取已有的会话。如果得到返回HtteSession,否则返回null。 URL重写API:
response.encodeURL("/BeerTest.do")response.encodeRedirectURL("/BeerTest.do") 不能对静态页面完成URL重写。因此必须在运行时动态生成HTML。
要使用URL重写,页面必须是动态生成的。可以用JSP来完成URL重写。<c:URL>标签可以很容易的完成这个工作。 URL重写以开发商特定的方式处理。我们无需知道。 URL重写是自动的,但是只有当你对URL完成了编码时它才奏效。必须通过响应对象的一个方法(可以是encodeURL()或encodeRedirectURL())来运行所有URL,其他的所有事情都由容器来完成。 URL编码有响应处理。 要点
在写至响应的HTML中,URL重写把会话ID增加到其中所有URL的最后。 会话ID作为请求URL最后的“额外”信息再通过请求返回。 如果客户不接受cookie,URL重写会自动发生,但是必须显式地对所有URL编码。要对一个URL编码,需要调用response.encodeURL(aString)。
out.println("<a href='" + response.encodeURL("/BeerTest.do") + "'>click me</a>"); 没有办法对静态页面完成自动的URL重写,所以,如果你依赖于会话,就必须使用动态生成的页面。
| 它做什么 | 你用它做什么 | |
|---|---|---|
| getCreationTime() | 返回第一次创建会话的时间。 | 得出这个会话有多“老”。你可能想把某些会话的寿命限制为一个固定的时间。例如,你可能会说“一旦登陆,就必须在10分钟之内完成这个表单……” |
| getLastAccessedTime() | 返回容器最后一次得到包含这个会话ID的请求后过去了多长时间(毫秒数) | 得出客户最后一次访问这个会话是什么时候。可以用这个方法来确定客户是否已经离开很长时间,这样就可以向客户发出一封email,询问他们是否还回来。或者可以调用invalidate()结束会话。 |
| setMaxInactiveInterval() | 指定对于这个会话客户请求的最大间隔时间(秒数)。 | 如果已经过去了指定时间,而客户未对这个会话做出任何请求,就会导致会话被撤销。可以用这个方法减少服务器中无用的会话。 |
| getMaxInactiveInterval() | 返回对于这个会话客户请求的最大间隔时间(秒数)。 | 得出这个会话可以保持多长时间不活动但仍“存活”。可以使用这个方法来判断一个不活动的客户在会话撤销之前还有多长的“寿命”。 |
| invalidate() | 结束会话。当前存储在这个会话中的所有会话属性也会解除绑定。 | 如果客户已经不活动,或者你知道会话已经结束,可以用这个方法杀死(撤销)会话。会话实例本身可能由容器回收,但是这一点我们并不关心。置无效意味着会话ID不再存在,而且属性会从会话对象删除。 |
<session-config> <session-timeout>15</session-timeout></session-config> 设置某一个会话的超时 session.setMaxInactiveInterval(20 * 60); 创建一个新Cookie
Cookie cookie = new Cookie("username", name); 设置cookie在客户端上存活多久
// 以秒为单位,如果-1,则浏览器退出时cookie消失。cookie.setMaxAge(30 * 60); 把cookie发送到客户
response.addCookie(cookie); 从客户请求得到cookie(或多个cookie)
Cookie[] cookies = request.getCookies(); <% out.println(""); %> 导入单个包,指令最后没有分号
<%@ page import="foo.*" %> 导入多个包,逗号分隔
<%@ page import="foo.*,java.util.*" %> <%= Counter.getCount() %> 需要分号结束。
<%! int count = 0; %> | API | 隐式对象 |
|---|---|
| JspWriter | out |
| HttpServeltRequest | request |
| HttpServletResponse | response |
| HttpSession | session |
| ServletContext | application |
| ServletConfig | config |
| Throwable | exception |
| PageContext | pageContext |
| Object | page |
<!-- HTML注释 --><%-- JSP注释 --%> DD
<servlet> <servlet-name>MyTestInit</servlet-name> <jsp-file>/TestInit.jsp</jsp-file> <init-param> <param-name>email</param-name> <param-value>cynhard85@126.com</param-value> </init-param></servlet><servlet-mapping> <servlet-name>MyTestInit</servlet-name> <url-parttern>/TestInit.jsp</url-parttern></servlet-mapping> 覆盖jspInit()
<%! public void jspInit() { ServletConfig sConfig = getServletConfig(); String emailAddr = sConfig.getInitParameter("email"); ServletContext ctx = getServletContext(); ctx.setAttribute("mail", emailAddr); }%> | servlet中 | JSP中 | |
|---|---|---|
| 应用 | getServletContext().setAttribute(“foo”, barObj); | application.setAttribute(“foo”, barObj); |
| 请求 | request.setAttribute(“foo”, barObj); | request.setAttribute(“foo”, barObj); |
| 会话 | request.getSession().setAttribute(“foo”, barObj); | session.setAttribute(“foo”, barObj); |
| 页面 | 不适用! | pageContext.setAttribute(“foo”, barObj); |
<%-- 设置一个页面作用域属性 --%><% Float one = new Float(42.5); %><% pageContext.setAttribute("foo", one); %><%-- 获得一个页面作用域属性 --%><%= pageContext.getAttribute("foo"); %><%-- 使用pageContext设置一个会话作用域属性 --%><% Float two = new Float(22.4); %><% pageContext.setAttribute("foo", two, PageContext.SESSION_SCOPE); %><%-- 使用pageContext获得一个会话作用域属性 --%><%= pageContext.getAttribute("foo", PageContext.SESSION_SCOPE); %><%-- 等价于: --%><%= session.getAttribute("foo"); %><%-- 使用pageContext获得一个应用作用域属性 --%><%= pageContext.getAttribute("mail", PageContext.APPLICATION_SCOPE); %><%-- 等价于: --%><%= application.getAttribute("mail"); %><%-- 使用pageContext,即使不知道作用域也可以查找一个属性 --%><%-- 查找顺序:page->request->session->application --%><%= pageContext.findAttribute("foo"); %> <%@ page import="foo.*" session="false" %> 定义页面的特定的属性,以及这个页面是否要有隐式的会话对象。
| 属性 | 意义 |
|---|---|
| import | 定义Java import语句 |
| isThreadSafe | 定义生成的servlet是否需要实现SingleThreadModel(bad idea)。 |
| contentType | 定义JSP响应的MIME类型。 |
| isELIgnored | 定义转换这个页面时是否忽略EL表达式。 |
| isErrorPage | 定义当前页面是否是另一个JSP的错误页面。 |
| errorPage | 定义一个资源的URL,如果有未捕获到的Throwable,就会发送到这个资源。 |
| language | 定义scriptlet、表达式和声明中使用的脚本语言。 |
| extends | JSP会变成一个servlet类,这个属性则定义了这个类的超类。 |
| session | 定义页面是否有一个隐式的session对象。默认值为true。 |
| buffer | 定义隐式out对象(JspWriter的引用)如何处理缓存。 |
| autoFlush | 定义缓存的输出是否自动刷新输出。默认值是true。 |
| info | 定义放到转换后页面中的串,这样就能使用所生成servlet继承的getServletInfo()方法来得到这个信息。 |
| pageEncoding | 定义JSP的字符编码。默认为“ISO-8859-1” |
<%@ taglib tagdir="/WEB-INF/tags/cool" prefix="cool" %> 定义JSP可以使用的标记库。
<%@ include file="wickedHeader.html" %> 定义在转换时增加到当前页面的文本和代码。
EL和Java比较:
EL:${application.mail} Java:<%= application.getAttribute("mail") %> 让JSP禁用脚本元素(scriptlet、Java表达式或声明):
<jsp-config> <sp-property-group> <url-parttern>*.jsp</url-parttern> <scripting-invalid>true</scripting-invalid> </sp-property-group></jsp-config> 忽略EL(默认是启用的):
通过DD:
<jsp-config> <sp-property-group> <url-parttern>*.jsp</url-parttern> <el-ignored>true</el-ignored> </sp-property-group></jsp-config> 通过page指令:
<%@ page isELIgnored="true" %> <jsp:include page="wickedFooter.jsp" /> 其他动作: <c:set var="rate" value="32" /> Bean应用:
servlet中:
Person person = new Person();person.setName("Gopher");request.setAttribute("person", person); jsp中:
<jsp:useBean scope="request" /><jsp:getProperty name="person" property="name" /> 分解:
<jsp:useBean scope="request" />
<jsp:useBean>标识标准动作 声明bean对象的标识符。这对应于以下servlet代码中所用的名:request.setAttribute("person", p); 声明bean对象的类类型。 scope="request"标识这个bean对象的属性作用域。 <jsp:getProperty name="person" property="name" />
jsp:getProperty标识标准动作 name="person"标识具体的bean对象。这与<jsp:useBean>标记的“id”值匹配。 property="name"标识属性中的性质名(也就是bean类中获取方法和设置方法对应的性质)。 <jsp:useBean>如果找不到属性对象,则会创建一个。
可以用<jsp:setProperty>设置属性。
<jsp:setProperty name="person" property="name" value="Cynhard" /> 利用<jsp:useBean>体,可以有条件地运行代码,只有找不到bean属性,而且创建了一个新bean是才会运行体中的代码。
<!-- 只有request没有person属性时,才会设置person的name。 --><jsp:useBean scope="request"> <jsp:setProperty name="person" property="name" value="Cynhard" /></jsp:useBean> Bean标记会自动转换基本类型的性质
<jsp:useBean>增加一个type属性class为对象类型。type为引用类型。
<jsp:useBean type="foo.Person" scope="page"> type可以是class类型、抽象类型或者是一个接口,只要能用作为bean对象class类型的声明引用类型,都可以指定为type。
如果使用了type,但没有class,bean必须已经存在。如果使用了class(有或没有type),class不能是抽象类,而且必须有一个无参数的公共构造函数。 type是你声明的类型(可以使抽象类),class是你要实例化的类(必须是具体类):type x = new class() <jsp:useBean scope="page"/> 等价于:
<jsp:useBean /> 利用param属性,可以把bean的性质值设置为一个请求参数的值。只需指定请求参数!
TestBean.jsp:
<jsp:useBean type="foo.Person" clas="foo.Employee"> <jsp:setProperty name="person" property="name" param="userName"/></jsp:useBean> 直接获取userName的值:
<form acton="TestBean.jsp"> name: <input type="text" name="userName"/> ...</form> 如果请求参数名与bean性质名匹配,就不需要在<jsp:setProperty>标记中为该性质指定值。
TestBean.jsp,不再需要param。
<jsp:useBean type="foo.Person" clas="foo.Employee"> <jsp:setProperty name="person" property="name"/></jsp:useBean> 直接获取userName的值:
<form acton="TestBean.jsp"> name: <input type="text" name="name"/> ...</form> 如果所有请求参数名都与bean性质名匹配,则<jsp:setProperty>中可以用property=*来获取所有请求参数的值。
<jsp:setProperty name="person" property="*"/> 使用EL,打印嵌套性质变得非常容易……换句话说,可以很轻松地打印性质的性质。
<!-- 访问person的dog属性的name属性 -->${person.dog.name} EL表达式总是放在大括号里,而且前面有一个美元符前缀。
表达式中第一个命名变量可以是一个隐式对象,也可以是一个属性。
EL隐式对象:pageScope requestScope sessionScope applicationScope param paramValues header headerValues cookie initParam pageContext。在所有隐式对象中,只有pageContext不是Map。这是pageContext对象的实际引用。 属性:页面作用域中的属性、请求作用域中的属性、会话作用域中的属性、应用作用域中的属性。 使用点号操作符访问性质和映射值:
点号左边的变量要么是一个Map(有键),要么是一个bean(有性质)。不论变量是一个隐式对象还是一个属性,都是如此。 pageContext隐式对象是一个bean,它有获取方法,所有其他隐式对象都是Map。 如果对象是一个bean,但是指定的性质不存在,会抛出一个异常。[]就像是更好的点号:
${ person["name"]} []左边可以是List、数组、Map、Bean。[]里面可以是键、索引、属性名。
如果不是String直接量,就会计算。
${ musicMap[Ambient]} // 查找一个名为"Ambient"的属性。${ musicMap["Ambient"]} 方括号内可以使用嵌套表达式
${musicMap[MusicType[0]]} 点号后面不能接索引:${foo.1}是不行的。如果不能用作为Java代码中的变量名,就不能把它放在点号后面。
HTML中:
<form action="TestBean.jsp"> Name: <input type="text" name="name"> ID#: <input type="text" name="empID"> First food: <input type="text" name="food"> Second food: <input type="text" name="food"> <input type="submit"></form> JSP中:
Request param name is: ${param.name}<br>Request param empID is: ${param.empID}<br>Request param food is: ${param.food}<br> <!-- 得到food中的第一个值 -->First food request param: ${paramValues.food[0]}<br>Second food request param: ${paramValues.food[1]}<br>Request param name: ${paramValues.name[0]} 得到“host”首部:
${ header["host"]}${ header.host} 使用requestScope会得到请求属性,而不是request性质。要得到request性质,需要通过pageContext。
${pageContext.request.method} 打印“useName”Cookie的值:
脚本:
<% Cookie[] cookies = request.getCookies(); for (int i = 0; i < cookies.length; i++) { if ((cookies[i].getName()).equals("userName")) { out.println(cookies[i].getValue()); } }%> EL:
${cookie.userName.value} 打印一个上下文初始化参数:
DD:
<context-param> <param-name>mainEmail</param-name> <parma-value>cynhard85@126.com</param-value></context-param> 脚本:
<%= application.getInitParameter("mainEmail") %> EL:
${initParam.mainEmail} 编写有一个公共静态方法的Java类。
这只是一个普通的Java类。这个方法必须是公共的,而且是一个静态方法。放在WEB-INF/classes中
package foo;public class DiceRoller { public static int rollDice() { return (int)((Math.random() * 6) +1); }} 编写一个标记库描述文件(TLD)。
对于一个EL函数,TLD提供了定义函数的Java类与调用函数的JSP之间的一个映射。这样一来,函数名和具体的方法名可以使不同的。 它必须放在WEB-INF目录或者某个子目录中。确保JSP中taglib指令包含的uri属性与TLD中的<uri>元素匹配。
<?xml version="1.0" encoding="ISO-8859-1" ?><!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"><taglib> <tlib-version>1.2</tlib-version> <uri>DiceFunctions</uri> <function> <name>rollIt</name> <function-class>foo.DiceRoller</function-class> <function-signature>int rollDice()</function-signature> </function></taglib> 在JSP中放一个taglib指令。
taglib指令告诉容器,“我想使用这个TLD,另外,在JSP中,使用这个TLD中的一个函数时,我想用这个名字作为前缀……”
<%@ taglib prefix="mine" uri="DiceFunctions"%><html><body>${mine:rollIt()}</body></html> 使用EL调用函数。
${prefix:name()} 算术操作符
加法 +
减法 –
乘法 *
除法 /和p
取模 %和mod
逻辑操作符
与 &&和and
或 ||和or
非 !和not
关系操作符
等于 == 和eq
不等于 !=和ne
小于 <和lt
大于>和gt
小于等于 <=和le
大于等于 >=和ge
$)前缀:${expression} 表达式中第一个命名变量要么是一个隐式对象,要么是某个作用域(页面作用域、请求作用域、会话作用域或应用作用域)中的一个属性。 点号操作符允许你使用一个Map键或一个bean性质名来访问值,例如,使用${foo.bar}可以得到bar的值,在此bar是Map foo的Map键名,或者bar是bean foo的一个性质。放在点号操作符右边的东西必须遵循Java的标识符命名规则!(换句话说,必须以一个字母,下划线或美元符号开头,第一个字符后面可以有数字,但是不能邮其他字符。) 点号右边只能放合法的Java标识符。例如:${foo.1}就不允许。 []操作符比点号功能更强大,因为利用[]可以访问数组和List,可以把包含命名变量的表达式放在中括号里,而且可以做任意层次的嵌套,只要你受得了。 例如,如果musicList是一个ArrayList,可以用${musicList[0]}或${musicList["0"]}来访问列表中的第一个值。EL并不关心列表索引加不加引号。 如果中括号里的内容没有用引号引起来,容器就会进行计算。如果确实放在引号里,而且不是一个数组或List的索引,容器就会把它看作是性质或键的直接量名。 除了一个EL隐式对象(PageContext)外,其他EL隐式对象都是Map。从这些Map隐式对象可以得到任意4个作用域中的属性、请求参数值、首部值、cookie值和上下文初始化参数。非映射的隐式对象pageContext,它是PageContext对象的一个引用。 不要把隐式EL作用域对象(属性的Map)与属性所绑定的对象混为一谈。换句话说,不要把requestScope隐式对象与具体的JSP隐式对象request混淆。访问request对象只有一条路,就是通过pageContext隐式对象来访问。(不过,想从请求得到的一些东西通过其他EL隐式对象也可以得到,包括param/paramValues,header/headerValues和cookie) EL函数允许你调用一个普通Java类中的公共静态方法。函数名不一定与具体的方法名匹配!例如,${foo:rollIt()}并不意味着包含函数的类中肯定有一个名为rollIt()的方法。 使用一个TLD(标记库描述文件)将函数名(例如rollIt())映射到一个具体的静态方法。使用<function>元素声明一个函数,包括函数的<name>(rollIt()),完全限定类<function-class>,以及<function-signature>,其中包括返回类型以及方法名和参数表。 要在JSP中使用函数,必须使用taglib指令声明一个命名空间。在taglib指令中放一个prefix属性,告诉容器你要调用的函数在哪个TLD里能找到。例如:<%@ taglib prefix="mine" uri="/WEB-INF/foo.tld"%> <%@ include file="Header.jsp"%> <jsp:include>标准动作:<jsp:include page="Header.jsp"> include指令在转换时发生。<jsp:include>标准动作在运行时发生。 include指令在转换时插入“Header.jsp”的源代码。而<jsp:include>标准动作在运行时插入“Header.jsp”的相应。 采用include指令,被包含页面的源代码将成为有include指令的“外围”页面的一部分。 include指令和<jsp:include>是位置敏感的。 不要把开始和结束HTML和Body标记放在可重用部件中。设计和编写布局模板部件时(如页眉、导航条等),要假设他们会包含在其他页面中。 <jsp:param>定制包含的内容包含的页面:
<jsp:include page="Header.jsp"> <jsp:param name="subTitle" value="Hello"/></jsp:include> 被包含的页面:
<strong>${param.subTitle}</strong> <jsp:forward>标准动作<% if (request.getParameter("useName") == null) { %> <jsp:forward page="HandleIt.jsp"/><% } %> 如果发生了转发,转发前些的所有内容都不会出现。
<c:if test="${empty param.userName}"> <jsp:forward page="HandleIt.jsp"/></c:if> <jsp:useBean>标准动作会定义一个变量,它可能是一个现有bean属性的引用,如果还不存在这样一个bean,则会创建一个新的bean,这个变量就是新bean的引用。 <jsp:useBean>必须有一个“id”属性,这个属性声明了JSP中引用bean时所用的变量名。 如果<jsp:useBean>中没有“scope”属性,作用域默认为页面(page)作用域。 “class”属性是可选的,它声明了创建一个新bean时使用的类类型。这个类型必须是公共的、非抽象的,而且有一个公共的无参数构造函数。 如果在<jsp:useBean>中放了一个“type”属性,bean必须能强制转换成这种类型。 <jsp:useBean>标记可以有一个体,体中的内容会有条件地运行,只有当创建一个新的bean作为<jsp:useBean>的结果时,才会运行体中的内容[这说明,指定(或默认)作用域中不存在有该“id”的bean]。 <jsp:useBean>体的主要作用是使用<jsp:setProperty>设置新bean的性质。 <jsp:setProperty>必须有一个name属性(它要与<jsp:useBean>的“id”匹配),还要有一个“property”属性。“property”属性必须是一个具体的性质名,或者是通配符“*”。 如果没有包含“value”属性,只有当一个请求参数的名与性质名匹配时,容器才会设置性质值。如果“property”属性使用通配符(*),容器会为所有与某个请求参数名匹配的性质设置值。(其他性质不受影响) 如果请求参数名与性质名不同,但是你想把性质的值设置为请求参数值,可以在<jsp:setProperty>标记中使用“param”属性。 <jsp:setProperty>动作使用自省将“性质”匹配到一个JavaBean设置方法。如果性质是“*”,JSP将迭代处理所有请求参数来设置JavaBean性质。 性质值可以是String或基本类型,<jsp:setProperty>标准动作会自动完成转换。 <jsp:include>标准动作),可以利用可重用的组件创建页面。 include指令在转换时完成包含,只发生一次。所以如果包含的内容一经部署后不太可能改变,使用include指令就很合适。 include指令实际上只是复制被包含文件中的所有内容,把它粘贴到有include指令的页面中。容器把所有被包含文件的内容合并起来,只编译一个文件来生成servlet。在运行时,有include指令的页面将成为一个“大”页面,就像是你自己把所有源代码键入到一个文件中一样。 <jsp:incldue>标准动作在运行时把被包含页面的响应包含到原页面中。所以如果包含的内容在部署之后可能更新,include标准动作就很适用,此时不适用include指令。 这两种机制都能包含静态HTML页面,也可包含动态元素(例如,有EL表达式的JSP代码)。 include指令是唯一一个对位置敏感的指令,所包含的内容会插入到页面中include指令所在的位置。 include指令和include标准动作的属性命名不一致,指令使用“file”属性,而标准动作使用“page”属性。 在你的可重用组建中,一定要去掉开始和结束标记。否则,生成的输出会有嵌套的开始和结束标记,对于这种嵌套的开始和结束标记,并非所有浏览器都能处理。设计和构造可重用部件时,要知道它们会包含/插入到别的页面中。 可以在<jsp:include>的体中使用<jsp:param>标准动作设置(或替换)请求参数,用来定制所包含的文件。 <jsp:param>也可以用在<jsp:forward>标记的体中。 <jsp:param>只能放在<jsp:include>或<jsp:forward>标准动作中。 如果<jsp:param>中使用的参数名已经有一个值(作为请求参数),新值会覆盖原来的值。否则,就会向请求增加一个新的请求参数。 对被包含资源有一些限制:它不能改变相应状态码或设置首部。 <jsp:forward>标准动作可以把请求转发到同一个Web应用中的另一个资源(就像使用RequestDispatcher一样)。 发生转发时,会首先清空响应缓冲区!请求转发到的目标资源会先清空输出。所以转发前写至相应的所有内容都会清掉。 如果在转发之前先提交响应(例如,通过调用out.flush()),会把刷新输出的内容发送给客户,但是仅此而已。不会发生转发,原页面的余下部分不会得到处理。 <c:out><c:out>用于输出,可以通过escapeXml指定是否转义XML字符,默认值为true
<c:out value='${pageContext.currentTip}' escapeXml='true'/> <c:out>可以指定默认值:
<c:out value='${user}' default='guest'/> <c:forEach><c:forEach>用于循环:
<c:forEach var="movie" items="${movieList}"> <tr><td>${movie}</td></tr></c:forEach> <c:forEach>可以嵌套
<c:if><c:if test="${useType eq 'member'}"> <jsp:include page="inputComments.jsp"/></c:if> <c:choose>、<c:when>、<c:otherwise><c:choose> <c:when test="${user} == 'admin'"> ... </c:when> <c:when test="${user} == 'guest'"> ... </c:when> <c:other> ... </c:other></c:choose> <c:set>“var”用于设置属性。可以有体也可以没体。
<c:set var="userLevel" scope="session" value="Cowboy"/><c:set var="userLevel" scope="session"> Cowboy</c:set> scope是可选的。 “target”用于设置bean性质或Map值。可以有体也可以没体。
<c:set target="${PetMap}" property="dogName" value="Clover"/><c:set target="${person}" property="name"> ${foo.name}</c:set> <c:set>中不能同时有“var”和“target”属性。
${bean.notAProperty}也会抛出一个异常。 <c:remove>删除属性:
<c:remove var="userStatus" scope="request"> <c:import>和<c:param>包含内容
<c:import url="http://www.cynhard.com/index.html"/> 可以到包含应用之外的内容
<c:param>定制包含的内容 <c:import url="Header.jsp"> <c:param name="subTitle" value="We are good"/></c:import> <c:url>在需要时重写URL
<a href="<c:url value='inputComments.jsp'/>">Click here</a> URL需要编码时,可以在体内写参数
<c:url value="/inputComments.jsp" var="inputURL"> <c:param name="firstName" value="${first}"/> <c:param name="lastName" value="${last}"/></c:url> 错误页面(errorPage.jsp):
<%@ page isErrorPage="true"%> 处理页面:
<%@ page errorPage="errorPage.jsp" %> 通过异常配置错误页面
<error-page> <exception-type>java.lang.Throwable</exception-type> <location>/errorPage.jsp</location></error-page> 通过状态码配置错误页面
<error-page> <error-code>404</error-code> <location>/notFoundError.jsp</location></error-page> <%@ page isErrorPage="true"%>...${pageContext.exception} <c:catch>捕获异常捕获并吞掉异常
<c:catch> <% int x = 10/0; %></c:catch> 获取异常对象
<c:catch var="myException"> <% int x = 10/0; %></c:catch>${myException.message} TLD文件
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"><taglib> <tlibversion>1.0</tlibversion> <jspversion>1.1</jspversion> <shortname>Tag Library for Openfire 2.0</shortname> <uri>http://www.igniterealtime.org/</uri> <info>Tab Library for Openfire Admin Console</info> <tag> <description>some thing</description> <name>tabs</name> <tagclass>org.jivesoftware.admin.TabsTag</tagclass> <bodycontent>JSP</bodycontent> <info /> <attribute> <name>css</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>currentcss</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag></taglib> 解析
<tlibversion>1.0</tlibversion> 必要,开发人员方这个标记来声明标记库的版本。 <shortname>Tag Library for Openfire 2.0</shortname> 必要,主要由工具使用 <uri>http://www.igniterealtime.org/</uri> 必要,taglib指令中使用的唯一名 <description>some thing</description> 可选,但是有这个标记确实很有好处 <name>tabs</name> 必要!标记中的名字 <tagclass>org.jivesoftware.admin.TabsTag</tagclass> 必要!容器根据此找到实现tag功能的类。 <bodycontent>JSP</bodycontent> 必要!说明标记的体中应该是什么内容。 <attribute> <name>css</name> <required>false</required> <rtexprvalue>true</rtexprvalue></attribute> 标记的属性
<name>css</name><required>false</required> 指定属性名,required说明该属性是否必须 <rtexprvalue>true</rtexprvalue> 指定属性是否运行时表达式(也就是说,不必非得是String直接量) jsp
<%@ taglib prefix="mine" uri="http://www.igniterealtime.org/"%>...<mine:tabs css="${userName}"%> 解析
prefix="mine" 指定前缀 uri="http://www.igniterealtime.org/" 对应TLD中的uri:<uri>http://www.igniterealtime.org/</uri> mine:tabs mine对应前缀,tabs对应TLD中的tag名字:<name>tabs</name> css="${userName}" css对应TLD中的属性名:<name>css</name> 由于TLD中指定css属性可以为运行时表达式:<rtexprvalue>true</rtexprvalue>,因此这里可以放置表达式:${useName} // SimpleTagSupport实现了定制标记所要完成的工作。public class AdvisorTagHandler extends SimpleTagSupport { // JSP使用TLD中声明的名字调用标记时,容器会调用doTag()。 public void doTag() throws JspException, IOException { // ... } // 容器调用这个方法将值设置为标记属性的值。它使用JavaBean性质命名约定得出应该向setUser()方法发送一个“user”属性。 public void setUser(String user) { }} <rtexprvalue><rtexprvalue>或<rtexprvalue>的值为false,则不能在属性值中用表达式。 属性的值允许rtexprvalue时,可以使用3种表达式。
EL表达式
<mine:advice user="${userName}"/> 脚本表达式
<mine:advide user='<%= request.getAttribute("username") %>' /> <jsp:attribute>标准动作
<mine:advice> <jsp:attribute name="user">${userName}</jsp:attribute></mine:advice> <bodycontent>的取值
<bodycontent>empty</bodycontent> 不能有体 <bodycontent>scriptless</bodycontent> 不能有脚本元素(scriptlet、脚本表达式和声明),但是可以是模板文本和EL,还可以是定制和标准动作。 <bodycontent>tagdependent</bodycontent>标记体要看作是纯文本,所以不会计算EL,也不会触发标记/动作。 <bodycontent>JSP</bodycontent> 能放在JSP中的东西都能放在这个标记体中 没有体的标记,有3种调用方法
空标记:<mine:advice user="${userName}"/> 开始和结束标记之间没有内容的标记:<mine:advice user="${userName}"></mine:advice> 在开始和结束之间只有<jsp:attribute>标记
<mine:advice> <jsp:attribute name="user">${userName}</jsp:attribute></mine:advice> <uri><uri>与taglib指令中uri值之间的匹配。uri不一定是具体标记处理器的位置。