一 概述
JSP是HTML + JS + CSS + JAVA的混合产物,负责与用户进行交互,将最终的界面呈现给用户。本质上是一个Servlet,因为Web容器(Tomcat)在使用时,将其转换Servlet进行使用。
1.1 原理
(1)Web容器(服务器)接收到JSP请求时,会将该请求交给JSP引擎处理,每一个JSP页面第一次被访问时,JSP引擎会将其翻译翻译为一个Servlet(包含_jspInit()、_jspDestory()、_jspServlet()等方法,所以说JSP本质上是一个Servlet),Web容器再调用转换后的Servlet完成业务操作。
(2)在Servlet中将 html + js + css 部分用out.wirte()进行输出,浏览器本身就是html运行环境,会将其解析成页面展示。
1.2 嵌入Java
1.2.1 嵌入脚本
<% Java脚本 %>
,在<% %>中写Java代码执行逻辑。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP starting page</title>
</head>
<body>
<%
String str = "Hello JSP";
System.out.println(str);
%>
</body>
</html>
1.2.2 嵌入方法
<%! Java方法 %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP starting page</title>
</head>
<body>
<%!
public void say() {
System.out.println("Hello JSP");
}
%>
</body>
</html>
1.2.3 嵌入表达式
<%= Java对象/变量 %>
,这里的Java对象和变量会在前端页面输出。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP starting page</title>
</head>
<body>
<% String str = "Hello JSP"; %>
<%= str %>
</body>
</html>
三者结合:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP starting page</title>
</head>
<body>
<!-- 嵌入方法 -->
<%!
public String say() {
System.out.println("在控制台说:Hello JSP");
return "Hello JSP";
}
%>
<%-- 嵌入脚本 --%>
<% String str = say(); %>
<p>
<%-- 嵌入对象/变量 --%>
在网页说:<%= str %>
</p>
</body>
</html>
上述JSP内容实际等价于在Servlet中写:
public String say() {
System.out.println("在控制台说:Hello JSP");
return "Hello JSP";
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
String str = say();
servletResponse.getWriter().write("<p>");
servletResponse.getWriter().write("在网页说:" + str);
servletResponse.getWriter().write("</p>");
}
二 九大内置对象
JSP转成Serlvet,提前生成好的九个对象,在JSP中可以直接使用。
(1)request: 请求对象 HttpServletRequest;
(2)response: 响应对象 HttpServletResponse;
(3)session: 一次会话 HttpSession;
(4)page: 当前JSP对应的Servlet对象,相当于this, Servlet;
(5)pageContext: 页面上下文,获取页面信息 PageContext;
(6)config: 当前JSP页面对应的Servlet对象信息 ServletConfig;
(7)application: 表示当前Web应用,全局对象,保存所有用户共享信息 ServletContext;
(8)out: 向浏览器输出信息 JspWriter;
(9)exception: 表示当前JSP页面法神的异常 Exception。
在此选取若干常用内置对象进行阐述:
2.1 request
常用方法:
(1)String getParameter(String key):根据参数名从客户端
(浏览器)接收参数值;
(2)String[] getParameterValues(String key):获取客户端传来的多个同名参数的值,存入数组;
(3)void setAttribute(String key, Object value):用键值对形式保存数据;
(4)String getAttribute(String key):使用通过key值取出存入的value值。Serlvet
之间传递参数时使用,注意与getParameter()区分。
(5)RequestDispatcher getRequestDispatcher(String path):返回一个RequestDispatcher对象,通过该对象的forward方法进行请求转发。
(6)void setCharacterEncoding(String charset):指定请求的编码。
2.1 response
常用方法:
(1)sendRedirect(String path):重定向,页面之间的跳转。
getRequestDispatcher()请求转发 与 sendRedirect()重定向的区别:
请求转发:将同一个请求传给下一个页面,同一个请求在服务器
中传递,地址栏不变,也叫服务器跳转,
重定向:重新创建一个新的请求传给下一个页面,之前的请求接收生命周期,客户端
发送一次新的请求访问目标资源,地址改变,也叫客户端跳转。
综上所述可以知道,开发过程中如果两个页面间需要用到request传值,则必须使用请求转发,因为重定向会接收原来的请求(request)的生命周期,无法保存需要传递的数据。
2.3 session
属于统一会话的请求有一个统一的标识,sessionID
session常用方法:
(1)String getId():获取sessionID;
(2)void setAttribute(String key, Object value):向session以键值对形式存放数据;
(3)void getAttribute(String key):通过key值从session中获取参数;
(4)void setMaxInactiveInterval(int i):设置session自动失效时间,单位秒。若不设置,默认1800秒;
(5)int getMaxInactiveInterval():获取session自动失效时间。
(6)void invalidate():手动使session失效。
三 作用域
pageContext,request,Session,application都有setAttribute()和getAttribute(),可以保存和读取数据,所有讨论作用域范围才有意义。
从小到大排列:
pageContext <= request <= Session <= application
(1)page作用域:注意描述的是PageContext对象,不是page对象!!!
,范围在当前JSP页面内;
(2)request作用域:范围在一次请求内;
(3)session作用域:范围在一次会话内;
(4)application作用域:范围在一次Web应用内;
四 EL表达式
简化JSP中对数据操作的代码写法,只能在JSP中使用,${ 变量名 }
的写法非常简洁方便。
不使用EL表达式,获取数据的写法如下:
<%
request.setAttribute("name", "张三");
%>
<%= request.getAttribute("name") %>
使用EL表达式后:
${name}
通过上面的例子,可以看出EL表达式本质上就是简化了getAttribute()的使用。但也因此有一个前提,必须先有setAttribute()存放数据,才能取出数据。
4.1 访问优先级
EL表达式访问变量,是在域(pageContext 、request、session、application)对象中访问,当不同域中存在同名对象时,访问优先级为:
pageContext ==> request ==> session ==> application。
当然,可以指定访问某个域中的对象,写法:
访问page 域:${ pageScope.name }
,名字虽然是page,但与page内置对象无关。
访问request域:${ requestScope.name }
访问session域:${ sessionScope.name }
访问application域:${ applicationScope.name }
4.2 操作对象
假设存在User类,域中存在User的对象user{id=1,name=“张三”}
public class User {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
<%
User user = new User();
user.setId(1);
user.setName("张三");
application.setAttribute("user", user);
%>
4.2.1 获取对象属性:
${ user.id }
${ user.name }
等价于:
<%= application.getAttribute("user").getId() %>
<%= application.getAttribute("user").getName() %>
4.2.2 设置对象属性
${ user.id=2 }
${ user.name="李四" }
等价于:
<%
user.setId(2);
application.setAttribute("user", user);
%>
<%= application.getAttribute("user").getId() %>
<%
user.setName("李四");
application.setAttribute("user", user);
%>
<%= application.getAttribute("user").getName() %>
注:EL表达式中是通过反射机制获取类中的getXxx()和setXxx(Xxx xxx)方法进行调用,是与方法绑定,而不是与属性绑定。
4.2.3 执行表达式
&& || ! != > < >= <= ==
(1)&& and
(2)|| or
(3)! not
(4)!= ne
(5)== eq
(6)< lt
(7)> gt
(8)<= le
(9)>= ge
(10)empty 变量为null,长度为0的String,size为0的集合
五 JSTL
JSTL 是JSP Standard Tag Library 的缩写,翻译过来叫JSP标准标签库。与EL表达式一样是用来简化JSP中代码的。不过El侧重于数据展示,JSTL侧重于逻辑处理。
例如A页面request中存放一个集合,B页面通过拼接for循展示数据,C页面使用jstl标签展示数据。
<%
// A页面
User user1 = new User(1, "张三", new Cat("波斯猫"));
User user2 = new User(2, "李四", new Cat("埃及猫"));
User user3 = new User(3, "王五", new Cat("大橘猫"));
List<User> list = new ArrayList<>();
list.add(user1);
list.add(user2);
list.add(user3);
request.setAttribute("list", list);
%>
<%
// B页面
List<User> userList = (List<User>) request.getAttribute("list");
for (User user:userList) {
request.setAttribute("user", user);
%>
${user.id}
${user.name}
${user.cat.type}
<%
}
%>
<%-- C页面 --%>
<c:forEach items="${list}" var="user">
${user.id}
${user.name}
${user.cat.type}
</c:forEach>
光从代码行数上来看,使用jstl都会简便很多,并且结构更加清晰统一,降低了拼接可能导致的错误。
5.1 前置准备
(1)使用时需要导入jstl.jar和standard.jar两个包,通过官网http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/下载。
(2)在项目工程web/WEB-INF下创建lib目录,将两个jar包复制进去。
(3)在工程里引用两个jar包.
(4)在JSP页面导入标签库,便可以使用了。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
5.2 常用标签
set、out、remove、catch
5.2.1 set
可以向域中添加数据,若不指定存放的域,默认存放在page域中。
<%-- var和value设置键值对 scope指定存放的作用域范围 --%>
<c:set var="name" value="关雁玲" scope="application"></c:set>
可以修改对象的数据
<%-- target指定对象,property指定属性,value指定修改值 --%>
<c:set target="${user}" property="name" value="赵国庆"></c:set>
5.2.2 out
输出域对象:
<%-- value指定key值,default指定域中没有key值对应数据时输出的内容 --%>
<c:out value="${Exgirl}" default="关雁玲"></c:out>
5.2.3 remove
根据key值删除域对象:
<%-- var指定根据哪个key值删除域对象,scope指定删除哪个域中的对象 --%>
<c:remove var="name" scope="application"></c:remove>
5.2.4 catch
捕获异常,var指定异常的信息:
<c:catch var="error">
<%
int i = 10/0;
%>
</c:catch>
${error}
5.2.5 if 和 choose
判断分支:
<c:set var="num1" value="1"></c:set>
<c:set var="num2" value="2"></c:set>
<%-- test判断条件 --%>
<c:if test="${num1>num2}">num1大</c:if>
<c:if test="${num1<num2}">num2大</c:if>
<%-- test判断条件,该结构类似switch case --%>
<c:choose>
<c:when test="${num1>num2}">num1大</c:when>
<c:when test="${num1<num2}">num2大</c:when>
<c:otherwise>一样大</c:otherwise>
</c:choose>
5.2.6 forEach
进行集合元素的迭代:
<%
List list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(5);
list1.add(6);
list1.add(7);
list1.add(8);
request.setAttribute("list", list1);
%>
<%-- items迭代的集合,var每个元素的临时key值,begin开始下标,end结束下标 step迭代步长 varStatus迭代信息(开始下标,结束下标,当前下标等) --%>
<c:forEach items="${list1}" var="num" begin="0" end="7" step="2" varStatus>
${sta.index}
${num}
</c:forEach>
5.3 其它标签库
5.3.1 fmt标签库
格式标签库fmt,使用时导入:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
格式化日期标签formatDate
、格式化数值标签formatNumber
<%
Date date = new Date();
request.setAttribute("date", date);
float num5 = 123.456f;
request.setAttribute("num5", num5);
%>
<%-- value格式化的日期,patter格式 --%>
<fmt:formatDate value="${date}" pattern="yyyy-MM-dd HH:mm:ss"></fmt:formatDate>
<%-- var格式化的数值,maxIntegerDigits整数位数,maxFractionDigits小数位数 --%>
<fmt:formatNumber var="${num5}" maxIntegerDigits="2" maxFractionDigits="2">
5.3.2 function标签库
提供各种函数,使用时导入:
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%
request.setAttribute("info", "Java,C");
%>
<%-- 内容是否存在 --%>
${fn:contains(info, "Python")}
<%-- 是否以某内容开头 --%>
${fn:startsWith(info, "Java")}
<%-- 是否以某内容结尾 --%>
${fn:endsWith(info, "C")}
<%-- 某内容开始下标 --%>
${fn:indexOf(info, "va")}
<%-- 替换内容 --%>
${fn:replace(info, "C", "Python")}
<%-- 截取 --%>
${fn:substring(info, 2, 3)}
<%-- 分隔 --%>
${fn:split(info, ",")[0]}-${fn:split(info, ",")[1]}
六 文件上传/下载
6.1 文件上传
前提条件:
(1)input标签type为file;
(2)form表单的enctype为multipart/form-data,以二进制形式传输数据;
(3)form表单的method为post,get请求会将文件名传给服务器,而不是文件本身。
过程图解:
6.1.1 JSP写法
<%-- enctype传输类型:二进制 --%>
<form enctype="multipart/form-data" action="/upload" method="post">
<%-- type输入的类型:文件 --%>
<input type="file" name="file">
<input type="submit">
</form>
6.1.2 Servlet写法
@WebServlet("/upload")
public class Upload extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 接收客户端传来的文件的字节流
InputStream inputStream = req.getInputStream();
// 转成字符输入流
Reader reader = new InputStreamReader(inputStream,"UTF-8");
// 再转成字符输入缓冲流,读的快,其实可以宏观看成输入流
BufferedReader bufferedReader = new BufferedReader(reader);
// 获取文件绝对路径
String path = req.getServletContext().getRealPath("file/test.txt");
// 建立输出流,并字节输出流=》字符输出流=》字符输出缓冲流
OutputStream outputStream = new FileOutputStream(path);
Writer writer = new OutputStreamWriter(outputStream);
BufferedWriter bufferedWriter = new BufferedWriter(writer);
// 临时接收读取的内容
String str = "";
// 向文件中写内容
while((str = bufferedReader.readLine()) != null) {
System.out.println(str);
bufferedWriter.write(str);
}
bufferedWriter.flush();
bufferedWriter.close();
writer.close();
outputStream.close();
bufferedReader.close();
reader.close();
inputStream.close();
}
}
6.1.3 fileupload写法
使用fileupload组件,可以将表单信息封装成对象,以对象形式对文件进行操作,使业务逻辑更清晰,同时自动处理请求传递中无关的信息,保持文件内容的原始性。
使用时需要commos-fileupload
和commos-io
两个jar包,下载地址分别为:commos-fileupload下载 和 commons-io下载。
将两个jar包复制进Web项目中web/WEB-INF/lib目录下。
将jar包导入工程。
使用:
@WebServlet("/upload")
public class Upload extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// FileItem工厂
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
// 获取请求中的表单内容,将表单元素用FileItem封装,存入List集合
List<FileItem> list = servletFileUpload.parseRequest(req);
// 遍历表单元素并处理
for (FileItem fileItem : list) {
// fileItem对象是否为普通表单元素(text、password等)
if(fileItem.isFormField()) {
// 获取表单元素的name值
String name = fileItem.getFieldName();
// 获取表单元素的输入值
String value = fileItem.getString();
} else { // 不是则为文件,进行上传
// 用字节输入流接收文件数据流
InputStream inputStream = fileItem.getInputStream();
// 获取项目工程的绝对路径,并拼接“保存目录/保存文件名”
String path = req.getServletContext().getRealPath("file/" + fileItem.getName());
// 设置字节输出流
OutputStream outputStream = new FileOutputStream(path);
int temp = 0;
// 按字节读文件,读完为止。上传文件的内容==>保存的文件内容
while((temp = inputStream.read()) != -1) {
outputStream.write(temp);
}
// 关闭输出、输入流,完成保存
outputStream.close();
inputStream.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
6.2 文件下载
6.2.1 JSP写法
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP '' starting page</title>
</head>
<body>
<a href="/download?type=txt">下载文本</a>
<a href="/download?type=jpg">下载图片</a>
</body>
</html>
6.2.2 Servlet写法
@WebServlet("/download")
public class Download extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String type = req.getParameter("type");
String fileName = "";
// 根据不同选择,下载不同文件
switch(type) {
case "txt":
fileName = "test.txt";
break;
case "jpg":
fileName = "背景.jpg";
break;
}
// 设置响应格式,让浏览器知道是下载操作
resp.setContentType("applicaton/x-mdownload");
// 设置下载后的文件名
resp.setHeader("Content-Disposition", "atachment;filename=" + fileName);
// 设置响应编码
resp.setContentType("text/html;charset=UTF-8");
// 建立输出流的连接
OutputStream outputStream = resp.getOutputStream();
// 可下载文件的存放位置的绝对路径
String path = req.getServletContext().getRealPath("file/" + fileName);
// 获取下载文件的内容
InputStream inputStream = new FileInputStream(path);
int temp = 0;
// 向输出流中写下载文件的内容
while((temp = inputStream.read()) != -1) {
outputStream.write(temp);
}
// 关闭输入、输出流,下载完成
inputStream.close();
outputStream.close();
}
}
@WebServlet("/download")
public class Download extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String filename = req.getParameter("filename");
File tempfile = new File(filename);
String realname = tempfile.getName();
// 设置响应格式,让浏览器知道是下载操作
resp.setContentType("applicaton/x-mdownload");
// 设置下载后的文件名
// resp.setHeader("Content-Disposition", "atachment;filename=" + realname);
// 中文下载文件名乱码或呈_时使用
resp.setHeader("Content-disposition", "attachment; filename=" + new String(realname.getBytes("utf-8"),"ISO8859-1"));
// 设置响应编码
resp.setContentType("text/html;charcet=UTF-8");
// 建立输出流的连接
OutputStream outputStream = resp.getOutputStream();
// 可下载文件的存放位置的绝对路径
String path = "D://upFiles/" + filename;
// 获取下载文件的内容
InputStream inputStream = new FileInputStream(path);
int temp = 0;
// 向输出流中写下载文件的内容
while((temp = inputStream.read()) != -1) {
outputStream.write(temp);
}
// 关闭输入、输出流,下载完成
inputStream.close();
outputStream.close();
}
}