1.Servlet
配置上手:链接
补充:①②③④⑤⑥⑦⑧⑨⑩√×
1.部署 tomcat 服务器需要先配置JDK环境①
2.在任意目录下catalina 参数
启动 Tomcat 服务器,需配置环境②
catalina的部分可选参数run: 在 catalina 同一个命令行窗口下启动服务器;start: 开启一个新窗口启动服务器;stop: 关闭服务器.
3.在 web.xml 文件中配置和映射这个 Servlet
<!-- 配置当前 WEB 应用的初始化参数 -->
<context-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
<!-- 配置和映射 Servlet -->
<servlet>
<!-- Servlet 注册的名字 -->
<servlet-name>helloServlet</servlet-name>
<!-- Servlet 的全类名 -->
<servlet-class>com.atguigu.javaweb.HelloServlet</servlet-class>
<!-- 可以指定 Servlet 被创建的时机 -->
<!-- 配置 Serlvet 的初始化参数。 且节点必须在 load-on-startup 节点的前面 -->
<init-param>
<!-- 参数名 -->
<param-name>user</param-name>
<!-- 参数值 -->
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>1230</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 需要和某一个 servlet 节点的 serlvet-name 子节点的文本节点一致 -->
<servlet-name>helloServlet</servlet-name>
<!-- 映射具体的访问路径: / 代表当前 WEB 应用的根目录. -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
PS:1)load-on-startup: 可以指定 Serlvet 被创建的时机. 若为负数, 则在第一次请求时被创建.若为 0 或正数, 则在当前 WEB 应用被
Serlvet 容器加载时创建实例, 且数组越小越早被创建.
2)关于 serlvet-mapping:
①同一个Servlet可以被映射到多个URL上,即多个 元素的子元素的设置值可以是同一个
Servlet的注册名。
②在Servlet映射到的URL中也可以使用 * 通配符,但是只能有两种固定的格式:一种格式是“.扩展名”,另一种格式是以正斜杠(/)开头并以"/"结尾。注意:既带 / 又带扩展名的不合法. 如 /*.do
4.Servlet 生命周期的方法: 以下方法都是由 Serlvet 容器负责调用.
1)构造器: 只被调用一次. 只有第一次请求 Servlet 时, 创建 Servlet 的实例. 调用构造器. 这说明 Serlvet 的单实例的!
2)init 方法: 只被调用一次. 在创建好实例后立即被调用. 用于初始化当前 Servlet.
3)service: 被多次调用. 每次请求都会调用 service 方法. 实际用于响应请求的.
4)destroy: 只被调用一次. 在当前 Servlet 所在的 WEB 应用被卸载前调用. 用于释放当前 Servlet 所占用的资源.
5.web.xml配置信息的获取
1)获取初始化参数: 对应
ServletConfig: 封装了 Serlvet 的配置信息, 并且可以获取 ServletContext 对象
String user = servletConfig.getInitParameter("user");
System.out.println("user: " + user);
Enumeration<String> names = servletConfig.getInitParameterNames();
while(names.hasMoreElements()){
String name = names.nextElement();
String value = servletConfig.getInitParameter(name);
System.out.println("^^" + name + ": " + value);
}
2)获取当前 WEB 应用的初始化参数:对应
ServletContext servletContext = servletConfig.getServletContext(); //该对象代表当前 WEB 应用,可以获取到当前web应用的各方面信息
servletContext获取参数形式同servletConfig
5.获取路径
//获取当前 WEB 应用的某一个文件在服务器上的绝对路径, 而不是部署前的路径(C:\Users\fei\Desktop\IdeaProjects\JavaWeb\webAPP\shuoFei.jpg)
String realPath = servletContext.getRealPath("/shuoFei.jpg"); //服务器上的绝对路径该路径可以通过配置Artifacts的输出路径更改
//C:\Users\fei\Desktop\IdeaProjects\JavaWeb\out\artifacts\webAPP_war_exploded
//获取当前 WEB 应用的名称:
String contextPath = servletContext.getContextPath(); //http://localhost:8080/webAPP 的/webAPP
6.获取当前 WEB 应用的某一个文件对应的输入流:getResourceAsStream(String path): path 的 / 为当前 WEB 应用的根目录.
InputStream is2 = servletContext.getResourceAsStream("/WEB-INF/classes/jdbc.properties");
7.GET 请求和 POST 请求:
1). 使用GET方式传递参数:
①. 在浏览器地址栏中输入某个URL地址或单击网页上的一个超链接时,浏览器发出的HTTP请求消息的请求方式为GET。
②. 使用GET请求方式给WEB服务器传递参数的格式: http://www.atguigu.com/counter.jsp?name=lc&password=123
③. 使用GET方式传送的数据量一般限制在 1KB 以下。
2). 使用 POST 方式传递参数:
①. POST 请求方式主要用于向 WEB 服务器端程序提交 FORM 表单中的数据: form 表单的 method 置为 POST
②. POST 方式将各个表单字段元素及其数据作为 HTTP 消息的实体内容发送给 WEB 服务器,传送的数据量要比使用GET方式传送的数据量大得多。
3). Servlet 的 service() 方法用于应答请求: 因为每次请求都会调用 service() 方法
①. 获取请求参数:
String getParameter(String name): 根据请求参数的名字, 返回参数值. 若请求参数有多个值(例如 checkbox), 该方法只能获取到第一个提交的值.
String[] getParameterValues(String name): 根据请求参数的名字, 返回请求参数对应的字符串数组.
Enumeration getParameterNames(): 返回参数名对应的 Enumeration 对象
Map getParameterMap(): 返回请求参数的键值对: key: 参数名, value: 参数值, String 数组类型.
②. 获取请求的 URI:String requestURI = httpServletRequest.getRequestURI(); // /day_29/loginServlet
③. 获取请求的 Serlvet 的映射路径:String servletPath = httpServletRequest.getServletPath(); // /loginServlet
④
PrintWriter out = response.getWriter(); 返回 PrintWriter 对象.
out.println(“helloworld…”); 调用该对象的 print() 方法, 将把 print() 中的参数直接打印到客户的浏览器上.
⑤设置响应的内容类型: response.setContentType(“application/msword”);
8.请求的转发和重定向:
本质区别: 请求的转发只发出了一次请求, 而重定向则发出了两次请求.
request.setAttribute("students", students); //由于request中设置了数据,只有同一个请求才不会失效。故使用请求转发
request.getRequestDispatcher("/students.jsp").forward(request,response); //请求转发
response.sendRedirect("http://www.atguigu.com"); //请求重定向
类型 | 地址栏 | request对象 | 请求url | / |
---|---|---|---|---|
请求转发 | 初次发出请求的地址 | Servlet中和转发的request是同一个 | 只能转发给当前 WEB 应用的的资源 | 代表当前 WEB 应用的根目录 如:localhost:8080/webAPP/ |
重定向 | 最后响应的那个地址 | Servlet中和重定向的request不是同一个 | 可以重定向到任何资源 | 代表当前 WEB 站点的根目录 如:localhost:8080/ |
PS:每次请求都会转向新的页面内容,只不过请求转发时url地址不变。
9.使用绝对路径:使用相对路径可能会有问题, 但使用绝对路径肯定没有问题.
绝对路径: 相对于当前 WEB 应用的路径. 在当前 WEB 应用的所有的路径前都添加 contextPath 即可. JSP中获取应用根路径: ${pageContext.request.contextPath}
若 / 需要服务器进行内部解析, 则代表的就是 WEB 应用的根目录. 若是交给浏览器了, 则 / 代表的就是站点的根目录
若 / 代表的是 WEB 应用的根目录, 就不需要加上 contextPath 了.
2.JSP
1.JSP 是简 Servlet 编写的一种技术, 它将 Java 代码和 HTML 语句混合在同一个文件中编写,只对网页中的要动态产生的内容采用 Java 代码来编写,而对固定不变的静态内容采用普通静态 HTML 页面的方式编写。
在 body 节点内的 <% %> 即可编写 Java 代码.
<body>
<%
Date date = new Date();
out.print(date);
%>
//等同于 <%= new Date() %> JSP表达式(expression)提供了将一个 java 变量或表达式的计算结果输出到客户端的简化方式,
</body>
①②③④⑤⑥⑦⑧⑨⑩√×
1)JSP 可以放置在 WEB 应用程序中的除了 WEB-INF 及其子目录外的其他任何目录中,JSP 页面的访问路径与普通 HTML 页面的访问路径形式也完全一样。
2)JSP的运行原理: JSP 本质上是一个 Servlet.
每个JSP 页面在第一次被访问时, JSP 引擎将它翻译成一个 Servlet 源程序, 接着再把这个 Servlet 源程序编译成 Servlet 的 class 类文件.然后再由WEB容器(Servlet引擎)像调用普通Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。
2.JSP 页面的隐含变量: 没有声明就可以使用的对象. JSP页面一共有 9 个隐含对象. request, response, pageContext, session,application, config, out, page 这 8 个隐含对象+ exception 隐含对象 application其实类似ServletContex的t对象
①其中pageContext, request, session, application 对象都有这些方法!这四个对象也称之为域对象.
pageContext: 属性的作用范围仅限于当前 JSP 页面。页面上下文, 是 PageContext 的一个对象. 可以从该对象中获取到其他 8 个隐含对象.
request: 属性的作用范围仅限于同一个请求.
session: 属性的作用范围限于一次会话: 浏览器打开直到关闭称之为一次会话(在此期间会话不失效)
application: 属性的作用范围限于当前 WEB 应用. 是范围最大的属性作用范围, 只要在一处设置属性, 在其他各处的 JSP 或 Servlet 中都可以获取到.
域对象的方法:注意JSP中的域对象代码写在 <% %>中
void setAttribute(String name, Object o): 设置属性
Object getAttribute(String name): 获取指定的属性
Enumeration getAttributeNames(): 获取所有的属性的名字组成的 Enumeration 对象
removeAttribute(String name): 移除指定的属性
②out: JspWriter 对象. 调用 out.println() 可以直接把字符串打印到浏览器上
③exception: 在声明了 page 指令的 isErrorPage=“true” 时, 才可以使用. <%@ page isErrorPage=“true” %>
3.JSP注释的格式:<%-- JSP 注释 --%> 区别: JSP 注释可以阻止 Java 代码的执行.
2.1 JSP指令
JSP指令(directive)是为JSP引擎而设计的, 它们并不直接产生任何可见输出, 而只是告诉引擎如何处理JSP页面中的其余部分。JSP2.0定义了page、include 和 taglib这三种指令
1.page 指令:
1)page指令用于定义JSP页面的各种属性, 无论page指令出现在JSP页面中的什么地方, 它作用的都是整个JSP页面, 为了保持程序的可读性和遵循良好的编程习惯, page指令最好是放在整个JSP页面的起始位置。
2). page 指令常用的属性:
①. import 属性: 指定当前 JSP 页面对应的 Servlet 需要导入的类. <%@page import=“java.text.DateFormat”%>
②. session 属性: 取值为 true 或 false, 指定当前页面的 session 隐藏变量是否可用, 也可以说访问当前页面时是否一定要生成 HttpSession
对象. <%@ page session=“false” %>
③. errorPage 和 isErrorPage:
(1) errorPage 指定若当前页面出现错误的实际响应页面时什么. 其中 / 表示当前 WEB 应用的根目录. <%@ page errorPage="/error.jsp" %>
(2) 在响应 error.jsp 时, JSP 引擎使用的请求转发的方式.
(3) isErrorPage 指定当前页面是否为错误处理页面, 可以说明当前页面是否可以使用 exception 隐藏变量. 需要注意的是: 若指定isErrorPage=“true”, 并使用 exception 的方法了, 一般不建议能够直接访问该页面.
(4) 如何使客户不能直接访问某一个页面呢 ? 对于 Tomcat 服务器而言, WEB-INF 下的文件是不能通过在浏览器中直接输入地址的方式来访问的. 但通过请求的转发是可以的!
(5) 还可以在 web.xml 文件中配置错误页面:
<error-page>
<!-- 指定出错的代码: 404 没有指定的资源, 500 内部错误. -->
<error-code>404</error-code>
<!-- 指定响应页面的位置 -->
<location>/WEB-INF/error.jsp</location>
</error-page>
<error-page>
<!-- 指定异常的类型 -->
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/WEB-INF/error.jsp</location>
</error-page>
④. contentType: 指定当前 JSP 页面的响应类型. 实际调用的是 response.setContentType(“text/html; charset=UTF-8”);
通常情况下, 对于 JSP 页面而言其取值均为 text/html; charset=UTF-8. charset 指定返回的页面的字符编码是什么. 通常取值为 UTF-8
⑤. pageEncoding: 指定当前 JSP 页面的字符编码. 通常情况下该值和 contentType 中的 charset 一致.
⑥. isELIgnored: 指定当前 JSP 页面是否可以使用 EL 表达式. 通常取值为 false.
2.include 指令: <%@ include file=“b.jsp” %>
1). include 指令用于通知 JSP 引擎在翻译当前 JSP 页面时将其他文件中的内容合并进当前 JSP 页面转换成的 Servlet 源文件中,
这种在源文件级别进行引入的方式称之为静态引入, 当前JSP页面与静态引入的页面紧密结合为一个Servlet。
2). file属性的设置值必须使用相对路径
3). 如果以 / 开头,表示相对于当前WEB应用程序的根目录(注意不是站点根目录),否则,表示相对于当前文件。
3.jsp:incluce 标签:
1). <jsp:include page=“b.jsp”></jsp:include>
2). 动态引入: 并不是像 include 指令生成一个 Servlet 源文件, 而是生成两个 Servlet 源文件, 然后通过一个方法的方式把目标页面包含进来.
4.jsp:forward:
1). <jsp:forward page="/include/b.jsp"></jsp:forward> 相当于.
<% request.getRequestDispatcher("/include/b.jsp").forward(request, response); %>
2). 但使用 jsp:forward 可以使用 jsp:param 子标签向 b.jsp 传入一些参数. 同样 jsp:include 也可以使用 jsp:param 子标签.
<jsp:forward page="/include/b.jsp">
<jsp:param value="abcd" name="username"/>
</jsp:forward>
或者
<jsp:include page="/include/b.jsp">
<jsp:param value="abcd" name="username"/>
</jsp:include>
在 b.jsp 页面可以通过 request.getParameter(“username”) 获取到传入的请求参数.
3.关于中文乱码:
1). 在 JSP 页面上输入中文, 请求页面后不出现乱码: 保证 contentType=“text/html; charset=UTF-8”, pageEncoding=“UTF-8” charset 和 pageEncoding 的编码一致, 且都支持中文. 通常建议取值为UTF-8,还需保证浏览器的显示的字符编码也和请求的 JSP 页面的编码一致.
2). 获取中文参数值: 默认参数在传输过程中使用的编码为 ISO-8859-1
①. 对于 POST 请求: 只要在获取请求信息之前(在调用 request.getParameter 或者是 request.getReader 等), 调用 request.setCharacterEncoding(“UTF-8”) 即可.
②. 对于 GET 请求: 前面的方式对于 GET 无效. 可以通过修改 Tomcat 的 server.xml 文件的方式.
参照文档
的 useBodyEncodingForURI 属性. 为 Connector 节点添加 useBodyEncodingForURI=“true” 属性即可.
4.表单重复提交
1). 重复提交的情况:
①. 在表单提交到一个 Servlet, 而 Servlet 又通过请求转发的方式响应一个 JSP(HTML) 页面,
此时地址栏还保留着 Serlvet 的那个路径, 在响应页面点击 “刷新”
②. 在响应页面没有到达时重复点击 “提交按钮”.
③. 点击 “返回”, 再点击 “提交”
2). 不是重复提交的情况: 点击 “返回”, “刷新” 原表单页面, 再 “提交”。
3). 如何避免表单的重复提交: 在表单中做一个标记, 提交到 Servlet 时, 检查标记是否存在且是否和预定义的标记一致, 若一致, 则受理请求,
并销毁标记, 若不一致或没有标记, 则直接响应提示信息: “重复提交” 注:标记放Session中
在原表单页面, 生成一个随机值 token
在原表单页面, 把 token 值放入 session 属性中
在原表单页面, 把 token 值放入到 隐藏域 中.
在目标的 Servlet 中: 获取 session 和 隐藏域 中的 token 值
比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 token 属性清除
若不一致, 则直接响应提示页面: “重复提交”
3.Cookie和Session
<%
//1. 获取 Cookie
Cookie [] cookies = request.getCookies();
if(cookies != null && cookies.length > 0){
for(Cookie cookie: cookies){
//2. 获取 Cookie 的 name 和 value
out.print(cookie.getName() + ": " + cookie.getValue());
out.print("<br>");
}
}else{
out.print("没有一个 Cookie, 正在创建并返回");
//1. 创建一个 Cookie 对象
Cookie cookie = new Cookie("name", "atguigu");
//设置 Cookie 的作用范围: //Cookie 的 作用范围: 可以作用当前目录和当前目录的子目录. 但不能作用于当前目录的上一级目录.
//可以通过 setPath 方法来设置 Cookie 的作用范围, 其中 / 代表站点的根目录.
cookie.setPath(request.getContextPath()); // 设置为web应用的根目录
//setMaxAge: 设置 Cookie 的最大时效, 以秒为单位, 若为 0 , 表示立即删除该 Cookie
//若为负数, 表示不存储该 Cookie, 若为正数, 表示该 Cookie 的存储时间.
cookie.setMaxAge(30);
//2. 调用 response 的一个方法把 Cookie 传给客户端.
response.addCookie(cookie);
}
%>
4.EL表达式
<!-- el2.jsp文件 -->
<!-- 1. EL 的 . 或 [] 运算符 -->
age: ${
sessionScope.customer.age} <!-- customer是Session的属性名,值为Customer对象 -->
<!-- sessionScope.customer根据属性名获取设置的数据即Customer对象 后 .age 即调用getAge方法-->
<%--
Customer customer = (Customer)session.getAttribute("customer"); <!-- 原本方式 -->
out.print(customer.getAge());
--%>
<!-- 如果域对象中的属性名带有特殊字符, 则使用 [] 运算符会很方便. -->
<%
Customer customer = new Customer();
customer.setName("ATGUIGU");
session.setAttribute("com.atguigu.customer", customer);
%>
name: ${
sessionScope["com.atguigu.customer"].name }
<!-- 2. EL 中的隐含对象 -->
<%
Customer cust2 = new Customer();
cust2.setAge(28);
request.setAttribute("customer", cust2);
%>
age: ${
customer.age } <!-- 默认从小到大的范围pageScope, requestScope, sessionScope, applicationScope寻找customer属性 -->
<!-- 3. EL 可以进行自动的类型转换 -->
score: ${
param.score + 11}
<br>
score: <%= request.getParameter("score") + 11 %> <!-- 此处是字符串连接 -->
<br>
<!-- 4. 隐含对象之与范围相关的: pageScope, requestScope, sessionScope, applicationScope -->
<%
application.setAttribute("time", new Date());
%>
time: ${
applicationScope.time.time }
<%--
<%= application.getAttribute("time") %>
--%>
<br>
<!-- 5. 与(地址栏)输入有关的隐含对象: param, paramValues -->
score: ${
param.score }
<%--
<%= request.getParameter("score") %>
--%>
<br>
names: ${
paramValues.name[0].class.name }
<%--
<%= request.getParameterValues("name")[0].getClass().getName()%>
--%>
<br>
<!-- 6. 其他隐含对象: pageContext 等(cookie, header, initParam 只需了解.) -->
pageContext: pageContext 即为 PageContext 类型, 但只能读取属性就可以一直的 . 下去。
<br>
contextPath: ${
pageContext.request.contextPath }
<br>
sessionId: ${
pageContext.session.id }
<br>
sessionAttributeNames: ${
pageContext.session.attributeNames }
<br>
initParam: ${
initParam.initName } <!-- web.xml中配置的 -->
<br>
Accept-Language: ${
header["Accept-Language"] }
<br>
JSESSIONID: ${
cookie.JSESSIONID.name } -- ${
cookie.JSESSIONID.value }
<!-- 7. EL 的运算符 -->
${
param.score > 60 ? "及格" : "不及格" }
<br>
<%
List<String> names = new ArrayList<String>();
names.add("abc");
request.setAttribute("names", names);
%>
<!-- empty 可以作用于一个集合, 若该集合不存在或集合中没有元素, 其结果都为 true -->
names is empty: ${
empty requestScope.names }
5.自定义标签
方式一:
①. 创建一个标签处理器类: 实现 SimpleTag 接口.
②. 在 WEB-INF 文件夹下新建一个 .tld(标签库描述文件) 为扩展名的 xml配置文件.
③. 在 tld 文件中描述自定义的标签:
<?xml version="1.0" encoding="ISO-8859-1"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- 描述 TLD 文件 -->
<description>MyTag 1.o core library</description>
<display-name>MyTag core</display-name>
<tlib-version>1.0</tlib-version>
<short-name>atguigu</short-name> <!-- 建议在 JSP 页面上使用的标签的前缀 -->
<uri>http://www.atguigu.com/mytag/core</uri> <!-- 作为 tld 文件的 id, 用来唯一标识当前的 TLD 文件, 多个 tld 文件的 URI 不能重复. 通过 JSP 页面的 taglib 标签的 uri 属性来引用. -->
<tag> <!-- 描述自定义的 HelloSimpleTag 标签 -->
<name>hello</name> <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
<tag-class>com.atguigu.javaweb.tag.HelloSimpleTag</tag-class> <!-- 标签所在的全类名 -->
<body-content>empty</body-content> <!-- 标签体的类型 -->
<!-- 描述当前标签的属性 -->
<attribute>
<name>value</name> <!-- 属性名, 需和标签处理器类的 setter 方法定义的属性相同 -->
<required>true</required> <!-- 该属性是否被必须 -->
<rtexprvalue>true</rtexprvalue> <!--runtime expression value 当前属性是否可以接受运行时表达式的动态值 -->
</attribute>
<attribute>
<name>count</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
/taglib>
④. 在 JSP 页面上使用自定义标签:
使用 taglib 指令导入标签库描述文件: <%@taglib uri=“http://www.atguigu.com/mytag/core” prefix=“atguigu” %>
使用自定义的标签: atguigu:hello/
2). setJspContext: 一定会被 JSP 引擎所调用, 先于 doTag, 把代表 JSP 引擎的 pageContext 传给标签处理器类.
private PageContext pageContext;
@Override
public void setJspContext(JspContext arg0) {
System.out.println(arg0 instanceof PageContext);
this.pageContext = (PageContext) arg0;
}
3). 带属性的自定义标签:
①. 先在标签处理器类中定义 setter 方法. 建议把所有的属性类型都设置为 String 类型.
private String value;
private String count;
public void setValue(String value) {
this.value = value;
}
public void setCount(String count) {
this.count = count;
}
②. 在 tld 描述文件中来描述属性:
③. 在页面中使用属性, 属性名同 tld 文件中定义的名字. <atguigu:hello value="${param.name }" count=“10”/>
方式二(推荐
4). 通常情况下开发简单标签直接继承 SimpleTagSupport 就可以了. 可以直接调用其对应的 getter 方法得到对应的 API
继承该类后:PageContext pageContext = (PageContext) getJspContext();
//API实现源码
public class SimpleTagSupport implements SimpleTag{
public void doTag()
throws JspException, IOException{
}
private JspTag parentTag;
public void setParent( JspTag parent ) {
this.parentTag = parent;
}
public JspTag getParent() {
return this.parentTag;
}
private JspContext jspContext;
public void setJspContext( JspContext pc ) {
this.jspContext = pc;
}
protected JspContext getJspContext() {
return this.jspContext;
}
private JspFragment jspBody;
public void setJspBody( JspFragment jspBody ) {
this.jspBody = jspBody;
}
protected JspFragment getJspBody() {
return this.jspBody;
}
}
5.1.带标签体的自定义标签:
1). 若一个标签有标签体: atguigu:testJspFragmentabcdefg</atguigu:testJspFragment>
在自定义标签的标签处理器中使用 JspFragment 对象封装标签体信息.
2). 若配置了标签含有标签体, 则 JSP 引擎会调用 setJspBody() 方法把 JspFragment 传递给标签处理器类。在 SimpleTagSupport 中还定义了一个 getJspBody() 方法, 用于返回 JspFragment 对象.
3). JspFragment 的 invoke(Writer) 方法: 把标签体内容从 Writer 中输出, 若为 null, 则等同于 invoke(getJspContext().getOut()), 即直接把标签体内容输出到页面上。有时, 可以 借助于 StringWriter, 可以在标签处理器类中先得到标签体的内容:
4). 在 tld 文件中, 使用 body-content 节点来描述标签体的类型:
: 指定标签体的类型, 大部分情况下, 取值为 scriptless。可能取值有 3 种:
empty: 没有标签体
scriptless: 标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素
tagdependent: 表示标签体交由标签本身去解析处理。
若指定 tagdependent,在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器
5). 定义一个自定义标签: <atguigu:printUpper time=“10”>abcdefg 把标签体内容转换为大写, 并输出 time 次到
浏览器上.
//标签处理类
public class PrintUpperTag extends SimpleTagSupport {
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);//利用 StringWriter 得到标签体的内容.
//2. 把标签体的内容都变为大写
String content = sw.toString().toUpperCase();
//3. 得到 out 隐含变量 //4. 循环输出
int count = 1;
try {
count = Integer.parseInt(time);
} catch (Exception e) {
}
for(int i = 0; i < count; i++){
getJspContext().getOut().print(i + 1 + "." + content + "<br>");
}
}
}
5.2.开发有父标签的标签:
1). 父标签无法获取子标签的引用, 父标签仅把子标签作为标签体来使用.
2). 子标签可以通过 getParent() 方法来获取父标签的引用(需继承 SimpleTagSupport 或自实现 SimpleTag 接口的该方法):
若子标签的确有父标签, JSP 引擎会把代表父标签的引用通过 setParent(JspTag parent) 赋给标签处理器
3). 注意: 父标签的类型是 JspTag 类型. 该接口是一个空接口, 但是来统一 SimpleTag 和 Tag 的. 实际使用需要进行类型的强制转换.
4). 在 tld 配置文件中, 无需为父标签有额外的配置. 但, 子标签是是以标签体的形式存在的, 所以父标签的
需设置为 scriptless
<atguigu:choose> <%--对应自定义的ChooseTag标签处理类doTag(){
getJspBody().invoke(null);}--%>
<atguigu:when test="${param.age > 24}">^大学毕业</atguigu:when> <%--对应自定义的WhenTag标签处理类--%>
<atguigu:when test="${param.age > 20}">^高中毕业</atguigu:when>
<atguigu:otherwise>^高中以下...</atguigu:otherwise> <%--对应自定义的OtherwiseTag标签处理类--%>
</atguigu:choose>
public class WhenTag extends SimpleTagSupport{
private boolean test;
public void setTest(boolean test) {
this.test = test;
}
@Override
public void doTag() throws JspException, IOException {
if(test){
//1. 得到父标签的引用
JspTag parent = getParent();
//2. 获取父标签的自定义的标志flag属性(不是标签属性
ChooseTag chooseTag = (ChooseTag)parent;
boolean flag = chooseTag.getFlag();
if(flag){
getJspBody().invoke(null);
chooseTag.setFlag(false);
}
}
}
}