一、JSP规范(语法)
Sun制定的JSP规范,包含了如何将JSP页面翻译成Servlet。Tomcat中的JSP翻译引擎,就遵循了这个JSP规范。
- JSP注释
<%-- 这是JSP注释 -->
<!-- 也可以用HTML注释 -->
- JSP的Java代码块
<%
int i=5;
System.out.println("i = "+i); //被翻译到_jspService()方法中
%>
- JSP的声明语句块
<!%
//被放在类体中
private int i=5;
public void doSome(){
System.out.println("---dosome---");
}
%>
- JSP的表达式块(表达式没有分号)
<%= a %>
<%= "张三" %>
<%-- out.print(a);
out.print("张三"); -->
二、JSP内置对象
在JSP的Java代码块、表达式块中可以直接使用的引用,称为JSP的内置对象。
之所以可以直接使用是因为这九个对象就是_jspServer()方法的局部变量。
- pageContext:
后面学EL表达式有用
- application
application,即ServletContext。
方法:
String getInitParameter():获取web.xml的上下文参数值。
Enumeration getInitParameterNames():获取web.xml的所有上下文参数名称。
void setAttribute(String name,Object object):在ServletContext的域属性空间中,放入数据。
Object getAttribute(String name):从域属性空间中获取指定名称的数据。
void removeAttribute(String name):从域属性空间中删除指定名称的数据。
String getRealPath(String path):获取当前Web应用中指定文件或目录
String getContextPath:获取当前应用在Web容器中的名称。
- out
继承Writer类,即out就是一个输出流对象。
- page
filan java.lang.Object page=this; //Servlet对象本身。
- exception
必须配合page指令使用。
- 其他对象
request、response、session、config。用法与Servlet相同。
三、JSP指令(directive)
JSP指令的作用是为当前页面做一些基本设置,为当前的页面运行提供基本的环境。
- page指令
用于设置当前JSP页面的相关信息。一个jsp文件卡宴包含多个page指令
<%@ page pageEncoding="utf-8" %>
<%-- response.setContextType("text/html;charset=UTF-8"); --%>
<% page contentType="text/html" %>
<%-- response.setContentType("text/html") -->
<%@ page import="java.util.Date,java.sql.*" %>
<%-- import java.util.Date;
import java.sql.*;
-->
<@ page errorPage="/error.jsp" %>
<%-- 用于指定,当前页面运行过程中发生异常是所要跳转到的页面-->
isErrorPage: <%@ page isErrorPage="true" pageEncoding="utf-8" %>
表示当前页面为一个“错误处理页面”,在_jspService()方法中多了一个exception变量。
可以使用<% exception.printStackTrace(); %>输出错误信息
- include指令
即包含指令,用于将指定的文件包含的当前JSP文件中。
<%@ include file="/left.jsp" %>
编译后只生成一个.java文件,并不会生成left_jsp.java文件
说明include指令是在编译之前完成的,是在编译之前由JSP翻译引擎完成的,不是在程序运行期完成的。这种包含是一种静态包含,称为静态联编。
四、JSP动作(Action)
JSP动作是指,使用系统定义好的标签来完成本应由Java代码完成的功能。
常用动作:转发动作和包含动作。
- forward动作
<%-- 只要有forward动作,当前页面的内容都无法显示。 --%>
<jsp:forward page="/next.jsp"/>
- include动作
include动作用于完成将指定页面包含到当前页面的功能。
<jsp:include page="/left.jsp"/>
编译结果生成两个.java文件。
说明include动作是在运行期完成的,称为动态联编。
静态联编只生成一个Servlet,对资源的消耗少,可以共享同一个变量。
动态联编不能共享变量。
五、EL表达式
Expression Language,表达式语言,是一种在JSP页面中获取数据的简单方式。
在JSP页面的任何静态部分均可以通过 ${expression} 的形式获取到指定的表达式的值
- 获取数据
<!-- 从四大域中依次查找数据 -->
<%
//pageContext.setAttribute("stu", "张三");
//request.setAttribute("stu", "张三");
//session.setAttribute("stu", "张三");
application.setAttribute("stu", "张三");
%>
<!-- 从pageContext依次查找到application域空间,效率低。 -->
stu = ${stu }
<!-- 从指定域中获取数据 -->
<%
pageContext.setAttribute("some", "pagValue");
request.setAttribute("some", "reqValue");
session.setAttribute("some", "sesValue");
application.setAttribute("some", "appValue");
%>
<!-- 从指定域中获取数据,提高效率 -->
some = ${pageScope.some }
<br> some = ${requestScope.some }
<br> some = ${sessionScope.some }
<br> some = ${applicationScope.some }
<br>
<!-- 访问指定对象的属性 -->
<%
Student student = new Student("张三", 23);
request.setAttribute("stu", student);
%>
studentName = ${requestScope.stu.name }
<br> studentName = ${requestScope.stu['name'] }
<br> studentName = ${requestScope.stu["name"] }
<br>
<!-- 获取数组中的元素 -->
<%
String[] studentName = { "张三", "李四", "王五" };
pageContext.setAttribute("names", studentName);
%>
studentName[1]=${names[1] }
<br>
<!-- 越界不会报错! -->
studentName[100]=${names[100] }
<br>
<!-- 取List中的元素(和数组类似,适用于Map) -->
<%
Student student1 = new Student("张三", 23);
Student student2 = new Student("李四", 24);
Student student3 = new Student("王五", 25);
List<Student> students = new ArrayList();
students.add(student1);
students.add(student2);
students.add(student3);
request.setAttribute("stus", students);
%>
<!-- 输出王五 -->
students[2].name=${stus[2].name }
<br>
<!-- 越界不会报错 -->
students[200].name=${stus[200].name }
<br>
EL无法输出Set集合的元素。因为Set集合的元素具有无序性,即没有索引的概念。无法索引获取元素。
- 运算符
EL表达式可以进行各种运算,其中常用的运算有:
除了上诉运算符外,还有一个非常有用的运算符empty,其用法为${empty 变量},结果为布尔值。
<!-- EL表达式运算符 -->
<%
pageContext.setAttribute("name", "");
pageContext.setAttribute("student", null);
pageContext.setAttribute("list", new ArrayList());
%>
<!-- 下面输出结果都为true -->
xxx变量未定义:${empty xxx}<br>
name变量为空字符串:${empty name }<br>
student对象为null:${empty student }<br>
list集合没元素:${empty list }
- EL内置对象
与代码块表达式类似,EL存在11个内置对象。
pageContext: 与JSP内置对象的pageContext是同一个对象。通过该对象可以获取request、response、session、servletContext、servletConfig
<form action="${pageContext.request.contextPath}/register.do" method="post">
<!-- -->
</form>
param: 在EL中通过${param.参数名}可获取到请求中指定参数的值
<!-- 例:URL=127.0.0.1:8080/JSP_TEST/index.jsp?name=abc -->
para1m.name=${param.name }<br>
<!-- 若请求中同一参数具有多值 -->
<!-- 例:URL:127.0.0.1:8080/JSP_TEST/index.jsp?hobby=swimming&hobby=reading -->
hobby[0] = ${paramValues.hobby[0] }<br>
hobby[1] = ${paramValues.hobby[1] }<br>
<!-- 获取初始化参数 -->
<!-- web.xml内定义:
<context-param>
<param-name>Tooi</param-name>
<param-value>1<param-value>
</context-param>
-->
初始化参数Tooi的值为:${initParam.Tooi }<br>
- 自定义EL函数
步骤: 1、定义函数:
public class StringFunctions {
public static String lowerToUpper(String source) {
return source.toUpperCase();
}
}
2、注册函数:在/WEB-INF目录下,新建一个扩展名为.tld的XML文件
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>myfn</short-name>
<uri>http://www.Tooi.com/jsp/myCustomTld</uri>
<function>
<name>myLowerToUpper</name>
<function-class>ELFunction.StringFunctions</function-class>
<function-signature>java.lang.String lowerToUpper( java.lang.String )</function-signature>
</function>
</taglib>
<short-name>: 指定该函数库的名称,一个函数库一个名称。
<uri>: 指定函数库对应的URI,用于JSP文件引用。
<name>: 指定将来在JSP使用函数的名称。
<function-class>: 指定该函数定义在那个类中。
<function-signature>: 指定该函数的签名,即指定类中的哪个方法。
3、使用函数
<!-- 引入自定义的标签库 -->
<%@ taglib uri="http://www.Tooi.com/jsp/myCustomTld" prefix="myfn"%>
<!-- 只能使用常量或域属性作为参数 -->
abc=${myfn:myLowerToUpper("abc") }
<%
request.setAttribute("username", "Tooi");
%>
name=${myfn:myLowerToUpper(username) }
- JSTL中的EL函数
Apache已经定义好了一套标准的标签库规范,称为JSTL,JSP Standard Tag Libraray,即JSP标准标签库。
1、Apache官网下载:下载地址
2、JSTL的EL函数标签库
3、16个EL函数
4、EL函数用法:
两个jar文件不能放在/WEB-INF/lib目录下,不需要bulid path
<!-- 引入JSTL函数标准库 -->
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<body>
abc=${fn:toUpperCase("abc") }
<br>
<%
request.setAttribute("name", "Tooi");
%>
name=${fn:toUpperCase(name) }
<br>
</body>
- EL总结
EL不能出现在Java代码块、表达式块灯JSP动态代码部分。
EL只能从pageContext、request、session、application四大域属性空间中获取数据。
EL不会抛出空指针异常、数字访问越界异常。
EL不具有对字符串的处理能力,需要借助JSTL。
六、自定义标签
使用自定义标签替换一个Java代码片段,完成相同的功能,简化代码。
- 1、定义标签处理器:
定义一个类来完成自定义标签的功能,需要实现接口:javax.servlet.jsp.tagext.SimpleTag。
但已经有javax.servlet.jsp.tagext.SimpleTagSuppord实现了接口,所以只要继承SimpleTagSuppord类。
doTag: 当JSP页面中执行到标签时,由服务器自动调用执行的方法。自定义标签功能的实现都在这个方法中。
getParent: 获取当前标签的父标签的引用。
setJspBody: 由服务器自动调用。服务器会将JSP代码片段传入给当前的Java类。
setJspContext: 由服务器自动调用。服务器会将PageContext对象传入给当前的Java类。注意,PageContext是JspContext的子类。
getJspContext: 获得JspContext,可以转为PageContext
setParent: 由服务器自动调用。服务器会将当前标签的父标签引用传给当前的Java类。
//没有标签体的标签处理器
public class ClientIpSimpleTag extends SimpleTagSupport{
@Override
public void doTag() throws JspException,IOException{
//获取PageContext对象
PageContext pageContext = (PageContext)getJspContext();
//获取客户端IP
String clientIP=pageContext.getRequest().getRemoteAddr();
//输出到浏览器
pageContext.getOut().write(clientIP);
}
}
//有标签体的标签处理器
public class ToUpperCaseSimpleTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
// 创建一个具有缓存功能的输出流,要求改输出流可以获取到缓存数据
// 此时的输出流缓存数据为空
StringWriter sw = new StringWriter();
// 获取JSP片段,即标签文本对象
JspFragment jspBody = this.getJspBody();
// 将标签文本对象写入到输出流中,输出流中就有了数据
jspBody.invoke(sw);
// 获取到标准JSP输出流对象
JspWriter out = this.getJspContext().getOut();
// 获取到缓存中所存放的标签文本
String tagText = sw.toString();
// 将文本数据转换为大写字母
String upperCase = tagText.toUpperCase();
// 输出到浏览器
out.write(upperCase);
}
}
//有属性的标签处理器
public class IfSimpleTag extends SimpleTagSupport {
private boolean test;
public void setTest(boolean test) {
this.test = test;
}
@Override
public void doTag() throws JspException, IOException {
if (test) {
//没有指定输出流,则默认写入到JspWriter中
this.getJspBody().invoke(null);
}
}
}
- 2、注册标签处理器
在WEB-INF目录下,新建一个扩展名为.tld的xml文件。
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>mytag</short-name>
<uri>http://www.Tooi.com/jsp/customtags</uri>
<!-- 注册没有标签体的标签处理器 -->
<tag>
<name>clientIp</name>
<tag-class>com.Tooi.SimpleTag.ClientIpSimpleTag</tag-class>
<body-content>empty</body-content>
</tag>
<!-- 注册有标签体的标签处理器 -->
<tag>
<name>toUpperCase</name>
<tag-class>com.Tooi.SimpleTag.ToUpperCaseSimpleTag</tag-class>
<body-content>scriptless</body-content>
</tag>
<!-- 注册有属性的标签处理器 -->
<tag>
<name>if</name>
<tag-class>com.Tooi.SimpleTag.ToUpperCaseSimpleTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
<body-content>: 有4个选项:
empty:表示没有标签体。
scriptless:表示有标签体,版标签不能包含Java代码与Java表达式。可以包含EL表达式。
JSP:表示会将标签体的文本内容原样显示在浏览器,只要继承TagSupport类可以使用,继承SimpleTagSupport类使用会报错。
tagdepengent:会将标签体原样显示在浏览器,EL表达式也会显示原样。
有属性的标签必须添加<attribute/>子标签:
<name/>:指定属性名,标签处理器必须有相同的属性名。
<required/>:指定该属性是否是必须的,true为必须。
<rtexprvalue/>:指定该属性值是否可以来自运行时表达式的值,true表示可以。
- 3、使用自定义标签
<!-- 导入自定义标签库 -->
<%@ taglib uri="http://www.Tooi.com/jsp/customtags" prefix="mytag" %>
<!-- 使用自定义标签 -->
<mytag:clientIp/>
<%
pageContext.setAttribute("name", "Tooi");
%>
<mytag:toUpperCase>name= ${name }</mytag:toUpperCase>
<%
pageContext.setAttribute("male", true);
%>
<mytag:if test="${male }">男</mytag:if>
- 标签处理器实例的生命周期
标签处理器是是多例的。每执行一次自定义标签,都会创建一个标签处理器的实例。该标签执行完毕,则标签处理器实例销毁。
七、JSTL
JSP Standard Tag Library,即JSP标准标签库
核心标签库: 主要用于完成基本的逻辑运算。
格式化标签库: 主要用于完成日期、数字的格式化显示。
EL函数标签库: 定义螺杆EL函数。
SQL操作标签库: 完成SQL操作,不使用了,已经有Java代码完成。
XML操作标签库: 不使用,由Java代码完成
- 核心标签库
c:set 用于变量的定义,并将变量放入指定的域属性空间;为Bean对象的属性赋值;为MAP的key与value赋值
<!-- 用于变量的定义,并将变量放入指定的域属性空间 -->
<c:set value="张三" var="name" scope="request" />
name = ${requestScope.name } <br>
<!-- 为Bean的属性赋值 -->
<jsp:useBean id="student" class="com.abc.beans.Student" scope="session"></jsp:useBean>
<c:set value="李四" property="name" target="${sessionScope.student }"/>
<c:set value="24" property="age" target="${sessionScope.student }"/>
student = ${sessionScope.student }<br>
<!-- 为MAP的key与value赋值 -->
<%
Map<String,String> map=new HashMap();
pageContext.setAttribute("map", map);
%>
<c:set value="男" property="gender" target="${map }"/>
<c:set value="1234567" property="mobile" target="${map }" />
gender = ${map.gender }<br>
monile = ${map.mobile }<br>
运行结果:
name = 张三
student = Student [name=李四, age=24]
gender = 男
monile = 1234567
c:remove 从域属性空间中删除变量。标签不常用。
<c:remove var="name" scpoe="request" />
name = ${requestScope.name }<br>
c:out 用于在页面上输出EL表达式的值,不常用。
<!-- c:out -->
<c:set var="city" value="<h2>北京<h2>" />
city1= <c:out value="${city }" /><br>
city2= <c:out value="${city }" escapeXml="false" /> <br>
country1= <c:out value="${country }" default="中国"/> <br>
country2= ${country } <br>
escapeXml:是否忽略HTML标签,默认为true,忽略按照原样输出
default:指定默认值,若要输出的变量不存在则输出默认值。
c:catch: 用于捕获异常,不常用。
<c:catch var="ex">
<!-- Student类没有score属性 -->
double score=${student.score }
</c:catch>
ex=${ex.meeeage }<br>
c:if: 用于对的条件的判断。
<c:set var="user" value="admin"></c:set>
<c:if test="${user=='admin' }" >
<a href="">进入管理页面</a>
</c:if>
c:choose: 实现多分支判断
<c:choose>
<%--共一页的情况 --%>
<c:when test="${pageCount==1 }">
首页 上一页 下一页 末页
</c:when>
<!-- 当前是第一页的情况 -->
<c:when test="${pageNo==1 }">
首页 上一页 <a href="#">下一页</a> <a href="#">末页</a>
</c:when>
<!-- 当前是最后一页的情况 -->
<c:when test="${pageNo==pageCount }">
<a href="#">首页</a> <a href="#">上一页</a> 下一页 末页
</c:when>
<!-- 当前是中间页的情况 -->
<c:otherwise>
<a href="#">首页</a> <a href="#">上一页</a> <a href="#">下一页</a> <a href="#">末页</a>
</c:otherwise>
</c:choose>
c:forEach:
<%
Object[] citys={"北京","上海","广州"};
pageContext.setAttribute("citys", citys);
%>
<c:forEach items="${citys }" var="city">
${city }<br>
</c:forEach>
<!-- 指定遍历索引,从0开始 -->
<%
Object[] countrys={"0中国","1美国","2德国","3英国","4法国",,"5日本","6韩国"};
pageContext.setAttribute("countrys", countrys);
%>
<c:forEach items="${ countrys }" var="country" begin="1" end="2">
${country }<br>
</c:forEach>
<!-- step:指定遍历时的步长 -->
<c:forEach items="${ countrys }" var="country" begin="1" end="5" step="2">
${country }<br>
</c:forEach>
运行结果:
1美国
2英国
5日本
<!-- 有很多高级应用,自行百度。 -->
- 格式化标签库
fmt:formatDate: 用于使用不同的模式格式化日期
<%
Date now = new Date();
request.setAttribute("now", now);
%>
<form action="">
<!-- 直接显示在页面 -->
<fmt:formatDate value="${now }" pattern="yyy-MM-dd"/> <br>
<!-- 格式化的结果显示在表单元素中 -->
<fmt:formatDate value="${now }" var="birth" pattern="yyyy-MM-dd"/>
<input type="text" name="birthday" value="${birth }">
</form>
value:将要格式化的数据
pattern:格式化的模式。
var:格式化后字符串所存放的变量名。
scpoe:变量存放的位置。默认page。
fmt:paresDate: 用于将指定的字符串转换为日期类型。
<!-- 转换后的日期类型直接显示在页面 -->
<fmt:parseDate value="1949-10-01" pattern="yyy-MM-dd"></fmt:parseDate> <br>
<!-- 将指定的日期字符串转换为日期类型,并存放到指定的变量中 -->
<fmt:parseDate value="1949-10-01" var="countryBirth" pattern="yyyy-MM-dd"></fmt:parseDate>
counntryBirth = ${countryBirth }<br>
fmt:formatNumber: 用于按照格式对数字进行格式化。
<!-- 格式化后结果直接显示在页面 -->
<fmt:formatNumber value="${12345.678 }" minIntegerDigits="6"
maxFractionDigits="2"></fmt:formatNumber>
<br>
<fmt:formatNumber value="${12345 }" minFractionDigits="2"
groupingUsed="false"></fmt:formatNumber>
fmt:parseNumber: 用于将指定字符串转换为数值类型
<!-- 将解析后的结果直接显示 -->
<fmt:parseNumber value="123,456.789" integerOnly="true"></fmt:parseNumber><br>
<fmt:parseNumber value="123,456.789" integerOnly="false"></fmt:parseNumber><br>