【JavaWeb】知识结构

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表达式

详情参考

To El2 Page

<!-- 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);
         }
      }
   }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40265247/article/details/105710142