什么是 JSP
JSP 全名为 Java Server Pages,java 服务器页面。JSP 是一种基于文本的程序,其特点就是 HTML 和 Java 代码独特存在!
为什么须要 JSP
JSP 是为了简化 Servlet 的工作呈现的替代品,Servlet 输入 HTML 十分艰难,JSP 就是代替 Servlet 输入 HTML 的。
简略应用一下 JSP
- 在 idea 下生成一个 JSP,咱们来看一下 JSP 长什么样子
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 简略应用 JSP</title>
</head>
<body>
</body>
</html>
- 看起来就像一个 HTML 页面,后面也说了:JSP 的特点就是 HTML 和 Java 代码独特存在
- 咱们向浏览器输入一句 HelloWorld,至于 <%%> 这个货色,我先不解释!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 简略应用 JSP</title>
</head>
<body>
<%
String s = "HelloWorld";
out.println(s);
%>
</body>
</html>
JSP 的工作原理
- 在 Tomcat 博客中我提到过:Tomcat 拜访任何的资源都是在拜访 Servlet!,当然了,JSP 也不例外!JSP 自身就是一种 Servlet。为什么我说 JSP 自身就是一种 Servlet 呢?其实 JSP 在第一次被拜访的时候会被编译为 HttpJspPage 类(该类是 HttpServlet 的一个子类)
- 方才我简略应用了一下 JSP,它被编译成了这么一个 Servlet:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.Date;
public final class _1_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
private static java.util.List<String> _jspx_dependants;
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.List<String> getDependants() {return _jspx_dependants;}
public void _jspInit() {_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
public void _jspDestroy() {}
public void _jspService(final HttpServletRequest request, final HttpServletResponse response)
throws java.io.IOException, ServletException {
final PageContext pageContext;
HttpSession session = null;
final ServletContext application;
final ServletConfig config;
JspWriter out = null;
final Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<title> 简略应用 JSP</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
String s = "HelloWorda";
out.println(s);
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
} catch (Throwable t) {if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
- 编译过程是这样子的:浏览器第一次申请 1.jsp 时,Tomcat 会将 1.jsp 转化成 1_jsp.java 这么一个类,并将该文件编译成 class 文件。编译结束后再运行 class 文件来响应浏览器的申请。
- 当前拜访 1.jsp 就不再从新编译 jsp 文件了,间接调用 class 文件来响应浏览器。当然了,如果 Tomcat 检测到 JSP 页面改变了的话,会从新编译的。
- 既然 JSP 是一个 Servlet,那 JSP 页面中的 HTML 排版标签是怎么样被发送到浏览器的 ?咱们来看下下面 1_jsp.java 的源码就晓得了。原来就是用 write() 进来的罢了。说到底,JSP 就是封装了 Servlet 的 java 程序罢了。
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<title> 简略应用 JSP</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
- 有人可能也会问:JSP 页面的代码服务器是怎么执行的 ?再看回 1_jsp.java 文件,java 代码就间接在类中的 service() 中。
String s = "HelloWorda";
out.println(s);
- JSP 比 Servlet 更不便更简略的一个重要起因就是:内置了 9 个对象!内置对象有:out、session、response、request、config、page、application、pageContext、exception,这几个内置对象不在这里讲。当初先晓得一下即可!
-
- *
JSP 生命周期
JSP 也是 Servlet,运行时只有一个实例,JSP 初始化和销毁时也会调用 Servlet 的 init()和 destroy()办法。另外,JSP 还有本人初始化和销毁的办法
public void _jspInit() {_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
public void _jspDestroy() {}
JSP 的语法
JSP 代码能够分为两局部:
- 模板数据:就是 HTML 代码
- 元素:JSP 页面中的 java 代码、JSP 指令、JSP 标签
JSP 脚本
- JSP 的脚本就是 JSP 页面中的 java 代码,也叫做 scriptlet。JSP 的脚本必须应用 <%%> 括起来,不然会被当成是模板数据的!
-
JSP 脚本有三种形式:
- <%%>【定义局部变量,编写语句】
- <%!%>【定义类或办法,然而没人这样用!】
- <%=%>(也称之为 表达式输入)【输入各种类型的变量,int、double、String、Object 等】
- 如果过多地应用 <%%> 会导致代码凌乱,JSP 还提供了一种 scriptlet 标签,应用此标签和 <%%> 有雷同的性能,只不过它更好看了一些
<jsp:scriptlet>
String s = "HelloWorld";
out.println(s);
</jsp:scriptlet>
JSP 正文
<%-- 这是 JSP 正文 --%>
<%--%>
// 这是 java 的当行正文
//
/* 这是 java 的多行正文 */
/**/
JSP 指令
JSP 指令用来申明 JSP 页面的相干属性,例如编码方式、文档类型等等
JSP 指令的语法:
<%@指令 属性名 ="值" %>
page 指令
- 我在 idea 生成的 JSP 页面就有 page 指令了。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
- page 指令常见属性:
- language=”java”
- extends=”package.class”
- import=”{package.class | package.*}, …”
- session=”true | false”
- buffer=”none | 8kb | sizekb”
- autoFlush=”true | false”
- isThreadSafe=”true | false”
- info=”text”
- errorPage=”relative_url”
- isErrorPage=”true | false”
- contentType=”mimeType ;charset=characterSet ” | “text/html ; charset=ISO-8859-1”
- pageEncoding=”characterSet | ISO-8859-1″
- isELIgnored=”true | false”
- 个别地,在 eclipse 或 idea 这些高级开发工具上开发,咱们只须要在 page 指令中指定 contentType=”text/html;charset=UTF-8″,就不会呈现中文乱码问题!
- 当然了 contentType 不仅仅能够指定以 text/html 的形式显示,还能够应用其余的模式显示进去。在 conf/web.xml 文件中能够查问进去
- 比方,我以 doc 模式显示 jsp 的数据
<%@ page contentType="application/msword;charset=UTF-8" language="java" %>
<html>
<head>
<title> 简略应用 JSP</title>
</head>
<body>
1111
</body>
</html>
- 成果是这样子的:
- 咱们上网的时候,如果咱们操作不当,或者服务器出错了,页面都是会呈现敌对提醒的 !这个也能通过 page 指令来实现 跳转到敌对提醒页面上!
- page 指令 errorPage= 和 isErrorPage 这两个属性,上面咱们来看一下怎么应用!
- 1.jsp 呈现了谬误,通过 page 指令的 errorPage 属性跳转到 error.jsp 页面 上
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
<html>
<head>
<title> 该页面出错了!</title>
</head>
<body>
<%-- 模仿页面出错了!!!--%>
<%
int result = 2 / 0;
%>
你好呀
</body>
</html>
- error.jsp 页面要通过 page 指令的 isErrorPage 属性设置页面就是谬误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title> 敌对提醒页面 </title>
</head>
<body>
服务器正忙着呢!</body>
</html>
- 上面是成果:
- 当然了,仔细的敌人能够发现地址栏是没有变动的,所以属于是服务器跳转。以上的做法是单个页面设置的,如果我会有很多谬误(JSP 多的状况下,谬误就会多),单个设置太麻烦了!
- 咱们能够在 web.xml 文件中全局设置谬误页,只有产生了 404 谬误或者空指针异样的谬误都会跳转到 error.jsp 页面上
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.NullPointerException</exception-type>
<location>/error.jsp</location>
</error-page>
- 轻易输个资源进行,会产生发 404 谬误的,跳转到谬误页面。上面是成果:
include 指令
- 在解说 request 对象的时候,咱们已经应用过 request.getRequestDispatcher(String url).include(request,response)来对页头和页尾面进行蕴含
- inclue 指令也是做这样的事件,咱们来试验一下吧!
- 这是页头
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 页头 </title>
</head>
<body>
我是页头
<br>
<br>
<br>
</body>
</html>
- 这是页尾
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 页尾 </title>
</head>
<body>
我是页尾
</body>
</html>
- 在 1.jsp 中把页头和页尾蕴含进来
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 蕴含页头和页尾进来 </title>
</head>
<body>
<%@include file="head.jsp" %>
<%@include file="foot.jsp" %>
</body>
</html>
- 拜访 1.jsp
- include 指令是动态蕴含 。动态蕴含的意思就是: 把文件的代码内容都蕴含进来,再编译!,看一下 jsp 的源代码就晓得了!
- jsp 还提供另一种蕴含文件的形式:JSP 行为 — 动静蕴含。jsp 行为在上面会讲到!
-
- *
taglib 指令
- JSP 反对标签技术,要应用标签技术就先得申明标签库和标签前缀。taglib 指令就是用来指明 JSP 页面内应用标签库技术。
- 这里就不具体阐明了,等到学习 JSP 标签的时候再应用吧!当初记住有这个指令即可。
-
- *
JSP 行为
JSP 行为(JSP Actions)是一组 JSP 内置的标签,只书写大量的标记代码就可能应用 JSP 提供丰盛的性能,JSP 行为是对罕用的 JSP 性能的形象和封装。
为什么我不把它间接称为 JSP 标签呢?我把这些 JSP 内置的标签称之为 JSP 行为,可能和 JSTL 标签辨别开来。当然了,你也能够把它称之为 JSP 标签,你不要搞混就行了。我集体喜爱把这些 JSP 内置标签称之为 JSP 行为。
include 行为
- 下面曾经提及到了,include 指令是动态蕴含,include 行为是动静蕴含 。 其实 include 行为就是封装了 request.getRequestDispatcher(String url).include(request,response)
- include 行为语法是这个样子的
<jsp:include page=""/>
- 咱们先来应用一下把,在 1.jsp 页面中也将页头和页尾蕴含进来。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 蕴含页头和页尾进来 </title>
</head>
<body>
<jsp:include page="head.jsp"/>
<jsp:include page="foot.jsp"/>
</body>
</html>
- 拜访 1.jsp 页面看一下成果:
- 应用 jsp 行为来蕴含文件,jsp 源文件是这样子的:
- jsp 行为蕴含文件就是 先编译被蕴含的页面,再将页面的后果写入到蕴含的页面中(1.jsp)
- 当然了,当初有动态蕴含和动静蕴含,应用哪一个更好呢?答案是:动静蕴含。
- 动静蕴含能够 向被蕴含的页面传递参数(用途不大),并且是 别离解决蕴含页面的(将被蕴含页面编译后得出的后果再写进蕴含页面)【如果有雷同名称的参数,应用动态蕴含就会报错!】!
- 模仿一下场景吧,当初我的头页面有个名为 s 的字符串变量
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 页头 </title>
</head>
<body>
<%
String s = "zhongfucheng";
%>
我是页头呀
<br>
<br>
<br>
</body>
</html>
- 我的页尾也有个名为 s 的字符串变量
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 页尾 </title>
</head>
<body>
<%
String s = "zhongfucheng";
%>
我是页尾呀
</body>
</html>
- 当初我应用动态蕴含看看会产生什么,出现异常了。
- 出现异常的起因很简略,就是 同一个文件中有两个雷同的变量 s
- 应用动静蕴含就能够防止这种状况
param 行为
- 当应用 <jsp:include> 和 <jsp:forward> 行为引入或将申请转发给其它资源时,能够应用 <jsp:param> 行为向这个资源传递参数。
forward 行为
- 在解说 request 对象的时候,咱们应用 request.getRequestDispatcher(String url).forward(request,response)进行跳转。其实 forward 行为就是对其封装!
- 咱们来看一下 forward 的语法:
<jsp:forward page=""/>
- 好的,咱们来应用一下吧。拜访 1.jsp 页面就跳转到 head.jsp 页面中
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 拜访 1.jsp 就跳转到 head.jsp</title>
</head>
<body>
<jsp:forward page="head.jsp"/>
</body>
</html>
- 看一下成果
- 如果我要传递参数,就要在 forward 行为嵌套 param 行为
- 在跳转到 head.jsp 时传入参数 username 值为 zhongfucheng
<jsp:forward page="head.jsp">
<jsp:param name="username" value="zhongfucheng"/>
</jsp:forward>
- 在 head.jsp 页面中获取到传递过去的参数
<%
String ss = request.getParameter("username");
%>
获取到的参数是:<%=ss%>
- 成果如下图所示
directive 行为
-
directive 的中文意思就是指令 。 该行为就是代替指令 <%@%> 的语法的
- <jsp:directive.include file=””/> 相当于 <%@include file=”” %>
- <jsp:directive.page/> 相当于 <%@page %>
- <jsp:directive.taglib/> 相当于 <%@taglib %>
- 咱们来试一下能不能用的
<jsp:directive.include file="head.jsp"></jsp:directive.include>
<jsp:directive.include file="foot.jsp"></jsp:directive.include>
- 看下成果,失常能够蕴含页面:
- 应用该指令能够让 JSP 页面更加好看!
- 应用 scriptlet 行为
<jsp:scriptlet>
代替 <%%> 是同样一个情理
javaBean 行为
-
JSP 还提供了操作 javaBean 对象的行为 , 在这里就不具体阐明了,前面会讲到的!当初记住 JSP 提供了 javaBean 行为来操作简略类即可!
<jsp:useBean id=""/>
<jsp:setProperty name=""property=""/>
<jsp:getProperty name=""property=""/>
- 什么是 JSP 内置对象
==========
JSP 引擎在调用 JSP 对应的 jspServlet 时,会传递或创立 9 个与 web 开发相干的对象供 jspServlet 应用 。JSP 技术的设计者为便于开发人员在编写 JSP 页面时取得这些 web 对象的援用,特意定义了 9 个相应的变量, 开发人员在 JSP 页面中通过这些变量就能够疾速取得这 9 大对象的援用
仔细的敌人会发现,咱们没有在 JSP 页面上定义过 out 对象,却能够间接应用!其实 out 对象就是 JSP 内置对象之一。
九个内置对象:
- pageContext
- page
- config
- request
- response
- session
- application
- exception
- out
-
- *
out 对象
out 对象的 API
- int getBufferSize()【失去缓存大小】
- int getRemaining()【失去未应用缓存的大小】
- boolean isAutoFlush()
- void println()
- void flush()
- void close()
- void clearBuffer()
- void clear()
- out 对象用于向浏览器输入数据,与之对应的是 Servlet 的 PrintWriter 对象。然而这个 out 对象的类型并不是 PrintWriter,是 JspWriter
- 咱们能够简略了解为:JspWriter 就是带缓存的 PrintWrieter。
- out 对象的原理如下:
-
只有向 out 对象中写入了内容,且 满足如下任何一个条件时,out 对象才去调用 ServletResponse.getWriter 办法 ,并 通过该办法返回的 PrintWriter 对象将 out 对象的缓冲区中的内容真正写入到 Servlet 引擎提供的缓冲区中:
- 设置 page 指令的 buffer 属性敞开了 out 对象的缓存性能
- out 对象的缓冲区已满
- 整个 JSP 页面完结
- 个别咱们在 JSP 页面输入都是用表达式(<%=%>),所以 out 对象用得并不是很多!
-
- *
request
- 内置对象 request 其实就是 HttpServletRequest,在 Servlet 解说的时候曾经具体阐明了,没什么好说的
response
- 内置对象 response 其实就是 HttpServletResponse,在 Servlet 解说的时候曾经具体阐明了,没什么好说的
config
- 内置对象 config 其实就是 ServletConfig,在 Servlet 解说的时候曾经具体阐明了,没什么好说的
session
- 内置对象 session 其实就是 HttpSession。,在 Servlet 解说的时候曾经具体阐明了,没什么好说的
留神:在 page 指令配置如下信息,session 将不可应用
<%@page session="false" %>
application
- 内置对象 application 其实就是 ServletContext 对象,在 Servlet 解说的时候曾经具体阐明了,没什么好说的
page
- 内置对象 page 是 HttpJasPage 对象,其实 page 对象代表的就是以后 JSP 页面,是以后 JSP 编译后的 Servlet 类的对象。也就是说:page 对象相当于一般 java 类的 this
exception
- 内置对象 exception 是 java.lang.Exception 类的对象,exception 封装了 JSP 页面抛出的异样信息。exception 常常被用来处理错误页面
- 后面咱们已 经讲过了怎么设置谬误页面了,上面咱们就来简略应用一下 exception 对象吧
- 1.jsp 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
<html>
<head>
<title></title>
</head>
<body>
<%-- 模仿空指针异样的谬误 --%>
<%
String sss = null;
sss.length();
%>
</body>
</html>
- error.jsp 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title> 谬误页面 </title>
</head>
<body>
<%
out.println("程序抛出了异样:" + exception);
%>
</body>
</html>
- 成果:
pageContext
pageContext 是内置对象中最重要的一个对象,它代表着 JSP 页面编译后的内容(也就是 JSP 页面的运行环境)!
pageContext 获取 8 个内置对象
- 既然它代表了 JSP 页面编译后的内容,天经地义的:它封装了对其余 8 大内置对象的援用!,也就是说,通过 pageContext 能够获取到其余的 8 个内置对象!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 获取八大内置对象 </title>
</head>
<body>
<%
System.out.println(pageContext.getSession());
System.out.println(pageContext.getRequest());
System.out.println(pageContext.getResponse());
System.out.println(pageContext.getException());
System.out.println(pageContext.getPage());
System.out.println(pageContext.getServletConfig());
System.out.println(pageContext.getServletContext());
System.out.println(pageContext.getOut());
%>
</body>
</html>
- 看下成果:
pageContext 作为域对象
-
相似于 request,session,ServletContext 作为域对象而言 都有以下三个办法:
- setAttribute(String name,Objcet o)
- getAttribute(String name)
- removeAttribute(String name)
- 当然了,pageContext 也不例外,pageContext 也有这三个办法!
- pageContext 实质上代表的是以后 JSP 页面编译后的内容,作为域对象而言,它就代表着以后 JSP 页面(也就是 page)!也就是说:pageContext 域对象只在 page 范畴内无效,超出了 page 范畴就有效了!
- 首先来看看在page 范畴内能不能应用
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 应用 page 域对象 </title>
</head>
<body>
<%
pageContext.setAttribute("name", "zhongfucheng");
%>
<%
String value = (String) pageContext.getAttribute("name");
System.out.println(value);
%>
</body>
</html>
- 成果如下:
- 咱们当初来试验一下 是不是超出了 page 范畴就有效了!
- 在 2.jsp 中 request 域对象设置属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>request 域对象设置属性 </title>
</head>
<body>
<%
// 这是 request 域对象保留的内容
request.setAttribute("name","zhongfucheng");
%>
<%-- 跳转到 1.jsp 中 --%>
<jsp:forward page="1.jsp"/>
</body>
</html>
- 希图在 1.jsp 中 pageContext 取出 request 存进去的属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 在 page 域对象获取属性 </title>
</head>
<body>
<%
// 希图获取 request 域对象存进的属性
String value = (String) pageContext.getAttribute("name");
System.out.println(value);
%>
</body>
</html>
- 成果如下:
- pageContext 实质上代表着编译后 JSP 的内容,pageContext 还能够封装了拜访其余域的办法!
-
下面的 pageContext 默认是 page 范畴的, 但 pageContext 对象重载了 set、get、removeAttribute 这三个办法
- getAttribute(String name,int scope)
- setAttribute(String name,Object value,int scope)
- removeAttribute(String name,int scope)
-
多了一个设置域范畴的一个参数,如果不指定默认就是 page。当然了,pageContext 把 request、session、application、page 这几个域对象封装着了动态变量供咱们应用。
- PageContext.APPLICATION_SCOPE
- PageContext.SESSION_SCOPE
- PageContext.REQUEST_SCOPE
- PageContext.PAGE_SCOPE
- 方才咱们没有应用重载办法的时候,应用 pageContext 是无奈获取到 request 域对象设置的属性的。当初咱们应用重载后的办法看一下能不能获取失去!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 在 page 域对象获取 request 域对象的属性 </title>
</head>
<body>
<%
// 应用重载的办法获取 request 域对象的属性
String value = (String) pageContext.getAttribute("name",pageContext.REQUEST_SCOPE);
System.out.println(value);
%>
</body>
</html>
- 成果:
-
pageContexst 还有这么一个办法:
- findAttribute(String name)
- 该办法会查找各个域的属性,从小到大开始寻找!也就是 page—>request->session->application。这个是 EL 表达式的原理!,EL 表达式前面会讲到!
- 咱们用此办法看能不能查找出 request 域对象的属性吧!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 应用 findAttribute</title>
</head>
<body>
<%
// 应用 findAttribute 查找 2.jsp 中 request 域对象的属性
String value = (String) pageContext.findAttribute("name");
System.out.println(value);
%>
</body>
</html>
- 成果如下:
引入和跳转
PageContext 类中定义了一个forward 办法和两个 include 办法来别离简化和代替 RequestDispatcher.forward 办法和 include 办法。
- pageContext.forward(String url)
- pageContext.include(String url)
4 种属性范畴
到目前为止,咱们曾经学了 4 种属性范畴了。
- page【只在一个页面中保留属性,跳转页面有效】
- requet【只在一次申请中保留属性,服务器跳转无效,浏览器跳转有效】
- session【在一个会话范畴中保留属性,无论何种跳转均无效,敞开浏览器后有效】
- application【在整个服务器中保留,所有用户都能够应用】
-
- *
- 4 个内置对象都反对以下的办法:
- setAttribute(String name, Object o)
- getAttribute(String name)
- removeAttribute(String name)
利用场景
- request:如果客户向服务器发申请,产生的数据,用户看完就没用了,像这样的数据就存在 request 域, 像新闻数据,属于用户看完就没用的
- session:如果客户向服务器发申请,产生的数据,用户用完了等一会儿还有用,像这样的数据就存在 session 域中,像购物数据,用户须要看到本人购物信息,并且等一会儿,还要用这个购物数据结帐
- servletContext:如果客户向服务器发申请,产生的数据,用户用完了,还要给其它用户用,像这样的数据就存在 servletContext 域中,像聊天数据
什么是 javaBean
- JavaBean 就是一个一般的 java 类 ,也称之为简略 java 对象 –POJO(Plain Ordinary Java Object), 是 Java 程序设计中一种设计模式,是一种基于 Java 平台的软件组件思维
-
JavaBean 遵循着特定的写法,通常有以下的规定:
- 有无参的构造函数
- 成员属性私有化
- 封装的属性如果须要被外所操作,必须编写 public 类型的 setter、getter 办法
- 下面的文字看起来如同很高大上,javaBean 其实非常简单,上面的代码就是依照特定写法、规定编写的一个 JavaBean 对象
public class Person {
private String username ;
private int age;
public Person() {}
public String getUsername() {return username;}
public void setUsername(String username) {this.username = username;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
}
为什么须要应用 Javabean
- 应用 javaBean 的益处就是:封装,重用, 可读!
- 上面援用知乎一段答复:
JaveBean 你能够了解为一辆货车,在你的 java 端和 web 页面进行数据传递的载体,你当然能够每个变量独自传递,或者应用汇合传递,然而javabean 能够使你的数据更有可读性,不便开发时明确变量的意义,也使其余浏览你代码的人能间接你的用意
如果把 bean 类与数据库联结应用,一张表应用 bean 类,能够使你的代码更加简洁高效,易于了解,当初大多数框架都会应用这种机制。
JSP 行为 –JavaBean
-
JSP 技术提供了 三个对于 JavaBean 组件的动作元素,即 JSP 行为(标签),它们别离为:
- <jsp:useBean>【在 JSP 页面中查找 javaBean 对象或者实例化 javaBean 对象】
- <jsp:setProperty>【设置 javaBean 的属性】
- <jsp:getProperty>【获取 javaBean 的属性】
jsp:useBean
-
<jsp:useBean>
标签用于 在指定的域范畴内查找指定名称的 JavaBean 对象:- 存在则间接返回该 JavaBean 对象的援用。
- 不存在则实例化一个新的 JavaBean 对象并将它以指定的名称存储到指定的域范畴中。
- 语法:
<jsp:useBean id="实例化对象的名称" class="类的全名" scope="保留范畴"/>
- 如果 JSP 不反对
<jsp:useBean>
这个行为,咱们要应用 Person 类是这样应用的
<%-- 这里须要导入 Person 类 --%>
<%@ page import="domain.Person" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
<%
//new 出对象
Person person = new Person();
person.setName("zhongfucheng");
System.out.println(person.getName());
%>
</body>
</html>
- 成果如下
- 咱们应用
<jsp:useBean>
就显得十分简洁,不必导包,不必 new 出对象
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
<jsp:useBean id="person" class="domain.Person" scope="page"/>
<%
person.setName("zhongfucheng");
System.out.println(person.getName());
%>
</body>
</html>
- 也能够实现同样的成果:
- 有人可能会想,凭什么写一个
<jsp:useBean>
这样的代码就能够创立出一个对象进去。 - 当初我把 JavaBean 中 无参的构造函数改成有参的 ,咱们看看会呈现什么状况, 出现异常了!
public Person(int age) {this.age = age;}
<jsp:useBean id="person" class="domain.Person" scope="page"/>
外部原理是这样子的:
- 通过下面的代码咱们也 晓得了为什么要有一个无参的构造函数 ! 外部在 new 对象的时候是没有传递参数进去的!
jsp:setProperty
- 语法:
<jsp:setProerty name="对象名称" property="属性名" param="参数名" value="值">
-
在语法上可分为 4 种模式
- <jsp:setProperty name=” 对象名称 ” property=”*”/> 主动匹配
- <jsp:setProperty name=” 对象名称 ” property=” 属性名称 ”/> 指定属性
- <jsp:setProperty name=” 对象名称 ” property=” 属性名称 ” param=” 参数名称 ”/> 指定参数【很少用】
- <jsp:setProperty name=” 对象名称 ” property=” 属性名称 ” value=” 内容 ”/> 指定内容【很少用】
- 当 咱们没有学习到
<jsp:setProperty>
时,咱们获 取表单的信息,而后导入到 javaBean 对象中是这样的一种状况: - 这是 表单的页面代码:
<form action="/zhongfucheng/1.jsp" method="post">
用户名:<input type="text" name="username">
年龄:<input type="text" name="age">
<input type="submit" value="提交">
</form>
- 这是 解决表单提交过去数据的 jsp 的代码
<jsp:useBean id="person" class="domain.Person" scope="page"/>
<%
int age = Integer.parseInt(request.getParameter("age"));
person.setAge(age);
System.out.println(person.getAge());
%>
- 这是能够实现的,然而相对来说,比拟麻烦!
- 咱们来 应用 <jsp:setProperty> 了来看看:
<jsp:useBean id="person" class="domain.Person" scope="page"/>
<%-- 指定属性名称为 age--%>
<jsp:setProperty name="person" property="age"/>
<%
System.out.println(person.getAge());
%>
- 也能够实现,并且 代码更少,性能更弱小!
- 代码更少能够直观看进去,为什么我说它性能更加弱小呢?表单提交过去的数据都是字符串,在咱们没有用 <jsp:setProperty> 前,咱们存储设置 int 类型或其余非字符串类型的数据是须要强转的 !然而 <jsp:setProperty> 不须要咱们强转,它 外部主动帮咱们转换了!
- 咱们再来 应用一下主动匹配来感触它的弱小之处吧
<jsp:useBean id="person" class="domain.Person" scope="page"/>
<%--property 的值设置为 * 就代表主动匹配 --%>
<jsp:setProperty name="person" property="*"/>
<%
System.out.println(person.getAge());
System.out.println(person.getName());
%>
- 咱们再来看一下成果:
- 看到这里,有人可能会感觉好神奇:只有设置 property 的值就能够将表单传递过去的数据封装到 JavaBean 对象中了!这到底是这样做到的???
- 仔细的敌人会发现,JavaBean 的属性名称和表单的 name 属性设置的名称是截然不同的!
private String username ;
private int age;
用户名:<input type="text" name="username">
年龄:<input type="text" name="age">
- 如果我设置不一样还能不能用?咱们试试:表单 name 属性的 username 改成是 user
用户名:<input type="text" name="user">
- 咱们再来 看看还能不能把表单的数据残缺地封装 JavaBean 对象中
- 咱们能够发现:要想可能把表单带过去的数据胜利封装到 JavaBean 对象上,名字要统一!也就是说:JavaBean 属性名要和表单的 name 的名称统一
- 至于原理,它是通过反射来做的, 调用了内省的办法!,咱们看编译后的 JSP 就明确了。
jsp:getProperty
-
语法:
- <jsp:getProperty name=” 对象名 ” property=” 属性名 ”/>
- 该 jsp 行为非常简略,咱们来应用一下就晓得了。
<%-- 应用 <jsp:getProperty> 输入 --%>
<jsp:getProperty name="person" property="username"/>
<jsp:getProperty name="person" property="age"/>
- 成果:
- 原理如下
什么是 EL 表达式?
表达式语言(Expression Language,EL),EL 表达式是用 ”${}” 括起来的脚本,用来更不便的读取对象!
- EL 表达式次要用来读取数据,进行内容的显示!
为什么要应用 EL 表达式?
- 为什么要应用 EL 表达式,咱们先来看一下 没有 EL 表达式是怎么样读取对象数据的吧!
- 在 1.jsp 中设置了 Session 属性
<%@ page language="java" contentType="text/html" pageEncoding="UTF-8"%>
<html>
<head>
<title> 向 session 设置一个属性 </title>
</head>
<body>
<%
// 向 session 设置一个属性
session.setAttribute("name", "aaa");
System.out.println("向 session 设置了一个属性");
%>
</body>
</html>
- 在 2.jsp 中获取 Session 设置的属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
<%
String value = (String) session.getAttribute("name");
out.write(value);
%>
</body>
</html>
- 成果:
- 下面看起来,也没有多简单呀,那咱们试试 EL 表达式的!
- 在 2.jsp 中读取 Session 设置的属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
</head>
<body>
${name}
</body>
</html>
- 只用了简简单单的几个字母就能输入 Session 设置的属性了!并且输入在浏览器上!
- 应用 EL 表达式 能够不便地读取对象中的属性、提交的参数、JavaBean、甚至汇合!
-
- *
EL 表达式的作用
- 首先来 看一下 EL 表达式的语法吧:
${标识符}
- EL 表达式如果找不到相应的对象属性,返回的的空白字符串“”,而不是 null,这是 EL 表达式最大的特点!
获取各类数据
获取域对象的数据
- 下面在例子中,咱们曾经体验到了 获取 Session 域对象的数据是如许中央便!其实 EL 表达式能够让咱们获取各个域范畴的数据
- 在 1.jsp 中设置 ServeltContext 属性(也就是 application)
<%
// 向 ServletContext 设置一个属性
application.setAttribute("name", "aaa");
System.out.println("向 application 设置了一个属性");
%>
- 在 2.jsp 中获取 application 的属性
<%
${name}
%>
- 和 Session 一样,也能获取失去!
- 之前咱们来讲 ServletContext 对象的时候讲过一个办法 findAttribute(String name),EL 表达式语句在执行的时候会调用该办法,用标识符作为关键字别离从 page、request、session、application 四个域中查找相应的对象。这也解释了为什么 EL 表达式能够仅仅通过标识符就可能获取到存进域对象的数据!
- findAttribute()的查找程序:从小到大,也就是 page->request->session->application
获取 JavaBean 的属性
- 以前在 JSP 页面获取 JavaBean 的数据是这样子的:
- 1.jsp 页面 Session 存进一个 Person 对象,设置 age 的属性为 22
<jsp:useBean id="person" class="domain.Person" scope="session"/>
<jsp:setProperty name="person" property="age" value="22"/>
在 2.jsp 中取出 Session 的属性
<%
Person person = (Person) session.getAttribute("person");
System.out.println(person.getAge());
%>
- 成果如下
- 当初我 应用了 EL 表达式读取数据又会十分不便了
// 等同于 person.getAge()
${person.age}
- 下面的代码 等同于调用对象的 getter 办法,外部是通过反射机制实现的!
获取汇合的数据
- 汇合操作在开发中被宽泛地采纳,在 EL 表达式中也很好地反对了汇合的操作!能够 十分不便地读取 Collection 和 Map 汇合的内容
- 为了更好地看出 EL 表达式的弱小之处,咱们也来比照一下应用 EL 表达式和不应用 EL 表达式的区别
- 上面 不应用 EL 表达式输入汇合的元素
- 在 1.jsp 页面中设置 session 的属性,session 属性的值是 List 汇合,List 汇合装载的又是 Person 对象
<%
List<Person> list = new ArrayList();
Person person1 = new Person();
person1.setUsername("zhongfucheng");
Person person2 = new Person();
person2.setUsername("ouzicheng");
list.add(person1);
list.add(person2);
session.setAttribute("list",list);
%>
- 在 2.jsp 中获取到 session 的属性,并输入到页面上
<%
List<Person> list = (List) session.getAttribute("list");
out.write(list.get(0).getUsername()+"<br>");
out.write(list.get(1).getUsername());
%>
应用 EL 表达式又是怎么样的成果呢?咱们来看看!
<%-- 取出 list 汇合的第 1 个元素(下标从 0 开始),获取 username 属性 --%>
${list[0].username}
<br>
<%-- 取出 list 汇合的第 2 个元素,获取 username 属性 --%>
${list[1].username}
- 同样也能够有雷同的成果:
- 咱们再来 应用一下 Map 汇合
- 在 1.jsp 中 session 属性存储了 Map 汇合,Map 汇合的关键字是字符串,值是 Person 对象
<%
Map<String, Person> map = new HashMap<>();
Person person1 = new Person();
person1.setUsername("zhongfucheng1");
Person person2 = new Person();
person2.setUsername("ouzicheng1");
map.put("aa",person1);
map.put("bb",person2);
session.setAttribute("map",map);
%>
- 看起来如同取出数据的时候是会有点简单,然而有了 EL 表达式也是十分轻松的!
${map.aa.username}
<br>
${map.bb.username}
- 成果:
- 如果Map 汇合存储的关键字是一个数字,就不能应用 ”.” 号运算符了,如下所示
- 对于这种状况,咱们能够 应用 ”[]” 的模式读取 Map 汇合的数据
${map["1"].username}
<br>
${map["2"].username}
- EL 表达式配合 JSTL 标签能够很不便的迭代汇合,前面讲到 JSTL 标签的时候会用到!这里就不具体阐明了。
-
- *
EL 运算符
- EL 表达式反对简略的运算符:加减乘除取摸,逻辑运算符。empty 运算符(判断是否为 null),三目运算符
- empty 运算符能够判断对象是否为 null,用作于流程管制!
- 三目运算符简化了 if 和 else 语句,简化代码书写
<%
List<Person> list = null;
%>
${list==null?"list 汇合为空":"list 汇合不为空"}
- 成果:
EL 表达式 11 个内置对象
EL 表达式次要是来对内容的显示,为了显示的不便,EL 表达式提供了 11 个内置对象。
- pageContext 对应于 JSP 页面中的 pageContext 对象(留神:取的是 pageContext 对象)
- pageScope 代表 page 域中用于保留属性的 Map 对象
- requestScope 代表 request 域中用于保留属性的 Map 对象
- sessionScope 代表 session 域中用于保留属性的 Map 对象
- applicationScope 代表 application 域中用于保留属性的 Map 对象
- param 示意一个保留了所有申请参数的 Map 对象
- paramValues 示意一个保留了所有申请参数的 Map 对象,它对于某个申请参数,返回的是一个 string[]
- header 示意一个保留了所有 http 申请头字段的 Map 对象
- headerValues 同上,返回 string[]数组。
- cookie 示意一个保留了所有 cookie 的 Map 对象
- initParam 示意一个保留了所有 web 利用初始化参数的 map 对象
- 上面 测试各个内置对象
<%--pageContext 内置对象 --%>
<%
pageContext.setAttribute("pageContext1", "pageContext");
%>
pageContext 内置对象:${pageContext.getAttribute("pageContext1")}
<br>
<%--pageScope 内置对象 --%>
<%
pageContext.setAttribute("pageScope1","pageScope");
%>
pageScope 内置对象:${pageScope.pageScope1}
<br>
<%--requestScope 内置对象 --%>
<%
request.setAttribute("request1","reqeust");
%>
requestScope 内置对象:${requestScope.request1}
<br>
<%--sessionScope 内置对象 --%>
<%
session.setAttribute("session1", "session");
%>
sessionScope 内置对象:${sessionScope.session1}
<br>
<%--applicationScope 内置对象 --%>
<%
application.setAttribute("application1","application");
%>
applicationScopt 内置对象:${applicationScope.application1}
<br>
<%--header 内置对象 --%>
header 内置对象:${header.Host}
<br>
<%--headerValues 内置对象, 取出第一个 Cookie--%>
headerValues 内置对象:${headerValues.Cookie[0]}
<br>
<%--Cookie 内置对象 --%>
<%
Cookie cookie = new Cookie("Cookie1", "cookie");
%>
Cookie 内置对象:${cookie.JSESSIONID.value}
<br>
<%--initParam 内置对象,须要为该 Context 配置参数能力看出成果【jsp 配置的有效!亲测】--%>
initParam 内置对象:${initParam.name}
<br>
- 效果图:
注意事项:
- 测试 headerValues 时,如果头外面有“-”,例 Accept-Encoding,则要 headerValues[“Accept-Encoding”]
- 测试 cookie 时,例
${cookie.key}
取的是 cookie 对象,如拜访 cookie 的名称和值,须${cookie.key.name}
或${cookie.key.value}
- 测试 initParam 时,初始化参数要的 web.xml 中的配置 Context 的,仅仅是 jsp 的参数是获取不到的
- 下面曾经测过了 9 个内置对象了,至于 param 和 parmaValues 内置对象个别都是别的页面带数据过去的(表单、地址栏)!
- 表单页面
<form action="/zhongfucheng/1.jsp" method="post">
用户名:<input type="text" name="username"><br>
年龄:<input type="text" name="age"><br>
喜好:<input type="checkbox" name="hobbies" value="football"> 足球
<input type="checkbox" name="hobbies" value="basketball"> 篮球
<input type="checkbox" name="hobbies" value="table tennis"> 兵乓球 <br>
<input type="submit" value="提交"><br>
</form>
- 解决表单页面:
${param.username}
<br>
${param.age}
<br>
// 没有学习 jstl 之前就一个一个写吧。${paramValues.hobbies[0]}
<br>
${paramValues.hobbies[1]}
<br>
${paramValues.hobbies[2]}
<br>
- 成果:
- 当然了,应用地址栏形式提交数据给解决页面也是用 param 内置对象去获取数据的!
EL 表达式回显数据
EL 表达式最大的特点就是:如果获取到的数据为 null,输入空白字符串 ””!这个特点能够让咱们数据回显
- 在 1.jsp 中模仿场景
<%-- 模仿数据回显场景 --%>
<%
User user = new User();
user.setGender("male");
// 数据回显
request.setAttribute("user",user);
%>
<input type="radio" name="gender" value="male" ${user.gender=='male'?'checked':''}> 男
<input type="radio" name="gender" value="female" ${user.gender=='female'?'checked':''}> 女
- 成果:
EL 自定义函数
EL 自定义函数用于扩大 EL 表达式的性能,能够让 EL 表达式实现一般 Java 程序代码所能实现的性能
- 开发 HTML 本义的 EL 函数
- 咱们 有时候想在 JSP 页面中输入 JSP 代码,然而 JSP 引擎会主动把 HTML 代码解析,输入给浏览器。此时咱们就要对 HTML 代码本义。
步骤:
- 编写一个蕴含静态方法的类 (EL 表达式只能调用静态方法),该办法很罕用,Tomcat 都有此办法, 可在 webappsexamplesWEB-INFclassesutil 中找到
public static String filter(String message) {if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuilder result = new StringBuilder(content.length + 50);
for (int i = 0; i < content.length; i++) {switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
- 在 WEB/INF 下创立 tld(taglib description)文件,在 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">
<tlib-version>1.0</tlib-version>
<short-name>myshortname</short-name>
<uri>/zhongfucheng</uri>
<!-- 函数的形容 -->
<function>
<!-- 函数的名字 -->
<name>filter</name>
<!-- 函数地位 -->
<function-class>utils.HTMLFilter</function-class>
<!-- 函数的办法申明 -->
<function-signature>java.lang.String filter(java.lang.String)</function-signature>
</function>
</taglib>
- 在 JSP 页面中导入和应用自定义函数,EL 自定义的函数个别前缀为 ”fn”,uri 是 ”/WEB-INF/tld 文件名称 ”
<%@ page language="java" contentType="text/html" pageEncoding="UTF-8" %>
<%@taglib prefix="fn" uri="/WEB-INF/zhongfucheng.tld" %>
<html>
<head>
<title></title>
</head>
<body>
// 实现了 HTML 本义的性能
${fn:filter("<a href='#'> 点我 </a>")}
</body>
</html>
- 成果:
EL 函数库(fn 办法库)
- 因为 在 JSP 页面中显示数据时,常常须要对显示的字符串进行解决,SUN 公司针对于一些常见解决定义了一套 EL 函数库供开发者应用。
- 其实 EL 函数库就是 fn 办法库,是 JSTL 标签库中的一个库,也有人称之为 fn 标签库,然而该库 长得不像是标签,所以称之为 fn 办法库
- 既然作为 JSTL 标签库中的一个库,要应用 fn 办法库就须要导入 JSTL 标签 ! 要想应用 JSTL 标签库就要导入 jstl.jar 和 standard.jar 包!
- 所以,要对 fn 办法库做测试,首先 导入开发包(jstl.jar、standard.jar)
- 在 JSP 页面中指明应用标签库
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
- fn 办法库全都是跟字符串无关的(能够把它想成是 String 的办法)
- fn:toLowerCase
- fn:toUpperCase
- fn:trim
- fn:length
- fn:split
- fn:join【接管字符数组,拼接字符串】
- fn:indexOf
- fn:contains
- fn:startsWith
- fn:replace
- fn:substring
- fn:substringAfter
- fn:endsWith
- fn:escapeXml【疏忽 XML 标记字符】
- fn:substringBefore
- 测试代码:
contains:${fn:contains("zhongfucheng",zhong)}<br>
containsIgnoreCase:${fn:containsIgnoreCase("zhongfucheng",ZHONG)}<br>
endsWith:${fn:endsWith("zhongfucheng","eng")}<br>
escapeXml:${fn:escapeXml("<zhongfucheng> 你是谁呀 </zhongfucheng>")}<br>
indexOf:${fn:indexOf("zhongfucheng","g")}<br>
length:${fn:length("zhongfucheng")}<br>
replace:${fn:replace("zhongfucheng","zhong" ,"ou")}<br>
split:${fn:split("zhong,fu,cheng",",")}<br>
startsWith:${fn:startsWith("zhongfucheng","zho")}<br>
substring:${fn:substring("zhongfucheng","2" , fn:length("zhongfucheng"))}<br>
substringAfter:${fn:substringAfter("zhongfucheng","zhong")}<br>
substringBefore:${fn:substringBefore("zhongfucheng","fu")}<br>
toLowerCase:${fn:toLowerCase("zhonGFUcheng")}<br>
toUpperCase:${fn:toUpperCase("zhongFUcheng")}<br>
trim:${fn:trim("zhong fucheng")}<br>
<%-- 将宰割成的字符数组用 "." 拼接成一个字符串 --%>
join:${fn:join(fn:split("zhong,fu,cheng",","),"." )}<br>
- 成果:
- 应用 fn 办法库数据回显
<%
User user = new User();
String likes[] = {"sing"};
user.setLikes(likes);
// 数据回显
request.setAttribute("user",user);
%>
<%--java 的字符数组以 "," 号宰割开,首先拼接成一个字符串,再判读该字符串有没有蕴含关键字,如果有就 checked--%>
<input type="checkbox"${fn:contains(fn:join(user.likes,","),"sing")?'checked':'' }> 唱歌
<input type="checkbox"${fn:contains(fn:join(user.likes,","),"dance")?'checked':'' }> 跳舞
- 成果:
什么是 JSTL
JSTL 全称为 JSP Standard Tag Library 即 JSP 规范标签库。
JSTL 作为最根本的标签库,提供了一系列的 JSP 标签,实现了根本的性能:汇合的遍历、数据的输入、字符串的解决、数据的格式化等等!
为什么要应用 JSTL
- EL 表达式不够完满,须要 JSTL 的反对 !在 JSP 中,咱们后面曾经用到了 EL 表达式,领会到了 EL 表达式的弱小性能: 应用 EL 表达式能够很不便地援用一些 JavaBean 以及其属性,不会抛出 NullPointerException 之类的谬误!然而,EL 表达式十分无限,它不能遍历汇合,做逻辑的管制。这时,就须要 JSTL 的反对了!
- Scriptlet 的可读性,维护性,重用性都非常差!JSTL 与 HTML 代码非常相似,遵循着 XML 标签语法,应用 JSTL 让 JSP 页面显得整洁,可读性十分好,重用性十分高,能够实现简单的性能!
- 在 JSP 中不举荐应用 scriptlet 输入,举荐应用 JSP 标签。
应用 JSTL 标签库步骤:
- 导入 jstl.jar 和 standard.jar 开发包
- 在 JSP 页面中用 tablib 指令引入须要用到的 JSTL 标签
core 标签库
- core 标签库是 JSTL 的外围标签库,实现了最根本的性能:流程管制、迭代输入等操作!
- core 标签库的前缀个别是 c
c:out
- 简略应用一下
<%
session.setAttribute("name", "zhongfucheng");
%>
//<c:out/> 标签反对标签体,default 属性上的数据能够写在标签体中
//<c:out value="${name}" escapeXml="true"> 您要的数据找不着 </c:out>
<c:out value="${name}" default="您要的数据找不着" escapeXml="true"/>
- 咱们发现 下面的代码实现的成果和 EL 表达式是一样的 , 它杰出的中央就多了两个属性,default 和 escapeXml 属性。如果咱们用到这两个属性,咱们就应用该标签,如果没有用到这两个属性就用 EL 表达式就能够了。
-
- *
c:set
- 该标签有 5 个属性,用起来有略微有些简单了!当初要记住的就是:var 属性操作的是 Integer、Double、Float、String 等类型的数据,target 属性操作的是 JavaBean 或 Map 对象的数据,scope 代表的是 Web 域,value 是值,property 是对象的属性!
应用 var 属性
- 既然 var 属性只能操作 Integer、Double、String 等类型,那么存在 var 属性就肯定没有 property 属性(property 代表的是对象的成员属性,Integer、String 这些类型哪来的成员变量呀)
- 上面的代码流程是这样的:创立了一个 name 的变量,设置的值为 zhongfucheng,范畴是 page
<c:set var="name" value="fucheng" scope="page"/>
${name}
- 成果:
- 当然了,set 标签也反对标签体,value 的值能够写在标签体里边
<c:set var="name" scope="page">
zhongfucheng
</c:set>
- 应用 var 属性和 scope 属性实现计数器
<%-- 因为上面变量须要做加法运算,所以要定义进去,不然服务器是不晓得我的变量是 Integer 类型的 --%>
<%
Integer sessionCount = 0;
Integer applicationCount = 0;
%>
<c:set var="sessionCount" value="${sessionCount+1}" scope="session"/>
<c:set var="applicationCount" value="${applicationCount+1}" scope="application"/>
- 成果:
应用 target 属性
- 应用 target 属性与之配对的是 property 属性,target 属性只能操作 JavaBean 或 Map 对象,property 就是对应的成员变量或 key 了。。
- 既然 target 属性操作的是 JavaBean 或 Map 对象,那么肯定是通过 EL 表达式来获取到对象了。taget 属性如果获取不到数据会抛出异样!应用 target 属性就肯定没有 scope 属性(scope 属性代表的是保留范畴,target 的值都是获取来的,难道你还能扭转人家的范畴?)
<%-- 创立出 JavaBean 对象,设置为 session 范畴的属性 --%>
<jsp:useBean id="person" class="domain.Person" scope="session"/>
<%-- 获取到 person 对象,设置 age 属性的值为 32--%>
<c:set target="${person}" property="age" value="32"/>
${person.age}
- 成果:
c:remove
remove 标签就相当简略了,只有 var 和 scope 属性,代表的是删除域范畴的属性
- 上面简略来测试一下吧:
<%-- 创立出 JavaBean 对象,设置为 session 范畴的属性 --%>
<jsp:useBean id="person" class="domain.Person" scope="session"/>
<%-- 获取到 person 对象,设置 age 属性的值为 32--%>
<c:set target="${person}" property="age" value="32"/>
${person.age}
<br>
<%-- 删除 session 属性 --%>
<c:remove var="person" scope="session"></c:remove>
${person.age==null?"存在 session 的 person 对象被删除了!":"我还在呢!"}
- 成果:
c:catch
该标签次要用来处理程序中产生的异样。
catch 标签也非常简略,只有一个 var 属性,var 属性封装了异样的信息!
<%-- 创立出 JavaBean 对象,设置为 session 范畴的属性 --%>
<jsp:useBean id="person" class="domain.Person" scope="session"/>
<c:catch var="message">
<%--target 属性只能是 EL 表达式,当初我是字符串,获取不到对象,必定会抛出异样的!--%>
<c:set target="person" property="age" value="32"/>
</c:catch>
${message}
- 成果:
c:if
JSTL 提供了 if 标签实现分支语句的实现,test 属性是不可或缺的。
var 和 scope 属性我看来如同没什么用的(保留执行后果有什么用?)
- 依据传递过去的参数的不同显示不同的页面!
<%-- 如果带过去的名字是 zhongfucheng,那么能够登陆 --%>
<c:if test="${param.name=='zhongfucheng'}">
用户名:<input type="text" name="username"><br>
明码:<input type="password" name="password"><br>
<input type="submit" value="登陆">
</c:if>
<%-- 如果带过去的名字是 ouzicheng,那么就是注册 --%>
<c:if test="${param.name=='ouzicheng'}">
用户名:<input type="text" name="username"><br>
明码:<input type="password" name="password"><br>
<input type="submit" value="注册">
</c:if>
- 留神地址栏的参数!
c:choose
if 标签没有 else 的性能,如果须要相似于 java 中的 if else 流程就须要应用 choose 标签。
choose 标签须要联结 when 和 otherwise 标签一起应用!
<c:choose>
<c:when test="${param.name=='zhongfucheng'}">
你好啊,zhongfucheng
</c:when>
<c:when test="${param.name=='ouzicheng'}">
你好啊,ouzicheng
</c:when>
<c:otherwise>
你是谁啊?别轻易过去!</c:otherwise>
</c:choose>
- 成果:
c:forEach
forEach 为循环标签,相当于 Java 中的 while 和 for
- 之前咱们在应用 EL 表达式获取到汇合的数据,遍历汇合都是用 scriptlet 代码循环,当初咱们学了 forEach 标签就能够舍弃 scriptlet 代码了。
- 向 Session 中设置属性,属性的类型是 List 汇合
<%
List list = new ArrayList<>();
list.add("zhongfucheng");
list.add("ouzicheng");
list.add("xiaoming");
session.setAttribute("list", list);
%>
- 遍历 session 属性中的 List 汇合,items:行将要迭代的汇合。var:以后迭代到的元素
<c:forEach var="list" items="${list}" >
${list}<br>
</c:forEach>
- 成果:
- 遍历 Map 对象有略微地不一样,咱们来看一下,var 属性保留的不是每个迭代的对象,而是 Map.Entry。
<%
Map map = new HashMap();
map.put("1", "zhongfucheng");
map.put("2", "xiaohong");
map.put("3", "xiaoming");
session.setAttribute("map",map);
%>
<c:forEach var="me" items="${map}" >
${me.key} ${me.value}<br>
</c:forEach>
- begin 默认从 0 开始、end 默认为汇合的最初一个元素、step 默认为 1
-
varStatus 代表着以后对象被迭代的信息,它有以下的属性。
- index【返回以后是第几个对象,从 0 开始计数】
- count【曾经遍历多少个对象了,从 1 开始计数】
- first【是否是第一个】
- last【是否是最初一个】
- current【以后被迭代的对象】
- begin【开始的地位】
- end【最初的地位】
- step【步长】
<c:forEach var="list" items="${list}" varStatus="varStatus" >
${list}您的下标是:${varStatus.index}<br>
</c:forEach>
- 成果:
c:forTokens
该标签相似于 String 类的 split()和 for 循环的一种汇合
它与 forEach 标签十分类似,都有 begin、end、step、items、var、varStatus 属性,不同的是 forTokens 标签的 items 属性外面是字符串,这个字符串会被 delims 属性的内容宰割成多个字符串!
<c:forTokens items="zhongfucheng,ouzicheng,xiaoming,xiaohong" var="name" delims="," >
${name}
</c:forTokens>
- 效果图:
c:import
import 标签相似于 JSP 行为 <jsp:include/>
和 JSP 指令<%include>
import 标签的属性:
- url【指定要蕴含的门路,Internet 所有的 url 都能够】
- context【拜访同一个 web 容器的其余资源,以 ”/” 结尾】
- var【保留导入的文件的内容,以 String 类型存储】
- socpe【保留的范畴,默认是 page】
- charEncoding【字符编码】
- varReader【保留导入文件的内容,以 Reader 类型存储】
当然了,import 标签性能更加更大!弱小在哪里呢?import 标签能够引入 Internet 网页上的内容,也就是说,csdn 也能够引入进来!
- 咱们来用一下把!
<c:import url="http://www.csdn.net" charEncoding="UTF-8" />
- 咱们一看,是没有款式的:
- 打印 csdn 的源代码:
<c:import url="http://www.csdn.net" charEncoding="UTF-8" var="net"/>
CSDN 的源码是:<br><br><br><br><br>
<c:out value="${net}" escapeXml="true"></c:out>
- 成果:
c:param
- 在 JSP 页面进行 URL 的相干操作时,常常要在 URL 地址前面附加一些参数。<c:param> 标签能够嵌套在 <c:import>、<c:url> 或 <c:redirect> 标签内,为这些标签所应用的 URL 地址附加参数。
- <c:param> 标签在为一个 URL 地址附加参数时,将主动对参数值进行 URL 编码,例如,如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到 URL 地址前面 ,这也就 是应用 <c:param> 标签的最大益处。
-
- *
c:url
url 标签非常实用!在浏览器禁用 Cookie 的时候,咱们之前学 Servlet 时解决办法是:response.encodeURL()。url 标签也能够实现这样的性能,再配合 param 标签应用,就非常实用了!
- 咱们 配合 param 标签来应用一下吧!
<c:url value="2.jsp" var="url">
<c:param name="name" value="中国!">
</c:param>
</c:url>
<a href="${url}"> 我通过了 URL 地址重写!</a>
- 成果:
c:redirect
redirect 标签用于实现 Redirect 性能,当然了,此标签也可能配合 param 标签应用!
- 简略应用一下,重定向到 2.jsp,带了一个参数:
<c:redirect url="2.jsp" >
<c:param name="name" value="zhongfucheng">
</c:param>
</c:redirect>
- 在 2.jsp 中获取到参数
fmt 标签库
fmt 标签库也叫做国际化标签库。这里就不具体阐明了,等我讲到 Web 国际化的时候才讲吧!
fn 办法库
fn 办法库也叫做 EL 函数库、fn 标签库。这个在解说 EL 表达式的时候有具体的阐明,可转移到我 EL 表达式的博文中!
为什么要应用自定义标签?
JSTL 标签库只提供了简略的输入等性能,没有实现任何的 HTML 代码封装,并且某些简单类型转换,或者逻辑解决的时候,JSTL 标签库实现不了,须要自定义标签!
编写自定义标签的步骤:
- 编写一个实现 Tag 接口的 Java 类【标签处理器类】
- 在 WEB-INF 目录下创立 tld(Tag Library Descriptor)文件,在 tld 文件中对标签解决类(实现 Tag 接口的 Java 类)进行形容
疾速入门
- 指标:应用标签输入客户机的 IP 地址!
- 依照步骤来:首先编写一个实现 Tag 接口的 Java 类
public class showIp implements Tag {
@Override
public void setPageContext(PageContext pageContext) { }
@Override
public void setParent(Tag tag) { }
@Override
public Tag getParent() {return null;}
@Override
public int doStartTag() throws JspException {return 0;}
@Override
public int doEndTag() throws JspException {return 0;}
@Override
public void release() {}
}
- 既然要获取到客户机的 IP 地址,那么 request 对象是必不可少的 。当初问题来了, 在 Tag 重写的办法如同不能间接获取到 request 对象啊。
- 通过我一番认真的察看,发现了上面这个办法:
@Override
public void setPageContext(PageContext pageContext) {}
- 既然能获取到 pageContext 对象,那么其余 8 大内置对象还不是随随便便?于是乎,我就 定义一个成员变量 pageContext,在 setPageContext()办法中传递过去的 pageContext 赋值给我定义的成员变量即可!
private PageContext pageContext = null;
@Override
public void setPageContext(PageContext pageContext) {this.pageContext = pageContext;}
- 好的,看回咱们的需要:应用标签输入客户机的 IP 地址。在下面残余 5 个办法中,最有可能就是在 doStartTag()办法中编写代码!
@Override
public int doStartTag() throws JspException {
// 获取到 request 对象
HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest();
// 获取到客户机的 ip 地址
String ip = httpServletRequest.getRemoteAddr();
// 获取输入到浏览器的对象
JspWriter jspWriter = pageContext.getOut();
// 上面的异样只能捕捉,因为子类的异样不能比父类多
try {jspWriter.write(ip);
} catch (IOException e) {e.printStackTrace();
}
return 0;
}
- 接着,编写 tld 文件,形容实现 Tag 接口的 Java 类【标签解决类】。
<?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">
<tlib-version>1.0</tlib-version>
<short-name>zhongfucheng</short-name>
<uri>/zhongfucheng</uri>
<!-- Invoke 'Generate' action to add tags or functions -->
<tag>
<name>viewIp</name>
<tag-class>tag.showIp</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
- 上面咱们 来测试一下看能不能用
标签解决类具体阐明
看完下面的程序,大部分人都是懵逼的。因为还不晓得它具体是怎么用的,调用程序是什么。
- 首先咱们来看一下 Tag 接口的源码!
public interface Tag extends JspTag {
int SKIP_BODY = 0;
int EVAL_BODY_INCLUDE = 1;
int SKIP_PAGE = 5;
int EVAL_PAGE = 6;
void setPageContext(PageContext var1);
void setParent(Tag var1);
Tag getParent();
int doStartTag() throws JspException;
int doEndTag() throws JspException;
void release();}
-
下面程序的执行流程:
- JSP 引擎遇到自定义标签,首先创立标签处理器类的实例对象。
- JSP 引擎实例化完标签处理器类后,调用 setPageContext()办法,将 pageContext 对象传递给标签处理器类,使得标签处理器类能够通过 pageContext 对象与 JSP 页面进行通信!
- setPageContext()办法执行完后,调用 setParent()办法,将以后标签的父标签传递给以后处理器类,如果以后标签没有父标签,则传入 null
- 当 WEB 容器执行到自定义标签的开始标记时,调用 doStartTag()办法。
- 当 WEB 容器执行到自定义标签的完结标记时,调用 doEndTag()办法。
- 一般来说,当 WEB 容器执行完自定义标签后,标签处理器类会驻留在内存中,直至进行 WEB 利用时,WEB 容器才会调用 release()办法
-
咱们当初曾经分明了办法的执行程序了,可 Tag 接口的源码还有 4 个变量阿,它们是用来做什么的呢?咱们在编写 JSP 页面时,常常须要在页面中引入一些逻辑,例如:
- 管制 JSP 页面某一部分(标签体)是否执行
- 管制整个 JSP 页面是否执行
- 管制 JSP 页面内容反复执行
- 批改 JSP 页面内容输入
- 再看回 4 个变量的名字,咱们能够发现,这 4 个变量就是用来做逻辑判断的!
- 咱们来测试一下吧,在 doEndTag()办法中,返回的是 SKIP_PAGE 变量,看下会怎么样!
@Override
public int doEndTag() throws JspException {return SKIP_PAGE;}
- 咱们再来看一看成果:
- 如同是没什么区别!咱们再查看一下源代码,发现执行完标签后,前面的代码全都没有执行!
- doStartTag()办法应用的是 SKIP_BODY 和 EVAL_BODY_INCLUDE 这两个变量,判断是否执行标签体的内容。
- doEndTag()办法应用的是 SKIP_PAGE 和 EVAL_PAGE 这两个变量,判断是否执行剩下页面的内容
- 管制 JSP 页面内容反复执行和批改 JSP 页面内容输入前面会有!
-
- *
tld 文件具体阐明
- 首先咱们来 看一下 tld 文件以后用到的内容吧!
<tlib-version>1.0</tlib-version>
<short-name>myshortname</short-name>
<uri>http://mycompany.com</uri>
<tag>
<name></name>
<tag-class></tag-class>
<body-content></body-content>
</tag>
-
咱们一个一个来看:
- shortname 举荐应用 prefix
- uri 就是引入这个标签库应用的 uri
- name 为标签名
- tagclass 为实现类
- bodycontent 为标签体的限度,它有 4 个值:EMPTY【不容许有标签体】,JSP【容许有 JSP 代码】,scriptless【不容许有脚本代码(也就是 <%%>),容许有 EL 表达式,文本,JSP 行为】,tagdepentend【标签体内的 JSP 代码不会被解析,间接输入文本】
-
- *
TagSupport 类
大部分时候咱们都不须要实现 Tag 接口来编写自定义标签,TagSupport 是 Tag 的一个模板类,实现了 pageContext,parent 的 getter、setter 办法以及一些其余的性能。咱们要做的就是重写 doStartTag()和 doEndTag()办法
- 上面咱们就来简略应用一下吧:
- 继承 TagSupport 类,重写 doStartTag()办法,比间接实现 Tag 接口简洁很多!
public class Demo1 extends TagSupport {
@Override
public int doStartTag() throws JspException {
// 获取到 request 对象
HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest();
String method = httpServletRequest.getMethod();
JspWriter jspWriter = pageContext.getOut();
try {jspWriter.write(method);
} catch (IOException e) {e.printStackTrace();
}
return 0;
}
}
- 在 tld 文件中形容一把:
<tag>
<name>showMethod</name>
<tag-class>tag.Demo1</tag-class>
<body-content>empty</body-content>
</tag>
- 成果:
带属性的标签
下面咱们编写的自定义标签都没有附带属性的,咱们在应用 core 标签库的时候,标签个别都带有属性。
其实JSTL 标签库的原理就是自定义标签,把自定义标签搞明确了,对 JSTL 标签库的应用就有更好的了解了!
- 想要自定义标签带有属性也非常简单,只有在标签处理器类上加一个成员变量和 setter、getter(),再在 tld 文件中形容下该属性即可!它的原理是这样的:当标签应用到属性的时候,引擎就会调用它的 setter()办法
- 上面我想要实现的性能是:应用标签的人,传入一个字符串格局就能够显示想要的格局日期
- 编写标签处理器类,减少一个成员变量以及对应的 setter、getter 办法
public class Demo1 extends TagSupport {
// 创立成员对象,对应的 setter、getter 办法
private String format = null;
@Override
public int doStartTag() throws JspException {
// 创立日期格式化对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
// 格式化日期并向浏览器输入
try {pageContext.getOut().write(simpleDateFormat.format(new Date()));
} catch (IOException e) {e.printStackTrace();
}
return 0;
}
public String getFormat() {return format;}
public void setFormat(String format) {this.format = format;}
}
- 在 tld 文件中形容标签和属性,name 代表的是属性的名字,required 代表的是是否为必须,rtexprvalue 代表是否应用 EL 表达式
<tag>
<name>formatDate</name>
<tag-class>tag.Demo1</tag-class>
<body-content>empty</body-content>
<attribute>
<name>format</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
- 咱们来看一下成果:
标签的继承关系
- 在深刻解说之前,咱们先来看一下各种 Tag 接口、类之间的关系,这样学习上来才不会晕!
IterationTag 阐明
- 咱们曾经应用过了 Tag 接口和 TagSupport 类了。接下来咱们看一下 IterationTag 是什么玩意。
public interface IterationTag extends Tag {
int EVAL_BODY_AGAIN = 2;
int doAfterBody() throws JspException;}
- 从关系图咱们也能够看出,IterationTag 接口实现了 Tag 接口,InterationTag 接口和 Tag 接口最次要的区别就是多了个 doAfterBody()办法和 EVAL_BODY_AGAIN 变量
- 了解起来也很简略:当 doAfterBody()返回的是 EVAL_BODY_AGAIN 变量,那么标签体的内容就始终循环!当然了,TagSupport 也实现了 Iteration 接口,也就是说 TagSupport 类也能实现 Iteration 接口的事件!
- 咱们来应用一下吧:
public class Demo1 extends TagSupport {
@Override
public int doStartTag() throws JspException {
try {pageContext.getOut().write("hello");
} catch (IOException e) {e.printStackTrace();
}
// 执行标签体
return EVAL_BODY_INCLUDE;
}
@Override
public int doAfterBody() throws JspException {// 标签体一直循环,直到 doAfterBody()返回的是 SKIP_BODY
return EVAL_BODY_AGAIN;
}
}
- tld 文件中形容,既然标签体有内容,就不能用 empty 了!
<tag>
<name>foreverEval</name>
<tag-class>tag.Demo1</tag-class>
<body-content>tagdependent</body-content>
</tag>
- 留神看横向的滑轮,曾经死循环输入了:
- doAfterBody()中只有返回的是 SKPI_BODY 就退出循环,执行 doEndTag()办法
// 定义一个变量,规定标签体循环的次数
int x = 0;
@Override
public int doStartTag() throws JspException {
try {pageContext.getOut().write("hello");
} catch (IOException e) {e.printStackTrace();
}
// 执行标签体
return EVAL_BODY_INCLUDE;
}
@Override
public int doAfterBody() throws JspException {
x++;
if (x >= 10) {return SKIP_BODY;}
// 标签体一直循环,直到 doAfterBody()返回的是 SKIP_BODY
return EVAL_BODY_AGAIN;
}
- 当初咱们曾经能管制循环的次数了!
BodyTag 阐明
后面咱们曾经应用到了带标签体的自定义标签了,后面的都是只能间接输入而得不到标签体的内容,既然得不到标签体的内容,就更别说批改标签体了!
- 此时,咱们就须要 BodyTag 接口的反对了!它专门用来解决带标签体的标签 ,上面咱们来 看一下 BodyTag 的源码!
public interface BodyTag extends IterationTag {
/** @deprecated */
int EVAL_BODY_TAG = 2;
int EVAL_BODY_BUFFERED = 2;
void setBodyContent(BodyContent var1);
void doInitBody() throws JspException;}
- BodyTag 多了 EVAL_BODY_BUFFERED 变量【一个曾经标识过期了】,多了 setBodyContent 和 doInitBody()两个办法
-
其实 应用 BodyTag 非常简略
- 如果 doStartTag()办法返回的是 EVAL_BODY_BUFFERED,把标签体的内容缓存起来
- 接着调用 setBodyContent()办法和 doInitBody()办法,封装标签体的内容到 BodyContent 对象中
- 接着调用 doEndTag()办法
- 对于标签体的内容,咱们能够通过 getBodyContenet()来获取!
- 再看回下面的关系图,BodyTag 实现了 IterationTag 和 Tag 接口,如果间接实现 BodyTag 接口做开发,要实现的办法就太多了。个别咱们应用继承 BodyTag 的 BodyTagSupport 来做开发
-
- *
BodyTagSupport 阐明
- 首先来看一下源代码吧:
public class BodyTagSupport extends TagSupport implements BodyTag {
protected BodyContent bodyContent;
public BodyTagSupport() {}
public int doStartTag() throws JspException {return 2;}
public int doEndTag() throws JspException {return super.doEndTag();
}
public void setBodyContent(BodyContent b) {this.bodyContent = b;}
public void doInitBody() throws JspException {}
public int doAfterBody() throws JspException {return 0;}
public void release() {
this.bodyContent = null;
super.release();}
public BodyContent getBodyContent() {return this.bodyContent;}
public JspWriter getPreviousOut() {return this.bodyContent.getEnclosingWriter();
}
}
-
能够发现:BodyTagSupport 次要裁减了以下的内容:
- 把 BodyContent 间接定义为成员变量,在获取标签体内容的时候就不须要通过 getBodyContent()获取了
- 提供获取 JspWriter 的办法,不须要从 pageConext 中获取了
- 以上的两个裁减都简化了咱们的代码书写!
protected BodyContent bodyContent;
public JspWriter getPreviousOut() {return this.bodyContent.getEnclosingWriter();
}
- 从 BodyTag 接口中,我就说到了:标签体的内容封装到了 BodyContent 类中,那么 BodyContent 类到底是什么?咱们来看一下源码:
public abstract class BodyContent extends JspWriter {
private JspWriter enclosingWriter;
protected BodyContent(JspWriter e) {super(-2, false);
this.enclosingWriter = e;
}
public void flush() throws IOException {throw new IOException("Illegal to flush within a custom tag");
}
public void clearBody() {
try {this.clear();
} catch (IOException var2) {throw new Error("internal error!;");
}
}
public abstract Reader getReader();
public abstract String getString();
public abstract void writeOut(Writer var1) throws IOException;
public JspWriter getEnclosingWriter() {return this.enclosingWriter;}
}
- 原来 BodyContent 继承着 JspWriter,它与 JspWriter 最大的区别是:BodyContent 类的任何写入的内容并不主动地向页面输入!
- 咱们个别应用 BodyContent 都应用两个办法:
// 将数据转变成 Reader 对象
public abstract Reader getReader();
// 将数据转变成 String 对象
public abstract String getString();
- 再从关系图咱们能够看初,BodyTagSupport 继承了 TagSupport 类实现了 BodyTag 接口,能够说:BodyTagSupport 有着后面讲的接口和类的所有性能!。
- 上面咱们来 应用下 BodyTagSupport 将标签体的内容转成是小写的:
- 标签处理器类
public class Demo1 extends BodyTagSupport {
@Override
public int doStartTag() throws JspException {
// 想要获取到标签体的内容,就要返回 EVAL_BODY_BUFFERED 变量
return EVAL_BODY_BUFFERED;
}
@Override
public int doEndTag() throws JspException {
// 获取到标签体的内容
String value = bodyContent.getString();
// 将标签体的内容转成小写并输入
try {this.getPreviousOut().write(value.toLowerCase());
} catch (IOException e) {e.printStackTrace();
}
return super.doEndTag();}
}
- tld 文件:
<tag>
<name>BodyContentToLowerCase</name>
<tag-class>tag.Demo1</tag-class>
<body-content>tagdependent</body-content>
</tag>
- 成果: