简单记录jsp-servlet相关内容
Servlet
概述
Servlet是Server和Applet的缩写,意思是服务端小程序。
Servlet是一种按照Servlet标准来开发的JAVA类,主要运行在服务器端,并由服务器调用执行。
Servlet的创建、使用、销毁都由提供Servlet功能的服务器来管理(Tomcat)。
Servlet可以处理HTTP协议相关的所有内容,是一种用于开发动态Web资源的技术。
Servlet的实现
- 实现Servlet规范,即继承HttpServlet类
- 满足Servlet规范只是让我们的类能接受请求,若要对请求进行业务逻辑处理,则需要重写service方法
- 使用**@WebServlet注解**将一个继承于HttpServlet的类标注为可以处理请求的Servlet
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import javax.io.IOException; import javax.servlet.annotation.WebServlet; @WebServlet("/ser01") public class Servlet extends HttpServlet{ @Override protected void service(HttpServletRequest request, HttpServletResponse respo) throws ServletException, IOException{ System.out.println("Hello Servlet!"); resp.getWriter().write("Hello World"); } }
HttpServletRequest对象
HttpServletRequest 对象:主要作用是用来接收客户端发送过来的请求信息,例如:请求的参数,发送的头信息等都属于客户端发来的信息。
service()方法中形参接收的是HttpServletRequest 接口的实例化对象,该对象主要应用在 HTTP 协议上,是由 Tomcat 封装好传递过来的。
HttpServletRequest对象的常用方法:
getParameter("name")
:获取指定参数的值
该方法用于获取前端页面提交的数据
setCharacterEncoding("UTF-8")
:设置编码格式
这种方式只针对 POST 有效(必须在接收所有的数据之前设定)
getRequestDispatcher("url").forward(request,response)
:请求转发
该方法可以让一次请求从服务端跳转到一个JSP页面
请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保存,地址栏中的 URL 地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始至终只有一个请求发出。
// 设置域对象内容 request.setAttribute(String name, String value); // 获取域对象内容 request.getAttribute(String name); // 删除域对象内容 request.removeAttribute(String name);
该方法可以在请求转发后在JSP页面获取到服务端的数据
HttpServletResponse对象
HttpServletResponse 对象:主要作用是用来响应客户端的请求。
service()方法中形参接收的是 HttpServletResponse 接口的实例化对象,这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
HttpServletRequest对象的常用方法:
getWriter()
:获取字符输出流(只能响应回字符)getOutputStream()
:获取字节输出流(能响应一切数据)
响应回的数据到客户端被浏览器解析。
// 字符输出流 PrintWriter writer = response.getWriter(); writer.write("Hello"); writer.write("<h2>Hello</h2>"); // 字节输出流 ServletOutputStream out = response.getOutputStream(); out.write("Hello".getBytes()); out.write("<h2>Hello</h2>".getBytes());
注意:两者不能同时使用
String.getBytes()
:字符转字节
setContentType("text/html; charset=UTF-8")
:同时设置服务端和客户端设置编码格式sendRedirect("url")
:重定向
重定向是一种服务器指导客户端的行为。客户端发出第一个请求,被服务器接收处理后,服务器会进行响应,在响应的同时,服务器会给客户端一个新的地址(下次请求的地址 response.sendRedirect(url);),当客户端接收到响应后,会立刻、马上、自动根据服务器给的新地址 发起第二个请求,服务器接收请求并作出响应,重定向完成。
- 请求转发和重定向的比较:
请求转发 | 重定向 |
request.getRequestDispatcher().forward() |
response.sendRedirect() |
一次请求,数据在request域中共享 | 两次请求,数据在request域中不共享 |
服务器端行为 | 客户端行为 |
地址栏不发生变化 | 地址栏发生变化 |
绝对地址定位到站点后 | 绝对地址可写到http:// |
Cookie对象
Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地的计算机上。Cookie随着服务器端的响应发送给客户端,保存在浏览器。当下次再访问服务器时把Cookie再带回服务器。
由于 Cookie 是服务器端保存在客户端的信息, 所以其安全性很差。
有一个专门操作Cookie的类 javax.servlet.http.Cookie。
Cookie 的格式:键值对用“=”链接,多个键值对间通过“;”隔开。
创建和发送
通过 new Cookie("key","value")
,来创建一个 Cookie 对象,然后通过response.addCookie(cookie)
将 Cookie添加到 response 对象中随响应发送到客户端浏览器中保存
// 创建Cookie对象 Cookie cookie = new Cookie("uname","zhangsan"); // 发送Cookie对象 response.addCookie(cookie);
获取
在服务器端只提供了一个 getCookies()
的方法用来获取客户端回传的所有 cookie 组成的一个数组,如果需要获取单个 cookie 则需要通过遍历,getName()
获取 Cookie 的名称,getValue()
获取 Cookie 的 值。
// 获取Cookie数组 Cookie[] cookies = request.getCookies(); // 判断数组是否为空 if (cookies != null && cookies.length > 0) { // 遍历Cookie数组 for (Cookie cookie : cookies){ System.out.println(cookie.getName()); System.out.println(cookie.getValue()); } }
设置到期时间
cookie.setMaxAge()
- 负整数
若为负数,表示不存储该 cookie。
cookie 的 maxAge 属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么cookie 就会消失。 - 正整数
若为大于 0 的整数,表示存储的秒数。
浏览器会把 Cookie 保存到硬盘上,就算关闭浏览器,重启客户端电脑,cookie 也会存活相应的时间。 - 零
若为 0,表示删除该 cookie。
cookie 生命等于 0 是一个特殊的值,它表示 cookie 被作废!也就是说,如果原来浏览器已经保存了这个 Cookie,那么可以通过 Cookie 的 setMaxAge(0)来删除这个 Cookie。 无论是在浏览器内存中,还是在客户端硬盘上都会删除这个 Cookie。
注意
- Cookie保存在当前浏览器中。
在一般的站点中常常有记住用户名这样一个操作,该操作只是将信息保存在本机上,换电脑以后这些信息就无效了。而且 cookie 还不能跨浏览器。 - Cookie存中文问题
Cookie 中不能出现中文,如果有中文则通过URLEncoder.encode()
来进行编码,获取时通过URLDecoder.decode()
来进行解码。
String name = "姓名"; String value = "张三"; // 通过 URLEncoder.encode()来进行编码 name = URLEncoder.encode(name); value = URLEncoder.encode(value); // 创建Cookie对象 Cookie cookie = new Cookie(name,value); // 发送Cookie对象 response.addCookie(cookie); // 获取时通过 URLDecoder.decode()来进行解码 URLDecoder.decode(cookie.getName()); URLDecoder.decode(cookie.getValue());
- 同名Cookie问题
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie。 - 浏览器存放Cookie的数量
不同的浏览器对Cookie也有限定,Cookie的存储有是上限的。Cookie是存储在客户端(浏览器) 的,而且一般是由服务器端创建和设定。后期结合Session来实现回话跟踪。
设置Cookie的路径
cookie.setpath("")
默认路径为当前项目
cookie的路径指的是可以访问该cookie的顶层目录,该路径的子路径也可以访问该cookie。
HttpSession对象
HttpSession对象是 javax.servlet.http.HttpSession 的实例,该接口并不像 HttpServletRequest 或 HttpServletResponse 还存在一个父接口,该接口只是一个纯粹的接口。这因为 session 本身就属于 HTTP 协议的范畴。
对于服务器而言,每一个连接到它的客户端都是一个 session,servlet 容器使用此接口创建 HTTP 客户端和 HTTP 服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应于一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的信息,比如会话标识符、创建时间和最后一次访问时间。在整个 session 中,最重要的就是属性的操作。
Session 的作用就是为了标识一次会话,或者说确认一个用户;并且在一次会话(一个用户的多次请求)期间共享数据。我们可以通过 request.getSession()
方法,来获取当前会话的 session 对象。
cookie和session的配合使用
用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session ,请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器,浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名。
当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。
cookie的session区别
因为Cookie是将数据存储在本地浏览器,所以会产生数据泄露风险。Session是将用户具体数据存储在服务器,将具体数据的ID存储在本地浏览器,所以即使本地数据泄露也不会泄露服务器端保存的用户具体数据。
// 如果session对象存在,则获取;如果session对象不存在,则创建 HttpSession session = request.getSession();
标识符JSESSIONID
Session 既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是 sessionId。
每当一次请求到达服务器,如果开启了会话(访问了 session),服务器第一步会查看是否从客户端 回传一个名为 SESSIONID 的 cookie,如果没有则认为这是一次新的会话,会创建 一个新的 session 对 象,并用唯一的 sessionId 为此次会话做一个标志。如果有 JESSIONID 这 个cookie回传,服务器则会根 据 JSESSIONID 这个值去查看是否含有id为JSESSION值的session 对象,如果没有则认为是一个新的会 话,重新创建一个新的 session 对象,并标志此次会话; 如果找到了相应的 session 对象,则认为是之 前标志过的一次会话,返回该 session 对象,数据达到共享。
这里提到一个叫做 JSESSIONID 的 cookie,这是一个比较特殊的 cookie,当用户请求服务器时,如果 访问了 session,则服务器会创建一个名为 JSESSIONID,值为获取到的 session(无论是获取到的还是 新创建的)的 sessionId 的 cookie 对象,并添加到 response 对象中,响应给客户端,有效时间为关闭 浏览器。
所以 Session 的底层依赖 Cookie 来实现。
session域对象
Session 用来表示一次会话,在一次会话中数据是可以共享的,这时 session 作为域对象存在,可以通过 setAttribute(name,value)
方法向域对象中添加数据,通过 getAttribute(name)
从域对象中获取 数据,通过 removeAttribute(name)
从域对象中移除数据。
// 获取session对象 HttpSession session = request.getSession(); // 设置session域对象 session.setAttribute("uname","admin"); // 获取指定名称的session域对象 String uname = (String) request.getSession.getAttribute("uname"); // 移除指定名称的session域对象 session.removeAttribute("uname");
数据存储在 session 域对象中,当 session 对象不存在了,或者是两个不同的 session 对象时,数据也就不能共享了。这就不得不谈到 session 的生命周期。
session对象的生命周期
通过 session.setMaxInactiveInterval(int)
来设定 session 的最大不活动时间,单位为秒。默认为30min,即你不操作界面的时间,一旦有操作,session 会重新计时。
我们也可以通过 getMaxInactiveInterval()
方法来查看当前 Session 对象的最大不活动时间。
我们也可以通过 session.invalidate()
方法让 session 立刻失效
// 获取session对象 HttpSession session = request.getSession(); // 设置session的最大不活动时间 session.setMaxInactiveInterval(15); // 15秒 // 获取session的最大不活动时间 int time = session.getMaxInactiveInterval(); // 销毁session对象 session.invalidate();
从前面的 JESSIONID 可知道,session 的底层依赖 cookie 实现,并且该 cookie 的有效时间为关闭浏览器,从而 session 在浏览器关闭时也相当于失效了(因为没有 JSESSION 再与之对应)。
当关闭服务器时,session 销毁。 Session 失效则意味着此次会话结束,数据共享结束。
ServletContext对象
每一个 web 应用都有且仅有一个ServletContext 对象,又称 Application 对象,从名称中可知,该对象是与应用程序相关的。在 WEB 容器启动的时候,会为每一个 WEB 应用程序创建一个对应的 ServletContext 对象。
该对象有两大作用,第一、作为域对象用来共享数据,此时数据在整个应用程序中共享; 第二、该对 象中保存了当前应用程序相关信息。例如可以通过 getServerInfo()
方法获取当前服务器信息 , getRealPath(String path)
获取资源的真实路径等。
ServletContext对象的获取
- 通过 request 对象获取
ServletContext servletContext = request.getServletContext();
- 通过 session 对象获取
ServletContext servletContext = request.getSession().getServletContext();
- 通过 servletConfig 对象获取,在 Servlet 标准中提供了 ServletConfig 方法
ServletConfig servletConfig = getServletConfig(); ServletContext servletContext = servletConfig.getServletContext();
- 直接获取,Servlet 类中提供了直接获取 ServletContext 对象的方法
ServletContext servletContext = getServletContext();
- 常用方法
// 获取项目存放的真实路径 String realPath = request.getServletContext().getRealPath("/"); // 获取当前服务器的版本信息 String serverInfo = request.getServletContext().getServerInfo();
- ServletContext域对象
ServletContext 也可当做域对象来使用,通过向 ServletContext 中存取数据,可以使得整个应用程序共享某些数据。当然不建议存放过多数据,因为 ServletContext 中的数据一旦存储进去没有手动移除将会一直保存。
// 获取ServletContext对象 ServletContext servletContext = request.getServletContext(); // 设置域对象 servletContext.setAttribute("name","zhangsan"); // 获取域对象 String name = (String) servletContext.getAttribute("name"); // 移除域对象 servletContext.removeAttribute("name");
Servlet的三大域对象
- request域对象
在一次请求中有效。请求转发有效,重定向失效。 - session域对象
在一次会话中有效。请求转发和重定向都有效,session销毁后失效。 - servletContext域对象
在整个应用程序中有效。服务器关闭后失效。
文件上传
文件上传涉及到前台页面的编写和后台服务器端代码的编写,前台发送文件,后台接收并保存文件, 这才是一个完整的文件上传。
前端页面
在做文件上传的时候,会有一个上传文件的界面,首先我们需要一个表单,并且表单的请求方式为 **POST**
;其次我们的 form 表单的 enctype
必须设为"multipart/form-data
",即 **enctype="multipart/form-data**"
,意思是设置表单的类型为文件上传表单。默认情况下这个表单类型是 "application/x-www-form-urlencoded"
, 不能用于文件上传。只有使用了multipart/form-data
才 能完整地传递文件数据。
<!-- 文件上传表单 1. 表单提交类型 method="post" 2. 表单类型 enctype="multipart/form-data" 3. 文件提交地址 action="uploadServlet" 4. 表单元素类型 1)普通项 type="text" 2) 文件项 type="file" 文件域设置name属性值 5. 表单提交一定要设置表单元素的name属性值,否则后端无法接受数据 --> <form method="post" action="uploadServlet" enctype="multipart/form-data"> 姓名:<input type="text" name="uname" > <br> 文件:<input type="file" name="myfile" > <br> <!--button默认类型是“提交” type="submit"--> <button type="submit">提交</button> </form>
后端实现
使用注解 @MultipartConfig 将一个 Servlet 标识为支持文件上传。 Servlet 将 multipart/form-data 的 POST 请求封装成 Part,通过 Part 对上传的文件进行操作。
import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import java.io.IOException; @WebServlet("/uploadServlet") @MultipartConfig // 如果是文件上传表单,一定要加这个注解 public class UploadServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponseresponse) throws ServletException, IOException { // 设置请求的编码格式 request.setCharacterEncoding("UTF-8"); // 获取普通表单项 (文本框) String uname = request.getParameter("uname"); // "uname"代表的是文本框的name属性值 // 通过 getPart(name) 方法获取Part对象 (name代表的是页面中file文件域的name属性值) Part part = request.getPart("myfile"); // 通过Part对象,获取上传的文件名 String fileName = part.getSubmittedFileName(); // 获取上传文件需要存放的路径 (得到项目存放的真实路径) String realPath = request.getServletContext().getRealPath("/"); // 将文件上传到指定位置 part.write(realPath + fileName); } }
文件下载
文件下载,即将服务器上的资源下载(拷贝)到本地,我们可以通过两种方式下载。第一种是通过超链接本身的特性来下载;第二种是通过后端代码下载。
超链接下载
当我们在 HTML 或 JSP 页面中使用a标签时,原意是希望能够进行跳转,但当超链接遇到浏览器不识别的资源时会自动下载;当遇见浏览器能够直接显示的资源,浏览器就会默认显示出来,比如 txt、 png、jpg 等。当然我们也可以通过 download 属性规定浏览器进行下载。但有些浏览器并不支持。
<!-- 当超链接遇到浏览器不识别的资源时,会自动下载 --> <a href="test.zip">超链接下载</a> <!-- 当超链接遇到浏览器识别的资源时,默认不会下载。通过download属性可进行下载 --> <a href="test.txt" download>超链接下载</a>
download 属性可以不写任何信息,会自动使用默认文件名。如果设置了download属性的值,则使用 设置的值做为文件名。当用户打开浏览器点击链接的时候就会直接下载文件。
后端实现下载
- 需要通过
response.setContentType
方法设置Content-type
头字段的值, 为浏览器无法使用某 种方式或激活某个程序来处理的MIME
类型,例 如"application/octet-stream"
或"application/xmsdownload"
等。 - 需要通过
response.setHeader
方法设置Content-Disposition
头的值 为"attachment;filename= 文件名"
- 读取下载文件,调用
response.getOutputStream
方法向客户端写入附件内容。
package com.xxxx.servlet; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class DownloadServlet extends HttpServlet { protected void service(HttpServletRequest request, HttpServletResponseresponse) throws ServletException, IOException { // 设置请求的编码 request.setCharacterEncoding("UTF-8"); // 获取文件下载路径 String path = getServletContext().getRealPath("/"); // 获取要下载的文件名 String name = request.getParameter("fileName"); // 通过路径得到file对象 File file = new File(path + name); // 判断file对象是否存在,且是否是一个标准文件 if (file.exists() && file.isFile()) { // 设置响应类型 (浏览器无法使用某种方式或激活某个程序来处理的类型) response.setContentType("application/x-msdownload"); // 设置头信息 response.setHeader("Content-Disposition", "attachment;filename=" +name); // 得到输入流 InputStream is = new FileInputStream(file); // 得到输出流 ServletOutputStream os = response.getOutputStream(); // 定义byte数组 byte[] car = new byte[1024]; // 定义长度 int len = 0; // 循环 输出 while ((len = is.read(car)) != -1) { os.write(car, 0, len); } // 关闭流 释放资源 os.close(); is.close(); } else { System.out.println("文件不存在,下载失败!"); } } }
JSP
概述
JSP:Java Server Page,是一种动态网页编程技术,是Java Web服务器端的动态资源。
与html相比,JSP可以在页面中嵌套java代码,为用户提供动态数据。
JSP本质是一个Servlet,当我们第一次访问JSP时,JSP引擎会将这个JSP翻译成一个Servlet,存放在tomcat中的work目录中。
基础语法
注释
显式注释语法:允许客户端看见。从HTML风格继承而来
隐式注释语法:不允许客户端看见。从JAVA风格继承;JSP自己的注释
//单行注释 /*多行注释*/ <!--HTML风格的注释--> <%--JSP注释--%>
Scriptlet
在Scriptlet是嵌入在HTML代码中的JAVA程序
三种Scriptlet代码:
<% JAVA代码 %>:java脚本段。定义局部变量、编写语句。 生成的代码在Servlet的service方法t中 <%! 声明全局变量 %>:声明。定义全局(成员)变量、方法、类。 生成的代码在Servlet的类体中 <%= 数值 %>:输出表达式、变量、字面值。 生成的代码在Servlet的service方法中
该方法使项目耦合度较高,不够灵活。
JSP的指令标签
将各个页面中相同的部分分成独立的文件,使用的时候直接导入
include静态包含
<%@ include file="要包含的文件路径" %> <!--相对路径-->
静态包含是将内容进行了直接的替换,在Servlet引擎转译时就把此文件内容包含了进去,将两个文件的源代码整合到一起,全部放到_jspService方法中,所以只生成了一个Servlet。两个页面不能有同名的变量。
该方法耦合度高,不够灵活。
include动态包含
动态包含在代码的编译阶段,包含和被包含是两个独立的部分,只有当运行时,才会动态包含进来,好比方法的调用。
<jsp:include page="include.jsp"></jsp:include>
动态包含的特点:
- 动态包含相当于方法的调用
- 动态包含会生成多个源码文件
- 可以定义同名变量
- 效率高,耦合度低
注意:当动态包含不需要传递参数时,include双标签之间不要加任何内容,包括换行和空格,除非确认要使用参数,否则报错。
动态包含传递参数:
<jsp:include page="要包含的页面路径"> <jsp:param name="参数名" value="参数值"/> </jsp:include&
name属性不支持表达式,value属性支持表达式
获取参数:request.getParameter()
JSP的四大域对象
四种属性范围
属性范围指的是对象可以在多少个页面中保存并继续使用。
- page范围
- pageContext:只在一个页面中保存属性,跳转后无效
- request范围
- requset:只在一次请求中保存,服务器跳转后依然有效
- session范围
- session:在一次会话范围中,无论何种跳转都可以使用
- application范围
- application:在整个服务器上保存
public void setAttribute(String name, Object o)
:设置属性的名称及内容
public Object getAttribute(String name)
:根据属性名取属性值
public void removeAttribute(String name)
:删除指定的属性
属性范围的特点
EL表达式
语法
$${域对象的名称}
EL表达式操作的是域对象中的数据,操作不了局部变量
当需要指定从某个特定的域对象中查找数据时可以使用四个域对象对应的空间对象,分别是:pageScope
,requestScope
,sessionScope
,applicationScope
EL默认从小到大查找,找到即可。当域对象全找完了还没找到返回空字符串""
使用
JSTL
标签的使用
Java Server Pages Standard Tag Libray:JSP标准标签库集合。用于解决一些常见的问题,例如迭代、条件测试、XML处理、数据库操作等。
根据标签库的功能可分为五个类别:
- 核心标签库
- 格式化标签库
- SQL标签库
- XML标签库
- 函数标签库
核心标签库:
- http://java.sun.com/jsp/jstl/core
- 包含Web应用的常见工作。例如:循环、表达式赋值、基本输入输出等。
格式化标签库:
- http://java.sun.com/jsp/jstl/fmt
- 用来格式化显示数据的工作。例如:对不同区域的日期格式化等。
为了在JSP页面使用JSTL类库,必须使用taglib
指令引入库。
<%@taglib uri="库的地址" prefix="别名"%>
条件动作标签
迭代标签
格式化动作标签