JSP技术
JSP概述
JSP(Java Server Pages),建立在Servlet规范之上的动态网页开发技术。与HTML页面最大的不同是在JSP文件中,HTML代码与JAVA可以共同存在,Java代码可以实现页面动态内容的显示。
JSP运行原理
当客户端发送一个.jsp文件的请求时,容器如果找到相应的.jsp文件会将.jsp文件转化为java文件,最终生成一个Servlet实例响应给客户端,从jsp文件到servlet的转换是一个动态的过程,转化发生在第一次请求时,并不是容器一开始就会将.jsp文件转化为servlet实例,对jsp文件的访问本质上是一个对servlet实例的访问。
前面在学习tomcat目录结构的时候,work目录是用来存放运行时jsp文件编译的代码,接下来通过对.jsp编译后的文件进行分析。在idea中,每个tomcat都是一个主tomcat的副本,在idea的tomcat启动日志中,如下图示例,可以查看该副本所在的位置,从而可以找到work目录下。
index.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
Hello,world!
<%
out.write("Change the world!");
%>
</body>
</html>
index_jsp.java类
可以看到,index_jsp这个类是继承子HttpJspBase的,而HttpJspBase类(可以在tomcat源码目录src\java\org\apache\jasper\runtime
路径查看到)是继承HttpServlet的,所以HttpJspBase类是一个Servlet,而index_jsp又是继承HttpJspBase类的,所以index_jsp类也是一个Servlet,所以当浏览器访问服务器上的index.jsp页面时,其实就是在访问index_jsp这个Servlet,index_jsp这个Servlet使用_jspService这个方法处理请求。
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}
public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}
public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}
public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
return;
}
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.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("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>$Title$</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" Hello,world!\n");
out.write(" ");
out.write("Change the world!");
out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
仔细观察源码,可以看到,index_jsp默认将三个常用的包前缀为javax.servlet
包导入了jsp中,这也是为什么我们能够在jsp页面中使用servlet
相关的对象而不用导包,另外web为jsp提供了9大内置对象,上面展示了8个,都已经定义并初始化了,这也是为什么能够使用域对象及获取到相应值的原因。类中所有对html代码的处理都是通过out.write()
写出到浏览器中的。
在jsp中编写的java代码会原封不动地翻译成java代码,如<%out.print(“Hello,world!”);%>直接翻译成out.write(" Hello,world!\n");而HTML代码则会翻译成使用out.write("\r\n");的形式输出到浏览器。
JSP基础语法
JSP脚本元素是指嵌套在<%和%>
之中的一条或多条Java语句。通过JSP脚本元素可以使Java代码嵌入到HTML页面中。
JSP脚本元素主要包含如下3种类型
- JSP脚本片段
- JSP声明语句
- JSP表达式
JSP表达式
JSP脚本表达式(expression)用于将程序数据输出到客户端
语法:<%= 变量或表达式 %>
举例:输出当前系统时间:
<%= new java.util.Date() %>
JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。
JSP脚本表达式中的变量或表达式后面不能有分号(;)。
JSP脚本片段
JSP脚本片断(scriptlet)用于在JSP页面中编写多行Java代码。语法:
<%
**多行Java代码**
%>
注意事项:
1)在<% %>中可以定义变量、编写语句,JSP脚本片段不能定义方法。
2)JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。
3)JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束
4)在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。
实例:单个脚本片断中的Java语句可以是不完整的。
<%
int a = 10;
%>
hello,world!
<%
System.out.println(a);
%>
JSP声明
JSP声明语句用于声明变量和方法,语法格式:
<%!
定义的变量或方法等
%>
Jsp声明中的java代码被翻译到_jspService方法的外面,这也就决定了在JSP声明中不能使用隐式对象,因为JSP隐式对象的作用范围仅限于Servlet的_jspService方法。
在JSP声明语句中定义的都是成员方法、成员变量、静态方法、静态变量、静态代码块等。
JSP注释
在JSP中,注释有两大类:
显式注释:直接使用HTML风格的注释:<!--注释内容-->
隐式注释:直接使用JAVA的注释://
、/*..*/
JSP自己的注释:<%--注释内容--%>
这三种注释的区别
HTML的注释在浏览器中查看源文件的时候是可以看得到的,而JAVA注释和JSP注释在浏览器中查看源文件时是看不到注释的内容的,这就是这三种注释的区别。
JSP指令
JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。
在JSP 2.0规范中共定义了三个指令:
- page指令
- Include指令
- taglib指令
JSP指令的基本语法格式:<%@ 指令 属性名="值" %>
例如:
<%@ page contentType="text/html;charset=gb2312"%>
如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写。
例如:
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.Date"%>
也可以写作:
<%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%>
page指令
page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置。例如
JSP 2.0规范中定义的page指令的完整语法:
<%@ 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" ]
%>
1、page指令的import属性是唯一可以声明多次的属性,其他属性的声明只能出现一次。
可以在一条page指令的import属性中引入多个类或包,其中的每个包或类之间使用逗号(,)分隔
在Jsp页面中,Jsp引擎会自动导入下面的包
- java.lang.*
- javax.servlet.*
- javax.servlet.jsp.*
- javax.servlet.http.*
例如:
<%@ page import="java.util.*,java.io.*,java.sql.*"%>
上面的语句也可以改写为使用多条page指令的import属性来分别引入各个包或类
例如:
<%@ page import="java.util.Date"%>
<%@ page import="java.io.*" %>
<%@ page import="java.sql.*" %>
2、page指令的errorPage属性
- errorPage属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前Web应用程序的根目录(注意不是站点根目录),否则,表示相对于当前页面
- 可以在web.xml文件中使用
<error-page>
元素为整个Web应用程序设置错误处理页面。 <error-page>
元素有3个子元素,<error-code>、<exception-type>、<location>
<error-code>
子元素指定错误的状态码,例如:<error-code>
404</error-code>
<exception-type>
子元素指定异常类的完全限定名,例如:<exception-type>java.lang.ArithmeticException</exception-type>
<location>
子元素指定以“/”开头的错误处理页面的路径,例如:<location>/ErrorPage/404Error.jsp</location>
- 如果设置了某个JSP页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用。
使用page指令的的isErrorPage属性显式声明页面为错误页面
如果某一个jsp页面是作为系统的错误处理页面,那么建议将page指令的isErrorPage属性(默认为false)设置为**“true”**来显式声明这个Jsp页面是一个错误处理页面。
例如:将error.jsp页面显式声明为错误处理页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isErrorPage="true"%>
<html>
<head>
<title>错误信息友好提示页面</title>
</head>
<body>
对不起,出错了,请联系管理员解决!
</body>
</html>
将error.jsp页面显式声明为错误处理页面后,有什么好处呢,好处就是Jsp引擎在将jsp页面翻译成Servlet的时候,在Servlet的 _jspService方法中会声明一个exception对象,然后将运行jsp出错的异常信息存储到exception对象中,如下所示:
由于Servlet的_jspService方法中声明了exception对象,那么就可以在error.jsp页面中使用exception对象,这样就可以在Jsp页面中拿到出错的异常信息了,如下:
如果没有设置isErrorPage=“true”,那么在jsp页面中是无法使用exception对象的,因为在Servlet的_jspService方法中不会声明一个exception对象,如下所示:
Jsp有9大内置对象,而一般情况下exception对象在Jsp页面中是获取不到的,只有设置page指令的isErrorPage属性为**“true”**来显式声明Jsp页面是一个错误处理页面之后才能够在Jsp页面中使用exception对象。
include指令
在JSP中对于包含有两种语句形式:
@include
指令<jsp:include>
指令
@include
可以包含任意的文件,当然,只是把文件的内容包含进来。
include指令用于引入其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为静态引入。
语法:<%@ include file=“relativeURL”%>,其中的file属性用于指定被引入文件的路径。路径以“/”开头,表示代表当前web应用。
include指令细节注意问题:
- 被引入的文件必须遵循JSP语法。
- 被引入的文件可以使用任意的扩展名,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见明知意,JSP规范建议使用.jspf(JSP fragments(片段))作为静态引入文件的扩展名。
- 由于使用include指令将会涉及到2个JSP页面,并会把2个JSP翻译成一个servlet,所以这2个JSP页面的指令不能冲突(除了pageEncoding和导包除外)。
使用@include可以包含任意的内容,文件的后缀是什么都无所谓。这种把别的文件内容包含到自身页面的@include语句就叫作静态包含,作用只是把别的页面内容包含进来,属于静态包含。
jsp:include指令
jsp:include指令为动态包含,如果被包含的页面是JSP,则先处理之后再将结果包含,而如果包含的是非*.jsp文件,则只是把文件内容静态包含进来,功能与@include类似。后面再具体介绍
JSP隐式对象
JSP运行原理
每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理。JSP引擎先将JSP翻译成一个_jspServlet
(实质上也是一个servlet) ,然后按照servlet的调用方式进行调用。
由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但第二次访问,JSP引擎如果发现JSP没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会受到影响。
JSP引擎在调用JSP对应的_jspServlet
时,会传递或创建9个与web开发相关的对象供_jspServlet
使用。JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用。
9个内置对象
NO. | 内置对象 | 类型 |
---|---|---|
1 | pageContext | javax.servlet.jsp.PageContext |
2 | request | javax.servlet.http.HttpServletRequest |
3 | response | javax.servlet.http.HttpServletResponse |
4 | session | javax.servlet.http.HttpSession |
5 | application | javax.servlet.ServletContext |
6 | config | javax.servlet.ServletConfig |
7 | out | javax.servlet.jsp.JspWriter |
8 | page | java.lang.Object |
9 | exception | java.lang.Throwable |
9个隐式对象
pageContext:可以通过成员方法得到其他内置对象
getException方法返回exception
getPage方法返回page
getRequest方法返回request
getResponse方法返回response
getServletConfig方法返回config
getServletContext方法返回application
getSession方法返回session
getOut方法返回out
application
config
session
request
response
page
out
exception
request,response,session,application,config这些对象在前面都已经作了详细的介绍,这里重点介绍一下剩下的pageContext对象,out对象,page对象。
page对象
当前页面转化为servlet的实例。可以理解为当前的页面对象,开发中不常用,了解即可。
out对象
out对象用于向客户端发送文本数据。out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。
JSP页面中的out对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。
只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:
- 设置page指令的buffer属性关闭了out对象的缓存功能
- out对象的缓冲区已满
- 整个JSP页面结束
out对象的工作原理:
pageContext对象
pageContext作为域对象
pageContext对象可以作为容器来使用,因此可以将一些数据存储在pageContext对象中。
pageContext对象的常用方法
public void setAttribute(String name,Object value);
public Object getAttribute(String name);
public void removeAttribute(String name);
/*
* Searches for the named attribute in page, request, session (if valid),
* and application scope(s) in order and returns the value associated or
* null.
*/
public Object findAttribute(java.lang.String name);
findAttribute(String name)
:当要查找某个属性时,findAttribute方法按照查找顺序==“page→request→session→application”==在这四个对象中去查找,只要找到了就返回属性值,如果四个对象都没有找到要查找的属性,则返回一个null。
实例:使用pageContext对象的findAttribute(String name)
查找个域内的值
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%
pageContext.setAttribute("a", "AA");
request.setAttribute("b", "BB");
session.setAttribute("c", "CC");
application.setAttribute("d", "DD");
%>
<%
String a = (String) pageContext.findAttribute("a");
String b = (String) pageContext.findAttribute("b");
String c = (String) pageContext.findAttribute("c");
String d = (String) pageContext.findAttribute("d");
String e = (String) pageContext.findAttribute("e");
%>
<h1>pageContext.findAttribute方法查找到的属性值:</h1>
<h3>a==><%=a%></h3>
<h3>b==><%=b%></h3>
<h3>c==><%=c%></h3>
<h3>d==><%=d%></h3>
<h3>e==>查找不存在的属性<%=e%></h3>
<hr>
<h1>使用EL表达式进行输出:</h1>
<h3>a==>${a}</h3>
<h3>b==>${b}</h3>
<h3>c==>${c}</h3>
<h3>d==>${d}</h3>
<h3>e==>查找不存在的属性${e}</h3>
</body>
</html>
EL表达式语句在执行时,会调用pageContext.findAttribute方法,用标识符为关键字,分别从page、request、 session、application四个域中查找相应的对象,找到则返回相应对象,找不到则返回”” (注意,不是null,而是空字符串)。
pageContext对象中封装了访问其它域的方法
public Object getAttribute(String name,int scope);
public void setAttribute(String name, Object value,int scope);
public void removeAttribute(String name,int scope);
代表各个域的常量
PageContext.APPLICATION_SCOPE
PageContext.SESSION_SCOPE
PageContext.REQUEST_SCOPE
PageContext.PAGE_SCOPE
使用pageContext访问其他域对象里面的值
String a1 = (String) pageContext.getAttribute("b", PageContext.REQUEST_SCOPE);
除了上述介绍,pageContext还有引入和跳转到其他web资源的功能。
隐式对象属性范围
JSP中存在的4大范围对象:
pageContext(page)
request
session
application
他们四对象公有的方法是
Object getAttribute(String name);
void setAttribute(String key, Object value);
void removeAttribute(String name);
通过范围对象设置的属性值是否可以在另一个页面中可以取得,这就是属性的作用范围,或者说属性的作用域。
范围大小:
- 当前页有效:一个属性只能在一个页面中取得,跳转到其他页面无法取得
- 一次请求有效:一个页面中设置的属性,只要经过了服务器跳转,则跳转之后的页面可以继续取得。
- 一次会话有效:一个用户设置的内容,只要是与此用户相关的页面都可以访问(一个会话表示一个人,这个人设置的东西只要这个人不走,就依然有效)
- 全局有效:在整个服务器上设置的属性,所有人都可以访问
JSP页面,如果出现EL表达式没有获取到request域中的值时,看看存放在域中的属性是否是二次请求了。
1、page属性范围(pageContext)
2、request属性范围
3、session属性范围
4、application属性范围
JSP动作元素
JSP动作元素用来控制JSP的行为,执行一些常用的JSP页面动作。通过动作元素可以实现使用多行Java代码能够实现的效果,如包含页面文件、实现请求转发等。
JSP动作元素包括:
-
<jsp:include>标签
-
<jsp:forward>标签
-
<jsp:param>标签
<jsp:include>
标签
<jsp:include>
标签用于把另外一个资源的输出内容插入进当前JSP页面的输出内容之中,这种在JSP页面执行时的引入方式称之为动态引入。
语法:
<jsp:include page="relativeURL | <%=expression%>" flush="true|false" />
page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得。
flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端。
<jsp:include>
标签与<%@ include ..%>
指令的区别
-
<jsp:include>
标签是动态引入,<jsp:include>
标签标签涉及到的2个JSP页面会翻译成2个servlet,这2个servlet的内容在执行时进行合并。 -
<%@ include ..%>
指令是静态引入,涉及到的2个JSP页面会翻译成1个servlet,其内容是是编译完成后进行合并,合并在同一个文件中。
下面看一个示例:
header文件中在JSP脚本段中定义了一个变量,index.jsp中也定义了相同的一个变量,当使用<%@ include ..%>
指令引入header文件时,提示变量已经存在,出现这种错误的原因就是静态引入,<%@ include ..%>
指令涉及的2个jsp会编译输出到一个文件中,而JSP脚本段的代码都会出现在_jspService()
方法中,这样就会导致变量重复定义的问题。
而采用<jsp:include ..%>
引入header.jsp就能解决上面这种问题,如下图
<jsp:include ..>
是将2个jsp文件分别进行编译,输出两个servlet相关的类,然后在将结果进行合并。两个变量在不同的类中,所以不会存在<%@ include ..%>
出现的错误。
<jsp:forward>
标签
<jsp:forward>
标签用于把请求转发给另外一个资源。
语法:
<jsp:forward page="relativeURL | <%=expression%>" />
page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。
示例:index.jsp跳转到header.jsp,并且通过<jsp:param>
传递参数,注意接受的时候使用request.getParamter,所以它不是放到域对象的属性中的。
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<jsp:forward page="header.jsp">
<jsp:param name="info" value='<%=URLEncoder.encode("你好", "utf-8")%>'/>
</jsp:forward>
</body>
</html>
header.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
header.jsp
req:<%=URLDecoder.decode(request.getParameter("info"), "utf-8")%>
</body>
</html>
<jsp:param>
标签
当使用<jsp:include>和<jsp:forward>
标签引入或将请求转发给其它资源时,可以使用<jsp:param>
标签向这个资源传递参数。
语法1:
<jsp:include page="relativeURL | <%=expression%>">
<jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>
语法2:
<jsp:forward page="relativeURL | <%=expression%>">
<jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>
<jsp:param>
标签的name属性用于指定参数名,value属性用于指定参数值。在<jsp:include>和<jsp:forward>
标签中可以使用多个<jsp:param>
标签来传递多个参数。
JSTL标签库
JSTL标签库的使用是为弥补html标签的不足,规范自定义标签的使用而诞生的。使用JSLT标签的目的就是不希望在jsp页面中出现java逻辑代码。
JSTL的使用和EL表达式也是分不开的。
JSTL标签库的分类
- 核心标签(用得最多)
- 国际化标签(I18N格式化标签)
- 数据库标签(SQL标签,很少使用)
- XML标签(几乎不用)
- JSTL函数(EL函数)
核心标签库
JSTL的核心标签库标签共13个,使用这些标签能够完成JSP页面的基本功能,减少编码工作。
从功能上可以分为4类:表达式控制标签、流程控制标签、循环标签、URL操作标签。
(1)表达式控制标签:out标签、set标签、remove标签、catch标签。
(2)流程控制标签:if标签、choose标签、when标签、otherwise标签。
(3)循环标签:forEach标签、forTokens标签。
(4)URL操作标签:import标签、url标签、redirect标签、param标签。
在JSP页面引入核心标签库的代码为:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
表达式控制标签
<c:out>标签
<c:out>
标签主要是用来输出数据对象(字符串、表达式)的内容或结果。
在使用Java脚本输出时常使用的方式为:<% out.println(“字符串”)%> 或者 <%=表达式%>
,在web开发中,为了避免暴露逻辑代码会尽量减少页面中的Java脚本,使用<c:out>
标签就可以实现以上功能。
<c:out value=”字符串”>
<c:out value=”EL表达式”>
JSTL的使用是和EL表达式分不开的,EL表达式虽然可以直接将结果返回给页面,但有时得到的结果为空,<c:out>
有特定的结果处理功能,EL的单独使用会降低程序的易读性,建议把EL的结果输入放入<c:out>
标签中。
**<c:out>标签
**标签的语法:
【语法1】:<c:out value=”要显示的数据对象” [escapeXml=”true|false”] [default=”默认值”]/>
【语法2】:<c:out value=”要显示的数据对象” [escapeXml=”true|false”]>默认值</c:out>
这两种方式没有本质的区别,只是格式上的差别。[escapeXml=”true|false”] [default=”默认值”]这些使用[]属性表示是不是必须的。
value :指定要输出的内容
escapeXml :是否将特殊字符进行html编码转换后输出
default :指定如果value属性的值为null时所输出的默认值
<c:set>
标签的功能
<c:set>
标签用于把某一个对象存在指定的域范围内,或者将某一个对象存储到Map或者JavaBean对象中。
语法格式:<c:set>
标签的编写共有4种语法格式。
语法1:存值,把一个值放在指定的域范围内。
<c:set value=”值1” var=”name1” [scope=”page|request|session|application”]/>
含义:把一个变量名为name1值为“值1”的变量存储在指定的scope范围内。
语法2:
<c:set var=”name2” [scope=”page|request|session|application”]>
值2
</c:set>
含义:把一个变量名为name2,值为值2的变量存储在指定的scope范围内。
语法3:
<c:set value=”值3” target=”JavaBean对象” property=”属性名”/>
含义:把一个值为“值3”赋值给指定的JavaBean的属性名。相当与setter()方法。
语法4:
<c:set target=”JavaBean对象” property=”属性名”>
值4
</c:set>
含义:把一个值4赋值给指定的JavaBean的属性名。
标签属性
流程控制标签
<c:if>
标签:功能等同于编程语言中的if,实现条件控制
<c:if>标签的语法
【语法1】:没有标签体内容(body)
<c:if test="testCondition" var="varName" [scope="{page|request|session|application}"]/>
【语法2】:有标签体内容
<c:if test="testCondition" [var="varName"] [scope="{page|request|session|application}"]>
标签体内容
</c:if>
【参数说明】:
(1)test属性用于存放判断的条件,一般使用EL表达式来编写。
(2)var属性用来存放判断的结果,类型为true或false。
(3)scopes属性用来指定var属性存放的范围。
<c:choose>、<c:when>和<c:otherwise>
标签实现if...else功能
<c:choose>、<c:when>和<c:otherwise>
,这3个标签通常情况下是一起使用的,<c:choose>
标签作为<c:when>
和<c:otherwise>
标签的父标签来使用。
语法
<c:choose>
<c:when test="条件1">
//业务逻辑1
<c:when>
<c:when test="条件2">
//业务逻辑2
<c:when>
<c:when test="条件n">
//业务逻辑n
<c:when>
<c:otherwise>
//业务逻辑
</c:otherwise>
</c:choose>
循环标签
<c:forEach>
实现对集合的循环迭代。
<c:forEach var="name" items="collection"
varStatus="statusName" begin="begin"
end="end" step="step">
</c:forEach>
【参数解析】:
(1)var设定变量名用于存储从集合中取出元素。
(2)items指定要遍历的集合。
(3)varStatus设定变量名,该变量用于存放集合中元素的信息。
(4)begin、end用于指定遍历的起始位置和终止位置(可选)。
(5)step指定循环的步长。
属性名称 | 是否支持EL表达式 | 属性类型 | 是否必须 | 默认值 |
---|---|---|---|---|
var | NO | String | 是 | 无 |
items | YES | Arrays|Collection|Iterator|Enumeration|Map|String [] | 是 | 无 |
begin | YES | int | 否 | 0 |
end | YES | int | 否 | 集合中最后一个元素 |
step | YES | int | 否 | 1 |
varStatus | NO | String | 否 | 无 |
varStatus的4个状态 | ||
---|---|---|
属性名 | 类型 | 说明 |
index | int | 当前循环的索引值 |
count | int | 循环的次数 |
frist | boolean | 是否为第一个位置 |
last | boolean | 是否为最后一个位置 |
URL操作标签
<c:param>
和<c:url>
、<c:redirect>
将value中的值放入var中的变量中,var中的变量根据scope存放到不同的域中,以便可以通过EL表达式直接使用。
EL表达式
EL 全名为Expression Language。EL主要作用:获取数据、执行运算、获取web域对象、快速获取域对象中的属性
获取数据
EL表达式获取数据语法格式:${表达式}
EL表达式语句在执行时,会调用pageContext.findAttribute方法,用标识符为关键字,分别从page、request、session、application四个域中查找相应的对象,找到则返回相应对象,找不到则返回”” (注意,不是null,而是空字符串)。
EL表达式可以很轻松获取JavaBean的属性,或获取数组、Collection、Map类型集合的数据
示例:注意一下EL表达式如何取复合对象中的引用和Map对象。
<html>
<head>
<title>el表达式获取数据</title>
</head>
<body>
<!-- 在jsp页面中,使用el表达式可以获取bean中引用的属性 -->
<%
Person person = new Person();
Address address = new Address();
person.setAddress(address);
request.setAttribute("person",person);
%>
${person.address.name}
<hr>
<!-- 在jsp页面中,使用el表达式获取list集合中指定位置的数据 -->
<%
Person p1 = new Person();
p1.setName("孤傲苍狼");
Person p2 = new Person();
p2.setName("白虎神皇");
List<Person> list = new ArrayList<Person>();
list.add(p1);
list.add(p2);
request.setAttribute("list",list);
%>
<!-- 取list指定位置的数据 -->
${list[1].name}
<!-- 迭代List集合 -->
<c:forEach var="person" items="${list}">
${person.name}
</c:forEach>
<hr>
<!-- 在jsp页面中,使用el表达式获取map集合的数据 -->
<%
Map<String,String> map = new LinkedHashMap<String,String>();
map.put("a","aaaa");
map.put("b","bbbb");
map.put("c","cccc");
map.put("1","1111");
request.setAttribute("map",map);
%>
<!-- 根据关键字取map集合的数据 -->
${map.c}
${map["1"]}
<hr>
<!-- 迭代Map集合 -->
<c:forEach var="me" items="${map}">
${me.key}=${me.value}<br/>
</c:forEach>
<hr>
</body>
</html>
执行运算
EL中的运算符
1、关系运算符
注:与字符串的比较形式为: ${value == 'string'}
,引号包裹
2、逻辑运算符:
3、empty运算符:检查对象是否为null(空),结果为布尔类型
基本语法格式:${empty var}
或者 ${empty(var)}
4、二元表达式:${user!=null?user.name :""}
5、[ ] 和 . 号运算符
点运算符用于访问JSP页面中某些对象的属性,例如${bean.property}
[]运算也是运用访问JPS页面中某些对象的属性,但是当获取的属性中包含特殊符号,如“-”或“?”等非字母或数字的符号(因为不符合EL表达式标识符定义),就只能使用[]运算符来访问改属性,语法格式如下:
${user["my-name"]}
//或者在获取http请求头的时候,只能使用[]运算符
${header["Accept-Cookie"]}
示例:
23 mod 5 = ${23 mod 5}<br>
user是否为空:${user == null}<br>
user是否为空:${empty user}<br>
<%
List<String> list = new ArrayList<String>();
list.add("gacl");
list.add("xdp");
request.setAttribute("list",list);
%>
<c:if test="${!empty(list)}">
<c:forEach var="item" items="${list}">
${item}
</c:forEach>
</c:if>
<br/>
<%
List<String> emptyList = null;
%>
<%--使用empty运算符检查对象是否为null(空) --%>
<c:if test="${empty(emptyList)}">
对不起,没有您想看的数据
</c:if><br/>
1+2 > 3: ${(1+2) gt 3 ? "成立":"不成立"}
EL隐式对象
EL表达式语言中定义了11个隐含对象,使用这些隐含对象可以很方便地获取web开发中的一些常见对象,并读取这些对象的数据。
隐式对象 | 作用 |
---|---|
pageContext | 对应于JSP页面中的pageContext对象 |
pageScope | 代表page域中用于保存属性的Map对象 |
requestScope | 代表request域中用于保存属性的Map对象 |
sessionScope | 代表session域中用于保存属性的Map对象 |
applicationScope | 代表application域中用于保存属性的Map对象 |
param | 表示一个保存了所有请求参数的Map对象 |
paramValues | 表示一个保存了所有请求参数的Map对象,它对于某个请求参数, 返回的是一个string类型数组 |
header | 表示一个保存了所有http请求头字段的Map对象 |
headerValues | 表示一个保存了所有http请求头字段的Map对象,返回string类型数组 |
cookie | 表示一个保存了所有cookie的Map对象 |
initParam | 表示一个保存了所有web应用初始化参数的map对象 |
EL隐式对象和JSP隐式对象的比较:
jsp中的四大域对象不等于EL表达式中的四大域对象,或者说EL表达式没有域对象
request≠requestScope,session≠sessionScope…
**pageScope,requestScope…**只是代表了用于保存域对象属性的Map对象。但是可以通过EL中的pageContext拿到JSP中的四大域对象。
在JSP中获取域对象有两种方法,一个是直接使用内置对象,二是通过pageContext.getXxx()
方法获取。
在EL中获取JSP中的域对象只能通过pageContext.xxx
方法
JSP:pageContext.getRequest()
||
EL:pageContext.request
JSP:pageContext.getSession()
||
EL:pageContext.session
...
转化方法:去掉JSP中的get、set方法,然后首字母小写。
示例:
<%
request.setAttribute("name", "Stronger");
%>
:从JSP四大域对象中查找
${name}<br>
${requestScope.name}
<br>characterEncoding:属性
${pageContext.request.characterEncoding}<br>
<br>requestScope.characterEncoding是不存在的,
requestScope并不代表域对象
${requestScope.characterEncoding}