Day12 JSP(Java Server Pages)
1.概述:
写jsp代码就像写html差不多,允许嵌套java代码——这点与html不同。访问jsp页面的时候,会生产一个.java文件(路径:C:\Users\WYH\.IntelliJIdea2017.3\system\tomcat\Tomcat_8_5_24_Day15_JSP_II&fileupload\work\Catalina\localhost\ROOT\org\apache\jsp的运行的tmocat实例的work目录下)
tomcat会同步生成一个.calss文件,保存在同一路径下。
经过长期的实践:人们习惯把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。
JSP中的Java代码如何执行的呢?
1.先找到jsp页面,然后tomcat会生成一个jsp对应的java文件编译成class字节码文件。2.然后加载class字节码文件。
3.调用jsp的 service方法。
4.然后产生结果,并把结果写回到client
见图:
.java文件中:
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent, org.apache.jasper.runtime.JspSourceImports {
.....很多方法
public void _jspService(final javax.servlet.http.HttpServletRequest request,
final javax.servlet.http.HttpServletResponse response)throws java.io.IOException, javax.servlet.ServletException {
}
其中
<%= 变量表达式%>——out.print(变量表达式)
<% java代码%> —— java代码(直接在 .java文件中以java代码的形式出现)
<%! 成员方法/成员变量/静态代码块等%> —— 放在这个_jspService()方法之外
其他的所有代码都以: out.write("")的形式出现:
eg:
out.write(" </head>\r\n");
out.write("\r\n");
out.write(" <body>\r\n");
out.write(" <div id=\"wrapper\">\r\n");
2.JSP的基本语法:
几种格式你认识吗?
<%= %> / <% %> / <%! %> / <%@ page或include%> / <%-- --%> /
JSP模版元素:
JSP页面中的HTML内容称之为JSP模版元素。JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。
JSP表达式 :
JSP脚本表达式(expression)用于将程序数据输出到客户端语法: <%= 变量或表达式 %>
举例:当前时间: <%= new java.util.Date() %>
JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用 out.print(…) 将数据输给客户端。
JSP脚本表达式中的变量或表达式后面 不能有分号(;)
注意几个点:
会报错:<%= int i=1 %>
JSP脚本片断:
JSP脚本片断(scriptlet)用于在JSP页面中编写多行Java代码。语法:
<%
多行java代码
%>
JSP脚本片断中 只能出现java代码,不能出现其它模板元素,
因为:JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到 Servlet的_jspService方法中(意味着这里的java代码都是在同一个方法体中,所以不能在其中定义方法,定义的变量也都是局部变量,需要显式初始化才能使用。)
在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素
<%
int x = 10;
out.println(x);
%>
<p>这是JSP页面文本</p>
<%
int y = 20;
out.println(y);
%>
注意几个点:
<% int i;%> <%System.out.println(i);%> 服务器会报错,因为<% 中间写的都是局部变量 %>,局部变量必须初始化才能使用,或者 <%! int i;%>那么这样就可以 输出i 为null。 java文件中: out.print("int i"); out.print("System.out.println(i)");
对应到java文件中: <%= 表达式 %> ——out.print(“表达式”)
见.java文件:
JSP声明:
JSP页面中编写的所有代码,默认会翻译到servlet的service方法中,而Jsp声明中的java代码被 翻译到_jspService方法的外面(用于初始化和声明其他方法)
(说明这里声明的都是成员变量和方法。)
语法:
<%!
java代码
%>
所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法 。
对应到java文件:——>单纯就是 java代码 的格式。
JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。
<%!
static {
System.out.println("loading Servlet!");
}
private int globalVar = 0;
public void jspInit();
{
System.out.println("initializing jsp!");
}
%>
<%!
public void jspDestroy();
{
System.out.println("destroying jsp!");
}
%>
JSP注释:
<%-- 注释信息 --%>JSP引擎在将JSP页面翻译成Servlet程序时,忽略JSP页面中被注释的内容。
JSP指令:
JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。在JSP 2.0规范中共定义了三个指令:
page指令;
Include指令;
taglib指令;
JSP指令的基本语法格式:
<%@ 指令 属性名="值" %>
eg. <%@ page contentType="text/html;charset=UTF-8"%>
page指令中的方法:
[ language="java" ] : 不用管[ extends="package.class" ] : 不用管
[ import="{package.class | package.*}, ..." ] : 倒包
[ session=“true | false” ]: true创建session对象 ()
[ contentType="mimeType;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ]
[ pageEncoding="characterSet | ISO-8859-1" ]
上面两个方法: contentType 和 pageEncoding 都是 设置编码格式的,
contentType 是用来指定响应报文和浏览器的编解码格式的。
pageEncoding 是用来指定当前java文件的编码格式,可以用来代替contentType,
但是如果两者都设置的话,浏览器以contentType为主,jsp以pageEncoding为主。
[ buffer="none | 8kb | sizekb" ]:设置jspWriter的缓冲区(out对象)。
如果buffer假如为1kb,表示jspWriter缓冲区大小为0,那么每次在使用 out.write()的方式写入的数据就会立马进入response缓冲区,
并且排在response缓冲区的最后(范围是整个.JSP文件的response.getwriter().write的后面),要等到response原本的元素输出完了,
才会输出out对象的数据。
如果buffer为none,那么out缓冲区的数据会立马插入response缓冲区,也就是会和response的输出代码一样对待,按顺序输出。
比如: <%out.write("11");
response.getWriter().write("33");%>
out.write("22");%>
buffer="1kb" 输出的结果是:331122
buffer="none" 输出结果是:113322(按照代码顺序输出)
include指令中的方法:
include指令用于引入其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为静态引入。
<%@ include file=“被包含组件的绝对URL或相对URL"%>
其中的file属性用于指定被引入文件的路径。路径以“/”开头,表示代表当前web应用。
eg. <%@include file="Footer.jsp"%>
1.被引入的文件必须遵循JSP语法。
2.被引入的文件可以使用任意的扩展名,即使其扩展名是html,
JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见名知意,
JSP规范建议使用.jspf(JSP fragments)作为静态引入文件的扩展名。
3.由于使用include指令将会涉及到2个JSP页面,并会把2个JSP翻译成一个servlet,
所以这2个JSP页面的指令不能冲突(除了pageEncoding和导包除外)。
注意:在引入页面中,引入jsp的page指令冲突还要考虑到指令值的大小写问题(utf-8和UTF-8也会引起冲突)
JSP运行原理:
第一次访问会翻译成servlet,所以第一次访问通常会比较慢;JSP引擎在调用JSP对应的_JSPServlet时,会传递或创建9个与web开发相关的对象
提供给_JSPservlet使用。
JSP九大隐式对象:
request: HttpServletRequestresponse: HttpServletResponse
config: ServletConfig
application: ServletContext 域对象引用
exception: Throwable
Session: HttpSession
page: this Object
out: 输出流
了解JSPwriter写入responseWrieter中的时机:
JSP页面中的out隐式对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter只有向out对象中写入了内容,且满足如下任何 一个条件时,out对象才去 调用ServletResponse.getWriter方法,
并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:
1.设置page指令的buffer属性关闭了out对象的缓存功能
2.out对象的缓冲区已满
3.整个JSP页面结束 提交响应
pageContext:
是JSP技术中最重要的一个对象,它本身是一个域对象,可以保存数据,还封装了其他8大对象的引用,可以检索其他域对象的属性。
如何获取其他8个隐式对象的引用? 都是通过 pageContext.getxxxx的方式
//getException方法返回exception隐式对象 //getPage方法返回page隐式对象 //getRequest方法返回request隐式对象 //getResponse方法返回response隐式对象 //getServletConfig方法返回config隐式对象 //getServletContext方法返回application隐式对象 //getSession方法返回session隐式对象 //getOut方法返回out隐式对象 //pageContext封装其它8大内置对象的意义
主要方法:
pageContext作为 域对象,同样的3个API( get/set/removeAttribute()方法)
还封装了其他域的方法:
<%= pageContext.getAttribute("username",PageContext.APPLICATION_SCOPE) %>
表示访问的是哪个属性(String ),哪个域中(int scope)
.findAttribute("属性名") —— 可以查找各个域中的属性: 从下往上找(如果按照域对象的作用范围从小到大来排序: pageContext < request < session < servletContext)
先在 pageContext域中 找此属性名,没有找到就到request域对象中去找,不断向上去查找,四个域中都没有找到就 会返回null
四大域对象:servletContext(整个应用)、request(一次请求转发)、session(一次会话)、pageContext(代表当前JSP页面)
pageContext对象的方法 (域对象方法)
public void setAttribute(java.lang.String name,java.lang.Object value)
public java.lang.Object getAttribute(java.lang.String name)
public void removeAttribute(java.lang.String name)
pageContext对象中还封装了访问其它域对象的方法:
public java.lang.Object getAttribute(java.lang.String name , int scope)
public void setAttribute(java.lang.String name , java.lang.Object value , int scope)
public void removeAttribute(java.lang.String name, int scope)
eg.
<%= pageContext.getAttribute("username",PageContext.APPLICATION_SCOPE) %>scope 代表各个域的常量:
PageContext.APPLICATION_SCOPE (=4)
PageContext.SESSION_SCOPE (=3)
PageContext.REQUEST_SCOPE(=2)
PageContext.PAGE_SCOPE (=1)
.findAttribute(String name)方法 ( *重点,查找各个域中的属性:从小往大找)
这个String name就是对象名
如果我们按照,域对象的作用范围,排个序:
pageContext < request < session < servletConetxt
<%= pageContext.findAttribute("username")%>匹配JSP的过程:
web.xml中的<servlet>和<mapping>标签, 过程类似servlet的查找过程。
Day13 EL技术(Expression Language)
作用:(前提,所有的获取都是从 域对象 中获取,所以要先将数据放入域对象)
1.获取数据:
主要是用于替换JSP页面中的脚本表达式<%= %>,以从各种类型的web域中检索java对象()。
脚本表达式<%= %>在java文件中的编译后的样子是out.print(表达式);所以这个功能会将表达式的结果输出到页面中;
语法:$("标识符")
在执行时,会调用 pageContext.findAttribute( )方法,空则返回""(空串而不是null),找到就返回响应对象。
eg.
<% application.setAttribute("name","zs")%>
<%--用脚本表达式--%>
<%= pageContext.getAttribute("name",pageContext.APPLICATION_SCOPE)%>
<%--用EL表达式: ${标识符}--%>
${name}:在浏览器输入这个 路径的 .jsp 就会输出 zs (因为这里他就是代替<%= %>的功能,都是输出在浏览器上)
//如果放入字符串,就会原封不动输出(原因:因为在.java文件中,他的格式是
out.write("\r\n");
application.setAttribute("name","zs");
out.write('\r');
out.write((java.lang.String)org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${name}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null));
改成字符串之后 ("${\"name\"}",这就要看源码里面对它的处理了。
)。
不需要指定域,因为会 调用 .findAttribute() 方法,从小到大从域中找
(pageContext < request < session < servletContext)
2.获取普通java对象:
java类User : id,name,password(构造方法/set&get/toString);
JSP中:<% request.setAttribute("user1",new User(...))%>
获取对象:${user1} —— 浏览器输出 : {id,name,password}
获取对象的属性 : ${user1.name} —— 通过user1对象的getname()方法来获取到name的值
3.获取List对象:
ArrayList<User> users = new ArrayList<>();
users.add(new User("1","lisi","1234"));
session.setAttribute("users",users);
4.获取session域中元素:
${users[0].password} ———— 可以访问到集合中的某个对象的某个属性值
${users.get(0).password} ————效果同上(调用users的方法)
5.获取数组:
int a = {1,2,5,9};
pageContext.setAttribute("array",a)
${array[0]} ———— 输出数组的第0个元素
6.获取Map对象:
HashMap<Integer,String> map = new HashMap<>();
map.put("a","sz");
map.put("b","sv");
${map} ———— 没有输出,因为${}只能获取域对象的东西;
所以还要放入域对象中;
session.setAttribute("maps",map);
${maps} ——正常输出
${map.key}:
${map.get("a")} / ${map["a"]} (清楚获取的方式)
注意:你的Map的存储是必须是:<String,String>,如果你的键想存整数,那么也是用String,不能用Integer(与普通的Map存储键值对的时候有点区别)。
<% Map<String, String> fuck = new HashMap<>(); fuck.put("a","za"); fuck.put("1","tttt"); session.setAttribute("fuck",fuck); %> ${fuck} ${fuck.get("a")} ${fuck["a"]} ${fuck["1"]} ${fuck.get("1")} 浏览器输出:{a=za, 1=tttt}zazatttttttt字符串或者整数都以字符串的形式存储的话,就都可以获得,但是如果存整数,就无法获得。
7.从域对象获取一个不存在的数据:
${null} ———— 返回空串""
${abcdef} ——>返回结果都是空串
${ null == abcdef}
8.执行运算:
语法:${运算表达式}
可以在JSP页面执行一些简单的运算(算术运算、关系运算、逻辑运算)
session.setAttribute("a",1);
session.setAttribute("b",2);
${a>b} ——> false
${true || false} ——> true
${empty student} ——> true
在遇到集合的时候,empty的判断会有一些不同:
(empty 空数组为false ; empty 空集合为true)
int[] c = {};放入域中
${empty c} ——》返回false(空数组)
ArrayList arr = new ArrayList<>()
${empty arr} ——>返回true;(空集合)
9.获取WEB开发常用对象(11个隐式对象):(操作的基本都是Map类型的对象)
可以获取和读取web开发中的一些常见对象和其数据。
1.pageContext : pageContext对象
${pageContext}
2.pageScope : 表示page域中用于保存属性的Map对象
${pageScope.name} 获取Map的对应name键的value值
3.param : 表示一个保存了所有请求参数的Map对象
${param.username}
${param.password}
4.paramValues : 表示一个保存了所有请求参数的Map对象,对于某个请求对象,返回的是一个String[]
${paramValues[0]} ——表示Map对象数组的第一个元素
${paramValues.hobby[0]} ——表示Map的hobyy属性的第1个元素的值
5.header : 表示一个保存了所有http请求头字段的Map对象
${header}
6.headerValues :所有请求头的Map对象,返回一个数组,通过确定数组元素位序得到值
${headerValues["Accept-Encoding"][0]} ——获取头字段为:Accept-Ecoding数组的第一个内容
(有可能有多个Accept-Ecoding)
${headerValues["Accept-Encoding"]} ——则会输出一个对象数组的地址
7.cookie:
${cookie.JESSSIONID} ——》会得到cookie的ID的对象
所以要通过${cookie.JESSSIONID.name} 得到对象的name值(一般就是JESSSIONID)
${cookie.JESSSIONID.value} 得到JESSSIONID对应的值(128位数的十六进制表示)
8.initParam: 表示一个保存了所有web初始化参数的Map对象
在web.xml中配置<initparam>
10.调用Java方法
在EL表达式中调用的只能是JAVA类的静态方法,且此静态方法必须要在TLD文件中描述此方法。
步骤:
1.在java类中编方法.
2.编写标签库描述文件(.tld)
<tlib-version>1.0</tlib-version>
<short-name>myshortname</>
<uri>http://www.zs.com</>
<function>
<name>方法名称</>
<function-calss>指明方法所在类(全类名)</>
<function-signatrue>方法返回值+方法签名</>
</function>
3.导入jsp文件中:
<%@ taglib prefix="zs" uri="http://www.zs.com">
${zs:add(11,12)}
taglib指令,如果服务器版本比较低的话,会忽略EL表达式,此时可以加入<%@ page isIngonre()="false"%>
Day14 JSTL技术(JSP Standard Tag Library)
JSP标准标签库 (JSP Standard Tag Library)。
作用:
提供为javaweb开发人员一个标准通用的标签函数库,用来和EL配合
核心标签库:
文件
在使用jstl的核心标签库之前,必须在当前页面引入该核心标签库
引入核心标签库:<%@ taglib prefix="name" uri="http://java.sun.com/jsp/jstl/core"%>
1.<c:if 属性名=""> :用来构造简单的"if—then" 的条件表达式。
属性名:
test : 决定是否处理 标签体中的 条件表达式 "${ 表达式 }"
<c:if test="${param.count == 0}" var="result" scope="page">
param.count等于0
</c:if>
然后在浏览器访问地址后面加上?count=0(相当于将请求参数传入)
等价于: if(条件表达式) {
parm.count等于0;
}
var : 指明将条件表达式的结果存储到某个web域对象当中,var指定在该域对象中 存储判断结果
在上面的语句中再写入 ${result} ——会输出false
scope: 指明条件表达式的结果存储在哪个web域对象中
2.<c:choose> <c:when> <c:otherwise>
1. <c:choose>标签用于指定 多个条件选择的组合边界它必须与<c:when>和<c:otherwise>标签一起使用
2. 使用<c:choose>,<c:when>和<c:otherwise>三个标签,可以构造类似 “if-else if-else” 的复杂条件判断结构.
实现多个条件的判断语句。
if(){ }
else if { }
else{ }
<c:choose>
<c:when test="${param.count == 0}">
请求不对
</c:when>
<c:otherwise>
<c:choose> (嵌套定义的时候,必须重新设置<c:choose>)
<c:when test="${param.count > 0}">
请求的有${param.count}条记录
</c:when>
<c:ohterwise>
请求参数非法
</c:otherwise>
</c:choose>
</c:otherwise>
</c:choose>
等价于:
if(param.count == 0){
请求不对
else if(param.count > 0){
请求的有param.count条记录
}else{请求参数非法}
}
3.<c:forEach> :
用于对一个集合中的元素进行循环迭代操作,或者按指定的次数重复迭代标签体中的内容。
属性名:
var :指定将当前迭代到的元素保存到这个page这个web域对象中的属性名称。
items :要迭代的集合对象
begin/end/step :循环
varStatus 获取迭代的当前对象
先添加集合:
List<String> list = new ArrayList<>();
list.add("zs");
list.add("lisi");
放入域对象:
request.setAttribute("list",list)
//迭代集合元素:
<c:forEach var="name" items="${list}">(指明在哪个集合上遍历(items))
${name}
</c:forEach>
//重复迭代集合元素 [begin,end](左右都取到)
<c:forEach begin="1" end="10" step="1">(类比 i = 1;i <10;i++)
java
</c:forEach>
输出10个java
varStatus属性:记录当前被访问的元素状态信息的对象,被存储到pageContext域中的属性的名称。
属性名:
index : 位序下标
count : 访问过了几个元素
first : 是不是第一个元素
last : 是不是最后一个元素
<c:forEach var="name" items="${list}" varStatus="status">
name = ${name} -- ${status.index} -- ${status.count}
</c:forEach>
自定义方法并通过taglib指令将方法引入:
1.在当前应用的WEB-INF目录下创建一个XML Configuration File — JSP Tag Library Descripter
2.在生成的文件中添加信息:
<tlib-version>1.0</tlib-version> <short-name>myshortname</short-name> <!--唯一标识这个tld文件内容--> <uri>http://www.zs.com</uri> //这个就是我们用来引用的入口<%@ taglib uri="http://www.zs.com"%> <!-- Invoke 'Generate' action to add tags or functions --> <!--name 方法名称 --> <!--function-class 指明方法所在类(全类名)--> <!--function-signature 方法返回值 + 方法签名--> <!--tablib标签中的,约束的版本比较低, 会忽略我们的el表达式,--> <function> <description>add x and y</description> <name>add</name> <function-class>com.cskaoyan.el.Tool</function-class> <function-signature> int add(java.lang.String,java.lang.String) </function-signature> </function> </taglib>3.在自己的JSP页面引入
<%@ taglib prefix="fn" uri="http:/java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sss" uri="http://www.zs.com"%>
<% int[] a = {}; application.setAttribute("a", a); %>
<%--${prefix:method(params)}--%> ${sss:add(11,102)} ${fn: length(a) == 0}
示例:
在浏览器中打印出10-100的偶数,并且每隔3个,显示成红色,代码如下:
<c:forEach var="var" begin="10" end="100" step="2" varStatus="list"> <c:choose> <c:when test="${((list.index + 1) % 3) == 0}"> <font color="red">${var}</font><br> </c:when> <c:otherwise> ${var}<br> </c:otherwise> </c:choose> </c:forEach>