- 它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。
- 相比servlet而言,servlet很难对数据进行排版,而jsp除了可以用java代码产生动态数据的同时,也很容易对数据进行排版。
2.jsp最佳实践
- 让jsp既用java代码产生动态数据,又做美化会导致页面难以维护。
- 让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。
- 因此最好的办法就是根据这两门技术的特点,让它们各自负责各的,servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示jsp来做。
1. Jsp模板元素
JSP页面中的html内容称之为 JSP模板元素。
JSP模板元素定义了网页的基本骨架,即定义了页面的结构和外观。
2.JSP脚本表达式
JSP脚本表达式用于将程序数据输出到客户端的语法<%=变量表达式%>
<%=new Date().toLocaleString()%>
- JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。
- JSP脚本表达式中的变量或表达式后面不能有分号(;)。
3.脚本片段
<%
多行java代码
%>
- 注意:JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。
-
JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。
- 在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素
- 单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句,例如:
<% for(int i=0;i<5;i++){ %> <strong>hello~</strong> <% } %>
将hello打印5遍
3.多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。
4.JSP声明
<%!
java代码
%>
- 所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法 。
- 多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。
- JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。
<body> <%!int a=0 ;%> <% int b=0; b++; a++; %> <%=a %> <%=b %> </body>
5. 注释
注释格式:<%--注释信息--%>
6.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页面的起始位置。
session属性:说明是否使用session,默认为true被翻译的Servlet会自动获得Session,如果该值被指定为false,被翻译的Servlet的service方法不能获取Session对象。
errorPage属性:显示错误页面errorPage="/errors/error.jsp"
通过配置web.xml可以配置错误页面
<!-- 配置全局错误页面 --> <error-page> <exception-type>java.lang.ArithmeticException</exception-type> <location>/errors/error.jsp</location> </error-page> <!-- 配置错误状态码页面 --> <error-page> <error-code>404</error-code> <location>/errors/error.jsp</location> </error-page>
pageEncoding属性:通知jsp引擎在翻译jsp的过程以什么编码方式来解码jsp文件,通知Servlet引擎response编码方式。简单地说:jsp引擎在翻译jsp时,就会自动加一句response.setContextType()
include指令:
include指令用于引用其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个Servlet,所以include指令引入通常也称之为静态引入。
语法%@include file="relativeURL"%
示例:
<%@include file="/public/head.jsp"%> 这是内容!!!<br/> %@include file="/public/foot.jsp"%
其中的file属性用于指定引入文件的相对路径。file属性的设置值必须使用相对路径,如果以"/"开头,表示相对于当前web应用的根目录。
include细节:
1.被引入的文件必须遵循JSP语法。
2.被引入的文件可以使用任意的扩展名,即使其扩展名和html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见名知意,JSP规范建议使用.jspf作为静态引入文件的扩展名
3.由于使用include指令将会涉及两个JSP页面,并会把两个JSP翻译成一个Servlet,所以这两个JSP页面的指令不能冲突。(除了pageEncoding和导包除外)
4.模板元素不要冲突。
每个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大对象的引用。
九大隐式对象
request、response、session、config、application、out、page、pageContext、Excepiton
out隐式对象:
out隐式对象用于向客户端发送文本数据。
out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。
- 设置page指令的buffer属性关闭了out对象的缓存功能
-
out对象的缓冲区已满
-
整个JSP页面结束
pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其他8大隐式对象的引用,它自身还是一个域对象,可以用来保存数据,而且,这个对象还封装了web开发中常用的操作,例如引入和跳转其它资源,检索其它域对象中的属性。
通过pageContext获得其它对象
<body> <% String data="hello"; request.setAttribute("data",data); %> <%--在脚本表达式内要获取request域内的值有两种方法 第一种直接通过request第二种通过pageContext对象的重载方法获取 --%> <%=request.getAttribute("data") %> <%=pageContext.getAttribute("data",pageContext.REQUEST_SCOPE)%> <%--在el表达式内也有两种方式,第二种方式有限制 --%> ${requestScope.data } ${data } </body>
原则: 四个域对象在选择的时候,能用范围小的绝不用范围大的
page: 数据只是暂时存在集合,在jsp页面的其他地方要用,用page(页面中自定义的map)
什么时候需要用map了,就用page
request:数据只是做显示的,看完了就没用了,就存request域
请求转发, Servlet 产生的处理结果(数据) 交给jsp显示,
session: 数据给用户看完了,一会还要用,会话结束了就没用了
用户登陆,用户信息发给客户端看,看完了,一会访问别的页面还要看用户信息
购物车,购物成功了,给用户看购物车,待会随时可以查看购物车
请求重定向,因为是两次请求,第一次请求的数据,第二次请求还要看
application : 数据给一个用户用完了,别人还要用
聊天室,聊天记录,需要给所有的用户看
统计网站在线人数,所有人看到的应该是一个数
总结: 需要定义Map不如用page,请求转发Servlet带给jsp的数据存request 请求重定向带过去的数据存Session,全局的数据存application
虽然我们希望JSP页面仅用作数据显示模板,不要嵌套任何Java代码引入任何业务逻辑,但在实际开发中不引入一点业务逻辑是不可能的,但是引入业务逻辑会导致页面出现难看的Java代码,怎么办?
Sun公司允许用户开发自定义标签封装页面的Java代码,以便jsp页面不出现一行java代码,当然sun公司在jsp页面中也内置了一些标签,这些标签叫做jsp标签,开发人员使用这些标签可以完成页面的一些常用业务逻辑。
JSP标签也称之为Jsp Action元素,他用于在jsp页面中提供业务逻辑功能。
JSP常用标签
<jsp:include>标签
<jsp:include page="/public/head.jsp"></jsp:include>
- <jsp:include>标签是动态引入, <jsp:include>标签涉及到的2个JSP页面会被翻译成2个servlet,这2个servlet的内容在执行时进行合并。
-
而include指令是静态引入,涉及到的2个JSP页面会被翻译成一个servlet,其内容是在源文件级别进行合并。
-
不管是<jsp:include>标签,还是include指令,它们都会把两个JSP页面内容合并输出,所以这两个页面不要出现重复的HTML全局架构标签,否则输出给客户端的内容将会是一个格式混乱的HTML文档。
-
<jsp:include>标签:使用page属性指定被引入资源。include指令:使用file属性指定被引入资源。
<jsp:forward>标签:用于把请求转发给另外一个资源
语法:<jsp:forward page="relativeURL|<%=expression%/>
page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。
<jsp:include page="/index.jsp"></jsp:include>
<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:forward>
<jsp:param>标签的name属性用于指定参数名,value属性用于指定参数值。在<jsp:include>和<jsp:forward>标签中可以使用多个<jsp:param>标签来传递多个参数。
虚拟目录映射:
<servlet> <servlet-name>xxx</servlet-name> <jsp-file>/1.jsp</jsp-file> </servlet> <servlet-mapping> <servlet-name>xxx</servlet-name> <url-pattern>/1.html</url-pattern> </servlet-mapping>
EL全名Expression Language;
EL主要作用:
1.获取数据: 表达式主要用于替换JSP页面中的脚本表达式,以从各种类型web域中检索Java对象、获取数据。
EL表达式与脚本表达式区别:EL 只能获取域中的数据。脚本表达式则可以直接获取定义的变量
<%int a=0;%> a= <%=a %><br/> a=${a}
结果:
a=0
a=
EL表达式在执行时,会调用pageContext.findAttribute方法,用标识符为关键字,分别从page,request,session、application四个域中查找相应的对象,找到则返回相应对象,找不到则返回空字符串 "";
<% pageContext.setAttribute("name","pageContext"); request.setAttribute("name","request"); session.setAttribute("name","session"); application.setAttribute("name","servletContext"); %> <!-- EL表达式获取数据 --> ${pageScope.name}<br/> ${requestScope.name}<br/> ${sessionScope.name}<br/> ${applicationScope.name}<br/> </body>
EL还可以访问javabean的属性,访问list集合。访问map集合,访问数组
<body> <!-- 获取数组中的数据 --> <% int[] arr={1,2,3,4,5}; //使用EL获取数据,数据必须保存到四个数据范围内 pageContext.setAttribute("arr",arr); %> ${arr}<br/><!-- [I@114b420 --> ${arr[2]}<br/> <!-- 获取Map集合中的数据 --> <% Map<String,String> map=new HashMap<String,String>(); map.put("1111","aaa"); map.put("2222","bbbb"); map.put("3333","ccc"); pageContext.setAttribute("map",map); %> ${map["1111"]}<br/> <!--获取对象中的数据--> <% User user=new User(); Address address=new Address(); address.setCity("北京"); user.setAddress(address); pageContext.setAttribute("user",user); %> ${user.address.city } </body>
2.执行运算
利用 表达式可以在 页面执行一些基本的关系运算,逻辑运算和算术运算,
语法格式:${运算表达式}
<body> <% pageContext.setAttribute("a",10); session.setAttribute("b",20); %> ${a-b}<br/> ${a>b}<br/><!-- false --> <% //empty 检查是否为空 pageContext.setAttribute("name",""); %> ${empty name }<br/> <!-- true --> </body>
3.获取web开发常用对象:
EL表达式定义了一些隐式对象,利用这些隐式对象,web开发人员可以很轻松获得对web常用对象的引用,从而获得这些对象中的数据。
EL表达式语言中定义了11个隐含对象。
语法${隐式对象名称}
<body> <%--通过EL pageContext获得其它八个内置对象 --%> <%=pageContext.getRequest() %> ${pageContext.request} <%--通过EL表达式获得对象中的数据 --%> ${requestScope.name}<!-- request.getAttribute("name") --> ${pageContext.request.name}<!-- pageContext.getRequest().getName() --> <%--获取请求参数 --%> <% request.getParameter("username"); %> ${param.username } <% request.getParameterValues("interest"); %> ${paramValues.interest } ${paramValues.interest[0] } <%--获取头 --%> <% request.getHeader("referer"); %> ${header.referer } <% request.getHeaders("accept-language"); %> ${headerValues["accept-language"]} </body>
获取Cookie对象
<body> <% Cookie cookie=new Cookie("company","google"); cookie.setMaxAge(60*60); response.addCookie(cookie); %> <!-- JSP读取cookie --> <% Cookie[] cookies=request.getCookies(); for(int i=0;cookies!=null&&i<cookies.length;i++){ if(cookies[i].getName().equals("company")){ String value=cookies[i].getValue(); out.write(value); } } %> <!-- EL表达式读取cookie --> ${cookie.company.value} </body>
4.调用Java方法
EL表达式允许用户开发自定义EL函数,以在JSP页面中通过EL表达式调用Java类的方法。
在EL表达式中调用的只能是Java类的静态方法。
这个Java类的静态方法需要在TLD文件中描述,才可以被EL表达式调用
EL自定义函数用于扩展EL表达式的功能,可以让EL表达式完成普通Java程序代码所能完成的功能。
一般来说 EL自定义函数开发与应用包括三个步骤:
1.编写一个Java类的静态方法。
2.编写标签库描述符(tld文件),在tld文件中描述自定义函数。
3.在JSP页面中导入和使用自定义函数
示例:开发对html标签进行转义的el function
编写一个类,定义静态方法
public class HTMLFilter { public static String filter(String message) { if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(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()); } }
使用向导创建tld文件
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" 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"> <tlib-version>1.0</tlib-version> <short-name>myel</short-name> <uri>http://www.itcast.cn/el</uri> <function> <name>myfilter</name><!-- 起名,在EL调用时调用 --> <function-class>cn.itcast.until.HTMLFilter</function-class> <function-signature>java.lang.String filter(java.lang.String)</function-signature><!-- 方法签名 --> </function> </taglib>
在JSP中使用函数
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib uri="http://www.itcast.cn/el" prefix="myel"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> ${myel:myfilter('<a href="http://www.baidu.com">百度</a>')} </body> </html>
注意事项:
1.编写完标签库描述文件后,需要将它放置到<web应用>\WEB-INF目录中或WEB-INF目录下的除了classes和lib目录之外的任意子目录中。
3.<function>元素用于描述一个EL自定义函数,其中:
由于在JSP页面中显示数据时,经常需要对显示的字符串进行处理,这些EL函数在JSTL开发包中进行描述,因此在JSP页面中使用SUN公司的EL函数库,需要导入JSTL开发包,并在页面中导入EL函数库,如下所示:
在页面中使用JSTL定义的EL函数:
<%@taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
fn:toLowerCase函数将一个字符串中包含的所有字符转换为小写形式,并返回转换后的字符串,它接收一个字符串类型的参数,例如
fn:toLowerCase("Www.IT315.org") 的返回值为字符串“www.it315.org”
fn:toLowerCase("")的返回值为空字符串
n:toUpperCase函数将一个字符串中包含的所有字符转换为大写形式,并返回转换后的字符串,它接收一个字符串类型的参数。例如:
fn:toUpperCase("Www.IT315.org") 的返回值为字符串“WWW.IT315.ORG”
fn:toUpperCase("")的返回值为空字符串
fn:trim函数删除一个字符串的首尾的空格,并返回删除空格后的结果字符串,它接收一个字符串类型的参数。需要注意的是,fn:trim函数不能删除字符串中间位置的空格。
例如,fn:trim(" www.it315.org ") 的返回值为字符串“www.it 315.org”。
fn:length函数返回一个集合或数组大小,或返回一个字符串中包含的字符的个数,返回值为int类型。fn:length函数接收一个参数,这个参数可以是<c:forEach>标签的items属性支持的任何类型,包括任意类型的数组、java.util.Collection、java.util.Iterator、java.util.Enumeration、java.util.Map等类的实例对象和字符串。
如果fn:length函数的参数为null或者是元素个数为0的集合或数组对象,则函数返回0;如果参数是空字符串,则函数返回0。
fn:split函数以指定字符串作为分隔符,将一个字符串分割成字符串数组并返回这个字符串数组。
fn:split函数接收两个字符串类型的参数,第一个参数表示要分割的字符串,第二个参数表示作为分隔符的字符串。
例如,fn:split("www.it315.org", ".")[1]的返回值为字符串“it315”。
fn:join函数以一个字符串作为分隔符,将一个字符串数组中的所有元素合并为一个字符串并返回合并后的结果字符串。fn:join函数接收两个参数,第一个参数是要操作的字符串数组,第二个参数是作为分隔符的字符串。
如果fn:join函数的第二个参数是空字符串,则fn:join函数的返回值直接将元素连接起来。例如:
假设stringArray是保存在Web域中的一个属性,它表示一个值为{"www","it315","org"}的字符串数组,则fn:join(stringArray, “.")返回字符串“www.it315.org”
fn:join(fn:split("www,it315,org", ","), ".") 的返回值为字符串“www.it315.org”
fn:indexOf函数返回指定字符串在一个字符串中第一次出现的索引值,返回值为int类型。fn:indexOf函数接收两个字符串类型的参数,如果第一个参数字符串中包含第二个参数字符串,那么,不管第二个参数字符串在第一个参数字符串中出现几次,fn:indexOf函数总是返回第一次出现的索引值;如果第一个参数中不包含第二个参数,则fn:indexOf函数返回-1。如果第二个参数为空字符串,则fn:indexOf函数总是返回0。例如:
fn:indexOf("www.it315.org","t3") 的返回值为5
fn:contains函数检测一个字符串中是否包含指定的字符串,返回值为布尔类型。fn:contains函数在比较两个字符串是否相等时是大小写敏感的。 fn:contains函数接收两个字符串类型的参数,如果第一个参数字符串中包含第二个参数字符串,则fn:contains函数返回true,否则返回false。如果第二个参数的值为空字符串,则fn:contains函数总是返回true。
实际上,fn:contains(string, substring)等价于fn:indexOf(string, substring) != -1。
忽略大小的EL函数:fn:containsIgnoreCase
fn:startsWith函数用于检测一个字符串是否是以指定字符串开始的,返回值为布尔类型。
fn:startsWith函数接收两个字符串类型的参数,如果第一个参数字符串以第二个参数字符串开始,则函数返回true,否则函数返回false。如果第二个参数为空字符串,则fn:startsWith函数总是返回true。例如:
fn:startsWith("www.it315.org","it315")的返回值为false
与之对应的EL函数:fn:endsWith
fn:replace函数将一个字符串中包含的指定子字符串替换为其它的指定字符串,并返回替换后的结果字符串。fn:replace方法接收三个字符串类型的参数,第一个参数表示要操作的源字符串,第二个参数表示源字符串中要被替换的子字符串,第三个参数表示要被替换成的字符串。例如:
fn:replace("www it315 org", " ", ".")的返回值为字符串“www.it315.org”
fn:substring函数用于截取一个字符串的子字符串并返回截取到的子字符串。fn:substring函数接收三个参数,第一个参数是用于指定要操作的源字符串,第二个参数是用于指定截取子字符串开始的索引值,第三个参数是用于指定截取子字符串结束的索引值,第二个参数和第三个参数都是int类型,其值都从0开始。例如:
fn:substring("www.it315.org", 4, 9) 的返回值为字符串“it315”
fn:substringAfter函数用于截取并返回一个字符串中的指定子字符串第一次出现之后的子字符串。fn:substringAfter函数接收两个字符串类型的参数,第一个参数表示要操作的源字符串,第二个参数表示指定的子字符串,例如:
fn:substringAfter(“www.it315.org”, “.”)的返回值为字符串“it315.org”。
与之对应的EL函数为:fn:substringBefore
<%--大写转换为小写 --%> ${fn:toLowerCase("ABCDE") }<br/><%--abcde--%> <%--小写转换为大写 --%> ${fn:toUpperCase("abcde") }<br/><%--ABCDE--%> <%--trim求掉两边的空格,注意不能去掉中间的空格 --%> ${fn:trim(" ab cde ") }<br/><%--ab cde--%> <%--length 返回一个集合和数组的长度,或者一个字符串的字符个数 如果fn:length函数的参数为null或者是元素个数为0的集合或数组对象, 则函数返回0;如果参数是空字符串,则函数返回0。 --%> <% int[] arr=new int[20]; pageContext.setAttribute("arr",arr); %> ${fn:length(arr)}<br/><%--20 如果arr不存在则返回0--%> <%-- fn:split函数以指定字符串作为分隔符,将一个字符串分割成字符串数组并返回这个字符串数组。 fn:split函数接收两个字符串类型的参数,第一个参数表示要分割的字符串,第二个参数表示作为分隔符的字符串。 --%> <%-- fn:join函数以一个字符串作为分隔符,将一个字符串数组中的所有元素合并为一个字符串并返回合并后的结果字符串。 fn:join函数接收两个参数,一个参数是要操作的字符串数组,第二个参数是作为分隔符的字符串。 --%> <% String[] strs={"www","baidu","com"}; pageContext.setAttribute("strs",strs); %> ${fn:join(strs,".") }<br/><%--www.baidu.com --%> ${fn:join(fn:split("www.baidu.com","."),".") }<br/><%--www.baidu.com --%> ${fn:indexOf("www.baidu.com","ba") }<br/><%--4--%> ${fn:substringAfter("www.baidu.com",".") }<br/><%--baidu.com --%> ${fn:substringBefore("www.baidu.com",".") }<br/><%--www --%>