Java EE
- 1,tomcat
- 2,HTTP
- 3,servlet
- 4,会话技术
- 4.1,Cookie对象
- 4.1.1 使用步骤:
- 4.1.2 Cookie的持久化存储
- 4.1.3 Cookie的共享
- 4.1.4 Cookie的特点和作用
- 4.1.5 Cookie的注意事项
- 4.1.6 一个使用Cookie的小测试
- 4.2,Session对象
- 5,jsp
- 6,EL
- 7,jstl标签库
- 8,Filter
- 9,Listener监听器
1,tomcat
1.1,修改tomcat的端口号
- conf/server.xml
- 找到
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
- 可以将tomcat的port改成80,80端口是http协议的默认端口号,这样在访问时,就不用输入端口号
1.2,部署项目的方式
-
直接将项目部署在webapps目录下(两种)
-
直接将项目文件夹放入webapps目录下,如果在本机上那么访问路径为:http://localhost:8080/项目文件夹名
-
将项目打包成一个war包,再将war包放置到webapps目录下。war包会自动解压缩,删除项目只需删除war包即可
-
-
配置conf/server.xml文件
- 找到
<Host>
标签 - 在标签里添加
<Context dacBase="实际的项目地址" path="/path"/>
- /path为虚拟映射的地址,在本机上访问路径为http://localhost:8080/path
- 找到
-
在conf/Catalina/loclhost文件下创建任意名称的xml文件。(热部署)
- 在其中编写
<Context docBase="实际项目地址"/>
- 虚拟目录为创建的xml文件的名称
- 在其中编写
1.3,java项目的目录结构
-
动态项目的目录结构
– 项目根目录
– WEB-INF目录
– web.xml:web项目的核心配置文件
– classes目录:放置字节码文件的目录
– lib目录:放置依赖的jar包
1.4,tomcat运行出错
-
tomcat执行jdk版本低于编译jdk版本(500),
2,HTTP
2.1,HTTP的请求报文
2.1.1,HTTP的请求行
请求行(Request Line)分为三个部分:请求方法、请求地址和协议及版本,以CRLF(rn)结束。HTTP/1.1 定义的请求方法有8种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE,最常的两种GET和POST
例如:
GET /cache/fpid/chromelib_0108.js HTTP/1.1
2.1.2,HTTP的请求方法
仅有POST、PUT以及PATCH这三个方法时会包含请求体,而GET、HEAD、DELETE、CONNECT、TRACE、OPTIONS这几个方法时不包含请求体。
2.1.3,HTTP的请求头
Header | 解释 | 示例 |
---|---|---|
Accept | 指定客户端能够接受的内容类型 | Accept: text/plain, text/html,application/json |
Accept-Charset | 浏览器可以接受的字符编码集。 | Accept-Charset: iso-8859-5 |
Accept-Encoding | 指定浏览器可以支持的web服务器返回内容压缩编码类型。 | Accept-Encoding: compress, gzip |
Accept-Language | 浏览器可接受的语言 | Accept-Language: en,zh |
Accept-Ranges | 可以请求网页实体的一个或者多个子范围字段 | Accept-Ranges: bytes |
Authorization | HTTP授权的授权证书 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
Cache-Control | 指定请求和响应遵循的缓存机制 | Cache-Control: no-cache |
Connection | 表示是否需要持久连接。(HTTP 1.1默认进行持久连接) | Connection: close |
Cookie | HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。 | Cookie: $Version=1; Skin=new; |
Content-Length | 请求的内容长度 | Content-Length: 348 |
Content-Type | 请求的与实体对应的MIME信息 | Content-Type: application/x-www-form-urlencoded |
Date | 请求发送的日期和时间 | Date: Tue, 15 Nov 2010 08:12:31 GMT |
Expect | 请求的特定的服务器行为 | Expect: 100-continue |
From | 发出请求的用户的Email | From: user@email.com |
Host | 指定请求的服务器的域名和端口号 | Host: www.baidu.com |
If-Match | 只有请求内容与实体相匹配才有效 | If-Match: “737060cd8c284d8af7ad3082f209582d” |
If-Modified-Since | 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 | If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT |
If-None-Match | 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 | If-None-Match: “737060cd8c284d8af7ad3082f209582d” |
If-Range | 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag | If-Range: “737060cd8c284d8af7ad3082f209582d” |
If-Unmodified-Since | 只在实体在指定时间之后未被修改才请求成功 | If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT |
Max-Forwards | 限制信息通过代理和网关传送的时间 | Max-Forwards: 10 |
Pragma | 用来包含实现特定的指令 | Pragma: no-cache |
Proxy-Authorization | 连接到代理的授权证书 | Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
Range | 只请求实体的一部分,指定范围 | Range: bytes=500-999 |
Referer | 先前网页的地址,当前请求网页紧随其后,即来路 | Referer:https://www.baidu.com/baidu?tn=monline_3_dg&ie=utf-8&wd=1 |
TE | 首部字段 TE 会告知服务器客户端能够处理响应的传输编码方式及相对优先级。它和首部字段 Accept-Encoding 的功能很像,但是用于传输编码。首部字段 TE 除指定传输编码之外,还可以指定伴随 trailer 字段的分块传输编码的方式。应用后者时,只需把 trailer 赋值给该字段值 | TE: trailers,deflate;q=0.5 |
Upgrade | 向服务器指定某种传输协议以便服务器进行转换(如果支持) | Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 |
User-Agent | User-Agent的内容包含发出请求的用户信息 | User-Agent: Mozilla/5.0 (Linux; X11) |
Via | 通知中间网关或代理服务器地址,通信协议 | Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) |
Warning | 关于消息实体的警告信息 | Warn: 199 Miscellaneous warning |
如果是POST、PUT以及PATCH这三个方法,请求头部的最后会有一个空行,表示请求头部结束,接下来为请求体,这一行非常重要,必不可少
2.2,HTTP的响应报文
HTTP响应报文主要由状态行、响应头部、空行以及响应数据组成。
2.2.1,状态行
由3部分组成,分别为:协议版本,状态码,状态码描述。其中协议版本与请求报文一致,状态码描述是对状态码的简单描述
状态码:
1xx:指示信息--表示请求已接收,继续处理。
2xx:成功--表示请求已被成功接收、理解、接受。
3xx:重定向--要完成请求必须进行更进一步的操作。
4xx:客户端错误--请求有语法错误或请求无法实现。
5xx:服务器端错误--服务器未能实现合法的请求
常见的有:
200:响应成功
302:跳转,跳转地址通过响应头中的Location属性指定(jsp中forward和redirect之间的区别)
400:客户端请求有语法错误,无法被服务器识别
403:服务器接收到数据,但是拒绝提供服务(认证失败)
404:请求资源不存在
500:服务器内部错误
2.2.2,响应头部
与请求头部类似,为响应报文添加了一些附加信息
响应头 | 说明 |
---|---|
Server | 服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。 |
Content-Type | 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。 由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。 |
Content-Length | 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。 如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStream, 完成后查看其大小,然后把该值放入Content-Length头, 最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容 |
Content-Charset | 响应正文使用的编码 |
Content-Encoding | 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。 利用gzip压缩文档能够显著地减少HTML文档的下载时间。 Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。 因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader(“Accept-Encoding”))检查浏览器是否支持gzip, 为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。 |
Location | 表示客户应当到哪里去提取文档。Location通常不是直接设置的, 而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。 |
Content-disposition | Content-Disposition响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。例: Content-Disposition: inline inline为内联 Content-Disposition: attachment; filename=“filename.jpg” attachment为附件,filename为附件名称 |
2.2.3,响应数据
用于存放需要返回给客户端的数据信息。
3,servlet
Servlet :控制器; 小服务程序
3.1,Servlet的使用
-
配置文件
- 定义一个类,实现Servlet接口
- 实现接口中的抽象方法
- 配置web.xml文件
<servlet> <!--<servlet-name为servlet-class的别名>--> <servlet-name>begin</servlet-name> <!--<servlet-class为实现Servlet接口的类的全名(包括包的名字)>--> <servlet-class>first.Begin</servlet-class> </servlet> <servlet-mapping> <servlet-name>begin</servlet-name> <!--<url-pattern为映射的虚拟路径>--> <url-pattern>/begin</url-pattern> </servlet-mapping>
-
使用@WebServlet注解
@WebServlet("path");//path为Servlet实现类的uri
3.2,Servlet的生命周期方法
-
创建时调用init方法(一次)
-
默认情况下,当实现类的虚拟路径第一次被访问时创建
-
可以通过配置web.config来指定Servlet实现类的创建时机
<servlet> <servlet-name>begin</servlet-name> <servlet-class>first.Begin</servlet-class> <!--<load-on-startup值为负整数则第一次访问时创建,值为0或者正整数则服务器启动时创建>--> <load-on-startup>5</load-on-startup> </servlet>
-
Servlet实现类的init方法,只执行一次,说明Servlet只被创建了一次,所以,Servlet是单例的
- 多个用户同时访问,可能造成线程安全
- 解决方法:尽量使用局部变量,避免使用成员变量,即使定义了成员变量,也要避免去修改该成员变量的值
-
-
使用服务时执行service方法(多次)
- 每次访问时调用
-
销毁时调用destroy方法(一次)
- 服务器正常关闭时调用、
3.3,Servlet的实现类HttpServlet
HttpServlet类实现了Servlet接口中的方法,在service方法中调用不同的方法实现不同的请求调用,例如post请求调用doPost方法
3.4,Request
3.4.1,Request的继承体系
-- javax.servlet.ServletRequest(接口)
-- javax.servlet.http.HttpServletRequest(接口)
-- org.apache.catalina.connector.RequestFacade(实现类)
3.4.2,Request的作用
1,获取请求相关数据
-
获取请求行数据
//以http://localhost:8080/web01/bh为例 //获取请求方式: String getMethod(); //获取虚拟目录,例如:/web01 String getContextPath(); //返回此请求的 URL 中调用 servlet 的部分。例如:/bh String getServletPath(); //返回get方式的请求参数 String getQueryString(); //获取请求的URI,例如:/web01/bh String getRequestURI(); //获取请求的URL,例如:http://localhost:8080/web01/bh String getRequestURL(); //获取协议及版本,例如:HTTP/1.1 String getProtocol(); //返回发送请求的客户端或最后一个代理的 Internet 协议 (IP) 地址。例如: String getRemoteAddr();
-
获取请求头数据
//通过请求头参数的名称来获取对应的值 String getHeader(String name); //获取所有的请求头名称 Enumeration<String> getHeaderNames(); //*Enumeration可通过迭代方式访问,hasMoreElements()等同于iterator的hasNext()方法,nextElements等同于iterator的next()方法
-
获取请求体数据(先获取流对象,再通过读取操作获取数据)
//获取字符输入流 BufferedReader getReader(); //文件上传 ServletInputStream getInputStream();
-
获取请求参数名称与请求参数值的通用方式
//根据参数名称获取参数值(适用于一个参数名称只对应一个参数值,例如 name=lisi) String getParameter(String name); //根据参数名称获取多个参数值(适用于一个参数名称对应多个参数值,例如hobby=playgame&hobby=shopping) String getParameterValues(String name); //获取请求的所有参数名称 Enumeration<String> getParameterNames(); //将所有参数和参数值封装成map集合 Map<String,String[]> getParameterMap();
-
中文乱码:
-
tomcat解决了get请求方法的参数值的中文乱码问题,post请求方法的参数值仍然存在中文乱码问题
-
解决方法:在获取参数值之前,设置request的字符编码:
request.setCharacterEncoding("utf8");//这个编码应与网站编码一致
-
-
2,请求转发
//返回一个RequestDispatcher对象,该对象充当位于给定路径上的资源的包装器。
RequestDispatcher dispatcher= request.getRequestDispatcher(String path);
//将来自servlet的请求转发到服务器上的另一个资源(servlet、JSP文件或HTML文件)。
dispatcher.forward(ServletRequest request, ServletResponse response);
//将上述两行合并得到如下代码
request.getRequestDispatcher(String path).forward(ServletRequest request,ServletResponse response);
3.5,域对象
-
共有的方法
//方法 //在Request域对象中存储属性键值对 void setAttribute(String name,Object o); //返回指定属性的值,如果该属性不存在,返回null Object getAtrribute(String name); //通过属性名称(键)移除键值对 void removeAttribute(String name);
-
Request域对象
-
//获取ServletContext对象 ServletContext servletContext=request.getServletContext(); //this为继承HttpServlet的类 ServletContext servletContext=this.getServletContext();
-
PageContext对象
3.6,Response
3.6.1,设置相关信息
//设置响应行,格式:HTTP/1.1 200 OK
//设置response状态码
setStatus(int sc);
//设置响应头
setHeader(String name,String value);
//设置响应体
//获取输出流
PrintWriter getWriter();
ServletOutputStream getOutputStream();
//使用输出流将内容传输到客户端
3.6.2,重定向的两种方式
//方式一,设置状态码和响应头中的location参数
resp.setStatus(302);
// /web01/begin为Servlet实现类的uri
resp.setHeader("location","/web01/begin");
//方式二,使用response.sendRedirect();
// /web01/begin为Servlet实现类的uri
resp.sendRedirect("/web01/begin")
重定向与转发:
重定向 | 访问地址发生改变 重定向可以访问其他服务器的资源 浏览器重定向会发送两次请求 |
---|---|
转发 | 访问地址不改变 只能访问当前服务器中的资源 浏览器只发送一次请求 |
3.6.3,response的中文乱码
//以下操作都是在获取流之前进行操作
//设置流的默认编码(可不设置)
response.setCharacterEncoding("utf8");
//设置响应头中响应内容的编码格式(可以只写这一行代码,上面一行可以不写)
response.setHeader("content-type","text/html;charset=utf8");
//设置响应头中响应内容的编码格式(response提供的方法),与上面一行代码作用相同
response.setContentType("text/html;charset=utf8");
3.7,绝对路径的使用
- 例如http://localhost:8080/web01/form.html
- 例如/web01/form.html
- 判断该路径是谁去访问:
- 服务器:不加虚拟目录(加了报错)
- 客户端:必须加虚拟目录(不加报错)
- 建议使用request.getContextPath()获取虚拟目录,然后拼接得到完整路径(这样操作不用担心虚拟目录发生变化)
3.8,ServletContext的功能
-
获取MIME类型
this.getServletContext().getMimeType("a.jpg")//a.jpg为文件的名称
-
域对象
//方法 //在Request域对象中存储属性键值对 void setAttribute(String name,Object o); //返回指定属性的值,如果该属性不存在,返回null Object getAtrribute(String name); //通过属性名称(键)移除键值对 void removeAttribute(String name);
-
获取文件的服务器路径
//b.txt在web目录下,获取它的服务器绝对路径 this.getServletContext().getRealPath("/b.txt"); //c.txt在WEB-INF目录下,获取它的服务器绝对路径 this.getServletContext().getRealPath("/WEB-INF/c.txt"); //a.txt在src目录下,获取它的服务器绝对路径 this.getServletContext().getRealPath("/WEB-INF/classes/a.txt");
3.9,文件下载
//通过req.getParameter(String name)获取到文件名称
//获取ServletContext对象
ServletContext servletContext=this.getServletContext();
//获取文件的服务器路径
String path=servletContext.getRealPath(String s);
//设置Content-Disposition和Content-Type
resp.setHeader("Content-disposition","attachment; filename=xxxx.xx");
//fileName为文件名称,通过getMimeType方法获取到Mime类型
resp.setContentType();resp.setContentType(servletContext.getMimeType(fileName));
//通过输入流加载s的内容然后通过输出流传输s的内容到浏览器
File file=new File(path);
//获取输出流
ServletOutputStream outputStream=resp.getOutputStream();
try(InputStream inputStream=new FileInputStream(file)){
byte[] arr=new byte[20*1024];
int len=0,count=0;
while((len=inputStream.read(arr,0,arr.length))!=-1){
outputStream.write(arr,0,len);
count+=len;
}
outputStream.flush();
System.out.println(""+count);
resp.setHeader("content-length",""+count);
}
- 中文文件名显示问题的解决方案:
//根据不同浏览器对文件名进行不同的编码,我们可以定义一个fileName编码方法,该方法接收User-Agent和fileName
public static String getFileName(String agent,String fileName){
if(agent.contains("MSIE")){
//ie浏览器
fileName=URLEncoder.encode(fileName,"utf8");
fileName=fileName.replace("+"," ");
}else if(agent.contains("Firefox")){
BASE64Encoder base64Encoder=new BASE64Encoder();
fileName="=?utf8?B?"+base64Encoder.encode(fileName.getBytes("utf8")+"?=");
}else{
fileName=URLEncoder.encode(fileName,"utf-8");
}
}
- 在热部署模式下同一个文件夹内的图片复制粘贴可能不会动态加载(未解决)
4,会话技术
-
一次会话:浏览器第一次给服务器发送请求时,会话建立,当有一方断开时,会话结束
-
功能:在一个会话的范围内的多次请求间,共享数据
-
方式:Session和Cookie(前者数据保存在服务器上,后者保存在浏览器上)
4.1,Cookie对象
4.1.1 使用步骤:
-
创建Cookie对象
Cookie cookie=new Cookie("name01","value01");//name01为Cookie的name,value01为Cookie的value;
-
通过Response返回Cookie对象给浏览器
response.addCookie(cookie);
-
浏览器在下一次请求中将会携带Cookie对象
以第一步创建的Cookie对象为例:通过浏览器抓包,当我们用response返回一个Cookie对象给浏览器时,浏览器接收到的响应头中出现了
Set-Cookie: name01=value01;
当再次发起请求时,浏览器的请求头中出现了
Cookie: name01=value01;
-
服务器获取Cookie,拿到数据
//获取请求携带的所有Cookie
Cookie[] cs = request.getCookies();
//遍历Cookie,获取所有的name和value
if(cs!=null){
for (Cookie c : cs) {
System.out.println("name:"+c.getName());
System.out.println("value:"+c.getValue());
}
}
4.1.2 Cookie的持久化存储
注意:当修改浏览器请求携带的cookie的Value和MaxAge时,需要重新通过Response.addCookie(Cookie cookie)将修改后的cookie返回给浏览器)
Cookie cookie=new Cookie("name","value");
cookie.setMaxAge(int seconds);
//seconds>0,即设置持久化存储,数字代表持久化存储的秒数
cookie.setMaxAge(200);
//seconds<0,代表默认值
cookie.setMaxAge(-1);
//seconds=0,代表清空cookie信息
cookie.setMaxAge(0);
//修改cookie的值
public void setValue(String newValue);
4.1.3 Cookie的共享
//在同tomcat服务器下部署多个项目,实现多个项目共享Cookie
//使用Cookie的setPath(String path)方法
Cookie.setPath("/");//实现多个项目共享Cookie,不设置则默认当前项目下共享Cookie
//不同tomcat服务器间共享Cookie数据
Cookie.setDomain(String path);//设置path为一级域名,即可实现不同tomcat服务器之间共享Cookie数据
//示例,实现tieba.baidu.com和news.baidu.com之间Cookie共享
Cookie.setDomain(".baidu.com");
4.1.4 Cookie的特点和作用
-
特点
-
作用
- Cookie一般用于存储少量的不敏感数据
- 在不登陆的情况实现服务器对浏览器的身份识别
4.1.5 Cookie的注意事项
- HttpServletResponse提供的Cookie操作只有一个addCookie(Cookie cookie),所以想要修改Cookie只能使用一个同名的Cookie来覆盖原先的Cookie。如果要删除某个Cookie,则只需要新建一个同名的Cookie,并将maxAge设置为0,并覆盖原来的Cookie即可。
- 新建的Cookie,除了value、maxAge之外的属性,比如name、path、domain都必须与原来的一致才能达到修改或者删除的效果。否则,浏览器将视为两个不同的Cookie不予覆盖。
- 值得注意的是,从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name和value属性,maxAge属性只被浏览器用来判断Cookie是否过期,而不能用服务端来判断。
4.1.6 一个使用Cookie的小测试
//使用cookie实现第一次登陆显示欢迎,之后登录显示上一次登陆的时间
response.setContentType("text/html;charset=utf-8");
String value=null;
//获取cookie数组
Cookie[] cs = request.getCookies();
PrintWriter writer = response.getWriter();
if (cs != null){
for (Cookie c : cs) {
//判断浏览器的请求是否携带指定的cookie,如果是,则获取urlencoding解码后cookie的值
if(c.getName().equals("visitedTime")){
value= URLDecoder.decode(c.getValue(),"utf8");
}
}
}
//格式化数据
SimpleDateFormat sdf=new SimpleDateFormat();
sdf.applyPattern("yyyy年MM月dd天HH时mm分ss秒 a");
Date date=new Date();
String s=sdf.format(date);
//返回cookie
Cookie cookie=new Cookie("visitedTime", URLEncoder.encode(s,"utf-8"));
cookie.setMaxAge(60*60*24*30);
response.addCookie(cookie);
if(value!=null){
writer.write("您上次的访问时间为"+value);
}else{
writer.write("欢迎您第一次访问");
}
4.2,Session对象
4.2.1 JSESSIONID
当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。
保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID(tomcat返回的是JSESSIONID)。但cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。
经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面。还有一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器
4.2.2,Session的创建
当调用request.getSession()时,如果浏览器携带的cookie中没有JSESSIONID或者服务器中没有JSESSION对应的Session对象,则服务器会创建一个Session对象
//当调用request.getSession()时,如果浏览器携带的cookie中没有JSESSIONID或者服务器中没有JSESSION对应的Session对象,则服务器会创建一个Session对象,并返回给浏览器一个Cookie对象,该对象的name为JSESSIONID,value为Session的id。例,这是浏览器未携带JESSIONIDCookie后服务器getSession后浏览器接收到的响应头中的内容
Set-Cookie: JSESSIONID=FA87CAA5EDC0539D744E99EEA226D84E; Path=/web01; HttpOnly
//服务器自动创建的JSESSIONID Cookie是存放在浏览器内存中,当浏览器关闭之后,该Session就被摧毁 。我们可以手动创建一个name为JSESSIONID的Cookie,然后使用setMaxAge()函数来设置它的持久性
//例:设置session对应的cookie在浏览器持久化存储一天
Cookie cookie=new Cookie("JSESSIONID",session.getId());
cookie.setMaxAge(60*60*24);
服务器关闭之前与关闭重启之后获取的Session对象是同一个吗?不是同一个,但是tomcat在服务器正常关闭的时候会将所有的Session对象序列化存储到硬盘(称为Session的钝化),在下次正常启动的时候加载成为Session对象(称为Session的活化),实现了关闭之前与关闭之后数据不丢失(在idea上失效,失效原因)
/*
我们可以在tomcat上直观的看到序列化存储的文件
步骤
将相关项目部署到webapps目录下
点击apache tomcat目录下的\bin\startup.bat,启动tomcat服务器
访问相关页面使得服务器创建Session对象
点击apache tomcat目录下的\bin\shutdown.bat
可以看到在 work/Catalina/localhost/项目名称 下出现了SESSIONS.ser文件,当重启tomcat之后该文件消失
*/
idea使用的是并不是tomcat中的work目录,而是idea启动tomcat时打印的CATALINA_BASE目录下的work目录,当idea关闭tomcat服务时,tomcat会将Session序列化存储到CATALINA_BASE/work/Catalina/localhost/项目名称下,idea重启之后,会删除CATALINA_BASE/work目录,然后重新生成该目录,所以里面的序列化文件就被删除了
4.2.3,Session的销毁
什么时候Session会被销毁:
-
服务器被关闭
-
Session对象调用invalidate()方法
-
Session默认失效时间:30分钟,可以通过设置web.xml中session-config标签内的session-timeout的内容来更改失效时间
<session-config> <session-timeout>30</session-timeout> </session-config>
4.2.4,Session的特点
- 可用于存储一次会话内的数据,无大小限制,无类型限制
4.2.5,Session与Cookie的区别
- Session数据安全,Cookie不安全
- Session数据存储于服务器,Cookie存储于浏览器
- Session依赖于Cookie
5,jsp
jsp本质上就是一个Servlet
5.1,jsp的脚本
- <% 代码 %>
- 该jsp被解析后,java代码将会被放入对应的Servlet的Service方法中,可以在其中编写Service方法支持的功能
- <%! 代码 %>
- jsp解析后,java代码将会被放入Servlet实现类的类体中的声明变量部分,所以可以在此部分声明成员变量
- <%= 变量%>
- 输出变量到页面上
5.2,jsp的内置对象
- pageContext (PageContext,域对象)
- 当前页面内共享数据,该对象可以获取其他八个内置对象
- request (HttpServletRequest,域对象)
- 一次请求内共享数据(可转发到其他页面)
- session (HttpSession,域对象)
- 一次会话内共享数据
- application (ServletContext,域对象)
- 每个web应用在创建时web容器都会创建一个ServletContext对象,在该web应用停止服务时ServletContext被销毁,作用范围为一个web应用内
- response (HttpServletResponse)
- 在jsp页面中,response的输出流输出内容总是优先于out输出流输出内容,不管response输出流的输出语句位于什么位置
- out (JSWriter)
- 输出对象
- page (Object)
- 当前页面对象,this
- config (ServletConfig)
- Servlet的配置对象
- exception (Throwable)
- 异常对象
5.3,jsp的指令
-
page:配置jsp页面的相关格式
-
contentType:等同于response.seDtContentType();
- 设置当前响应体的mime类型以及使用的字符集
- 在高级集成开发环境中,开发工具会根据该属性自动设置当前jsp页面的编码(低级开发工具中,则需要设置pageEncoding属性设置当前页面的的字符集)
-
import:导包
-
errorPage:当前页面发生异常后,自动跳转到该属性指定的页面
-
isErrorPage:表示当前页面是否是错误页面
- true:是,可以使用错误页面独有的内置对象exception
- false:否
-
-
include:导入其他页面
<%@include file="top.jsp" %>//导入top.jsp页面
-
taglib:导入资源
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> //prefix:自定义前缀
5.4,注释
-
html注释(注释和注释内容会被response正常返回给浏览器)
<!-- -->
-
jsp注释(推荐,注释和注释内容不会被response返回给浏览器)
<%-- --%>
6,EL
EL百度百科:Expression Language,表达式语言
6.1,EL运算符
+、-、*、/、%、div、mod、>、<、<=、>=、==、!=、&&、||、!、and、not、or
empty(判断字符串、集合、数组等对象是否为null或者长度为0),示例:${empty list}、not empty(与empty相反)
//例:
<%
List list=new ArrayList();
request.setAttribute("list",list);
%>
${empty list}
//结果为true
6.2, EL获取四大域对象中的值。
表达式:${域名称.键}或者${域名称[键名]}
,域名称如下:
1. pageScope:表示pageContext域对象
2. requestScope:表示request域对象
3. sessionScope:表示session域对象
4. applicationScope:表示application(servletContext)域对象
6.3,${键名}
从最小的域对象中查找是否有该键对应的值,找到为止
6.4,通过EL表达式获取域中对象的值
- 表达式:
${域名称.键名.属性名}或者${域名称[键名][属性名]}
/*
User对象是一个java bean
*/
//jsp中的内容
<h1>el获取域中对象的值</h1>
<%
User user=new User();
user.setName("李四");
user.setAge(20);
user.setBirthday(new Date());
request.setAttribute("user",user);
%>
${requestScope.user.name}
${requestScope.user.age}
${requestScope.user.birthday.month}
${requestScope["user"]["age"]}
//原理:el表达式会调用对象的getter方法来获取对应属性的值
6.5,通过EL表达式获取域中list集合指定下标的值
- 表达式
${域名称.键名[索引]}或者${域名称[键名][索引]}
<%
List list=new ArrayList();
list.add("10");
list.add("100");
request.setAttribute("list",list);
%>
${requestScope["list"][1]}
${requestScope.list[0]}
6.6,通过EL表达式获取域中map集合的值
- 表达式
${域名称.键名.keyName}或者${域名称[键名][keyName]}
6.7,EL中的隐式对象
-
pageContext:
-
获取jsp其他八个内置对象
-
获取虚拟目录
//获取其他内置对象 ${pageContext.request}<br> ${pageContext.request}<br> ${pageContext.out}<br> ${pageContext.exception}<br> . . . //获取虚拟目录 ${pageContext.request.contextPath}<br> //可以用于jsp页面链接的拼接使用,这样不用担心虚拟目录的改变 //例: <form action="${pageContext.request.contextPath}/index.jsp" method="post" > <input type="submit"> </form>
-
7,jstl标签库
JSTL(Java server pages standarded tag library,即JSP标准标签库)
7.1,导入jstl
- 导入jstl相关jar包
- 引入标签库:<%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core”%>,其中c是taglib普遍使用的别名,可换用其他值
- 使用标签
7.2,常用标签
-
if:作用相当于if语句
-
属性
-
test:必须属性,不写会报错,接收一个boolean表达式,如果表达式为true,显示if标签之间的内容,否则不显示
-
用法:
<c:if test="true">我一定会被显示</c:if> <c:if test="false">我一定不会被显示</c:if>
-
c:if标签没有else语句
-
-
-
choose:作用相当于switch语句
-
使用choose标签声明 相当于switch声明
-
使用when标签做判断 相当于case
-
使用otherwise标签做其他情况判断 相当于default
-
示例:
<% request.setAttribute("number",8); %> <c:choose> <c:when test="${number%7==1}">星期一</c:when> <c:when test="${number%7==2}">星期二</c:when> <c:when test="${number%7==3}">星期三</c:when> <c:when test="${number%7==4}">星期四</c:when> <c:when test="${number%7==5}">星期五</c:when> <c:when test="${number%7==6}">星期六</c:when> <c:when test="${number%7==0}">星期日</c:when> <c:otherwise>输入错误</c:otherwise> </c:choose>
-
-
foreach:作用相当于for语句
- 简单遍历
<c:forEach begin="1" end="10" step="1" var="i" varStatus="s"> i:${i}<br> index:${s.index}<br> count:${s.count}<br> <br> </c:forEach> //相当于java中的for(int i=1;i<=10;i++){} //属性: 1.begin:开始值 2.end:结束值 3.var:临时变量 4.step:步长 5.varStatus:循环状态对象,可以通过该对象.index获取当前循环变量的值,该对象.count获取当前当前循环的次数
- 增强for循环(foreach)
<% List<String> string=new ArrayList<String>(); string.add("1"); string.add("2"); string.add("3"); request.setAttribute("list",string); %> <c:forEach items="${list}" var="string" varStatus="s"> s.inde:${s.index};s.count:${s.count};s:${string};<br> </c:forEach> //属性: 1.item:容器对象 2.var:容器中元素的临时变量 3.varStatus:循环状态对象 4.index:容器中元素的索引,从0开始 5.count:循环次数,从1开始
8,Filter
8.1,使用步骤
-
定义一个类,实现接口Filter
-
复写方法
-
配置拦截路径
-
xml配置
<filter> <!-- path为Filter接口实现类的路径 --> <filter-name>filter01</filter-name> <filter-class>path</filter-class> </filter> <filter-mapping> <filter-name>filter01</filter-name> <!-- url-pattern为过滤器生效的访问路径, url-pattern为"/*"(这里加引号只是为了防止/和*一起转义)代表访问该应用程序下所有的servlet实现类都需要经过该过滤器 --> <url-pattern>pattern</url-pattern> </filter-mapping>
-
@WebFilter注解配置
@WebFilter(pattern);
当我们设置Filter实现类的拦截路径为"/*"时,在idea上启动项目,发现Filter被调用了两次
原因:浏览器打开index.jsp的同时会像服务器发送两个请求,一个是index.jsp的访问请求,一个是favicon.ico的访问请求
8.2,Filter的执行流程
- 执行chain.doFilter(req, resp)该句代码之前的内容
- 执行chain.doFilter(req, resp);请求到达请求的页面
- 请求到达请求的页面得到相应的资源之后返回Filter,继续执行执行chain.doFilter(req, resp)之后的内容
8.3,Filter的生命周期
- init:服务器启动之后创建Filter对象,并调用init方法,只执行一次,所以该方法可以用来加载资源。
- doFilter:请求如果符合Filter的url-pattern,就会拦截请求并执行doFilter方法
- destroy:在服务器正常关闭时,就会调用destroy方法,只执行一次,所以可以用来释放资源
8.4,Filter的配置
- 拦截路径配置
- 具体资源路径
- 示例:/index.jsp; 只有访问index.jsp资源时,过滤器才会被执行
- 拦截目录
- 示例:/user/*; 访问/user下的所有资源时,过滤器都会被执行
- 后缀名拦截
- 示例:*.jsp; 访问所有后缀名为.jsp的资源时,过滤器都会被执行
- 拦截所有资源
- 示例:/*; 访问所有资源时,过滤器都会被执行
- 具体资源路径
- 拦截方法配置
- 注解配置
- 设置注解的dispatcherTypes属性
- DispatcherType.REQUEST:默认值,当请求为浏览器请求,过滤器执行
- DispatcherType.FORWARD:当请求为转发时,过滤器执行
- DispatcherType.INCLUDE:当请求为包含请求时,过滤器执行
- DispatcherType.ERROR:当请求为错误跳转请求时,过滤器执行
- DispatcherType.ASYNC:当请求为异步访问请求时,过滤器执行
- 设置注解的dispatcherTypes属性
- web.xml配置
在<filter-mapping></filter-mapping>标签体中添加一个<dispatcher></dispatcher>标签并设置它的值。 dispatcher标签的取值有: 1.REQUEST:默认值,当请求为浏览器请求,过滤器执行 2.FORWARD:当请求为转发时,过滤器执行 3.INCLUDE:当请求为包含请求时,过滤器执行 4.ERROR:当请求为错误跳转请求时,过滤器执行 5.ASYNC:当请求为异步访问请求时,过滤器执行
- 注解配置
8.5,过滤器链
- 执行顺序
如果有两个过滤器,过滤器1和过滤器2:- 过滤器1
- 过滤器2
- 请求访问资源
- 继续执行过滤器2未执行完的代码
- 继续执行过滤器1未执行完的代码
- 过滤器先后顺序
- 注解配置
- 根据字符串比较规则比较Filter实现类的类名,值小先执行
- 例:AFilter 和 BFilter,AFilter先执行
- web.xml配置
- 看<filter-mapping>的定义顺序
- 注解配置
-
8.6,使用过滤器和动态代理实现敏感词汇过滤
- 思路:
- 在过滤器中对request进行增强,将增强的request对象替换原来的request对象
- 对request对象增强什么呢?
- 当调用request方法时,就进行判断,如果方法名为getParameter或getParameterMap…(只要是获取参数的方法),就将方法返回的值进行一个过滤,如果包含敏感词汇,就替换成***,这里用到了代理设计模式
- 代理设计模式:
- 代理对象代理真实对象,对真实的一些功能进行增强
- 实现方式:
- 静态代理
- 动态代理(使用)
- 动态代理的步骤:(代理对象和真实对象实现相同的接口)
- 代理对象=Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
- 使用代理对象调用方法
- 对方法进行增强
- 动态代理的步骤:(代理对象和真实对象实现相同的接口)
- 增强方式:
- 增强参数列表
- 增强返回值类型
- 增强方法执行逻辑
9,Listener监听器
9.1,ServletContextListener接口
规定了ServletContext对象创建后和销毁前调用方法的规范
- void contextDestroyed(ServletContextEvent sce);在ServletContext对象被正常销毁之前会调用该方法
- void contextInitialized(ServletContextEvent sce);在ServletContext对象创建后会调用该方法
- 步骤:
- 定义一个类,实现ServletListener接口
- 复写方法
- 配置
- web.xml
<listener> <listener-class>path<listener-class> </listener>
- 注解
- @WebListener