JavaWeb知识点总结
一、Servlet作用
- 接收请求
- 处理请求
- 完成响应
二、Servlet创建和使用流程
1、创建类继承HttpServlet
public class OneServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
2、在web.xml
配置Servlet位置和地址映射
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--tomcat初始化打开的页面-->
<welcome-file-list>
<welcome-file>/index.html</welcome-file>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
<!--将servlet接口实现类类路径地址交给tomcat -->
<servlet>
<!-- 为我们自定义的Servlet起一个名称-->
<servlet-name>OneServlet</servlet-name>
<!-- 该servlet具体的包下的地址-->
<servlet-class>org.lc.controller.OneServlet</servlet-class>
<!-- load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
当值小于0或者没有指定时,则表示容器在该Servlet被请求时,才会去加载。
正数的值越小,该Servlet的优先级就越高,应用启动时就优先加载。
当值相同的时候,容器就会自己选择优先加载。-->
<!--tomcat启动时就会创建 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 为自定义servlet配置地址映射-->
<servlet-mapping>
<!-- 指定servlet-->
<servlet-name>OneServlet</servlet-name>
<!-- 指定请求地址-->
<url-pattern>/one</url-pattern>
</servlet-mapping>
</web-app>
三、Servlet生命周期
1、初始化init()。Servlet对象只会初始化一次
若Servlet指定load-on-startup的配置,则在tomcat服务器启动时 该Servlet即被初始化
若Servlet没有指定load-on-startup配置,则在第一次请求时初始化Servlet
<servlet> <!-- 为我们自定义的Servlet起一个名称--> <servlet-name>OneServlet</servlet-name> <!-- 该servlet具体的包下的地址--> <servlet-class>org.lc.controller.OneServlet</servlet-class> <!-- load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。 当值小于0或者没有指定时,则表示容器在该Servlet被请求时,才会去加载。 正数的值越小,该Servlet的优先级就越高,应用启动时就优先加载。 当值相同的时候,容器就会自己选择优先加载。--> <!--tomcat启动时就会创建 --> <load-on-startup>1</load-on-startup> </servlet>
2、调用service()方法,处理post或者get及其他请求。每次请求都会调用该service方法
3、调用destory()方法,当服务区关闭时就会调用此方法。该方法只会在服务器结束时调用一次
注意:
- Servlet为单实例多线程,所有不要在Servlet实例中写共享变量。
四、HttpServletResponse和HttpServletRequest
HttpServletResponse
封装服务端返回给浏览器的数据
HttpServletRequest
封装客户端请求的数据
1、HttpServletResponse
1)向客户端返回中文数据
浏览器根据不同的响应体中的ContentType类型类解析不同的二进制数据
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//根据响应体对象返回数据给浏览器 注意:必须先指定字符编码 然后获取PrintWriter流对象。
response.setContentType("text/html;charset=UTF-8");
PrintWriter printWriter = response.getWriter();
printWriter.write("hello world<br/>");
printWriter.write("你好世界!");
printWriter.flush();
printWriter.close();
}
2)重定向其它地址
浏览器会
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//重定向到我们项目下的指定页面
resp.sendRedirect("success.html");
//重定向到我们指定的地址
//resp.sendRedirect("http://www.baidu.com");
}
3)若禁用cookie,可采用编码重定向
详细参考cookie禁用
使用response中的encodeRedirectURL()
方法优化其请求路径。自动判断,如果禁用cookie则帮我们携带上session否则不会携带
<a href="<%=response.encodeRedirectURL("sessiontest.jsp")%>">去其他页面</a>
4)获取响应流对象
一般用作文件下载。详细参考文件下载
2、HttpServletRequest
1)获取请求参数
<form action="/one" method="get">
用户名<input type="text" name="username"><br/>
<input type="submit" value="提交">
</form>
<h1>POST请求</h1>
<form action="/one" method="post">
用户名<input type="text" name="username"><br/>
爱好: 篮球<input type="checkbox" name="interest" value="lanqiu">
足球<input type="checkbox" name="interest" value="zuqiu">
排球<input type="checkbox" name="interest" value="paiqiu">
性别:
男:<input type="radio" name="gender" value="male">
女:<input type="radio" name="gender" value="female">
<input type="submit" value="提交">
</form>
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
//1、获取单个值 文本框,单选框
String username = req.getParameter("username");
String gender = req.getParameter("gender");
System.out.println(username);
System.out.println(gender);
//2、获取某个参数的多个值,例如多选框
String[] interests = req.getParameterValues("interest");
System.out.println(Arrays.toString(interests));
//3、获取请求头信息
System.out.println(req.getHeader("Host"));
System.out.println(req.getHeader("Content-Type"));
System.out.println(req.getHeader("Accept"));
}
}
2)get和post请求处理乱码方式
- GET请求
①浏览器地址栏默认使用ISO-8859-1进行编码,所以我们需要用对应的编码格式解码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决get请求乱码
System.out.println(new String(req.getParameter("username").getBytes("ISO-8859-1"),"utf-8"));
}
②修改tomcat的解码规则
找到tomcat安装路径下的conf/server.xml文件:
在下列标签加入:URIEncoding="utf-8"
即可
<Connector connectionTimeout="30000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="utf-8" />
- PSOT请求
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决请求乱码 重新设置请求的编码格式,浏览器和我们服务器的编码和解码的格式不一致,所以我们需要在获取请求参数之前,同一设置编码格式
req.setCharacterEncoding("utf-8");
//第一种方式:设置文本类型并设置编码格式
req.setContentType("text/html;charset=utf-8");
//第二种方式:设置响应头
//req.addHeader("Content-Type","text/html;charset=utf-8");
//必须在获得PrintWriter流之前设置请求编码
PrintWriter printWriter = req.getWriter();
}
在我们设置响应的编码格式时:只需要设置内容类型和编码格式就行。
3)请求转发
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//转发到指定路径或请求
RequestDispatcher requestDispatcher = req.getRequestDispatcher("success.html");
//若转发到page/success.html页面,则:
//req.getRequestDispatcher("page/success.html");
//并转发请求和响应参数
requestDispatcher.forward(req, resp);
}
3)获得项目路径
// /servlet_test
String contextPath1 = req.getContextPath();
4)获取请求地址
request.getRequestURI();
获取请求的URI 不包含项目路径
request.getRequestURL();
获取请求的URL 包含项目路径(全路径)
5)获取请求流对象
- 若请求的enctytpe类型为 enctype="application/x-www-form-urlencoded"
- 则流ServletInputStream流中存储的为 请求参数和值。
- 若请求的enctytpe类型为 enctype="multipart/form-data"
- 则流ServletInputStream中存储为 一段段数据部分。
ServletInputStream inputStream1 = request.getInputStream();
3、转发和重定向的区别
转发(HttpServletRequest对象)
- 地址栏的路径不会更改
- 请求只会发生一次,即getRequestDispatcher("success.html")中的路径不会请求,只转发相应的请求和响应对象即可。
- 服务器内部完成
- 可以共享请求域数据(HttpServletRequest和HttpServletResponse)
- 必须是当前服务器的资源
- 可以访问WEB-IN下的资源
重定向(HttpServletResponse对象)
- 地址栏会变成getRequestDispatcher("success.html")中指定的转发路径
- 会发生两次请求
- 浏览器完成
- 不可以共享请求域数据(HttpServletRequest和HttpServletResponse)
- 可以是外部的资源
- 不可以访问WEB-IN下的资源
注意:我们只能一个请求对应一个响应,而且先处理请求操作再处理响应操作,不能有多个响应或者请求转发操作。
五、项目路径问题
我们一般新建javaweb选择,选择打包的方式:
上面的Application context:/servlet_test 默认为项目名称。可以把其设置为 / 即可
我们的Servlet在进行地址解析的时候web.xml中的地址解析为:
<servlet-mapping>
<servlet-name>TwoServlet</servlet-name>
<!--其中的 / 代表的是当前项目的路径 即http://localhost:8081/servlet_test/ 这也是为什么我们无需在页面访问时加 / 的原因了-->
<url-pattern>/two</url-pattern>
</servlet-mapping>
1、项目路径和服务器路径的区别:
项目路径:http://localhost:8081/servlet_test/ 携带项目名称
服务器路径:http://localhost:8081/ 以我们tomcat服务器的路径,不携带项目名称
2、页面路径访问问题(在页面请求中访问servlet也如此)
若设置了路径项目名称
- 如果请求路径以
/
开头的,则代表为当前服务器路径即 http://localhost:8081/,。需要在后面加上项目名称/ servlet_test
- 即访问的规则
<a href="/servlet_test/page/error.html">错误页面</a>
- 即访问的规则
- 如果请求路径没有以
/
开头,那么可以不加项目名称<a href="page/error.html">错误页面</a>
- 如果请求路径以
若没有设置项目路径名称,则请求加不加
/
都无所谓
3、Servlet中的路径问题
重定向路径
因为重定向是请求路径交给浏览器去访问,所以在Servlet中使用重定向时加
/
也代表当前服务器路径即 http://localhost:8081/, 需要加项目名 。用法和在页面访问路径一致response.sendRedirect("/servlet_test/success.html");
转发路径
因为转发是在服务器内部完成的,所有在Servlet中使用重定向时
/
代表当前项目路径即http://localhost:8081/servlet_test/
,所有我们使用/
开头作为路径时无需加项目路径req.getRequestDispatcher("/success.html").forward(req, resp);
//或者 req.getRequestDispatcher("success.html").forward(req, resp);
但是我们频繁的写项目路径不够灵活
使用
ServletContext
中的getContextPath
获取项目名称@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context=getServletContext(); // /servlet_test String contextPath = context.getContextPath(); resp.sendRedirect(contextPath+"/success.html"); }
使用
HttpServletRequest
中的getContextPath
方法获取项目名称// /servlet_test String contextPath1 = req.getContextPath();
六、JSP介绍
JSP其实就是一个Servlet, 服务器将jsp中的内容,从上到下依次逐行通过write("页面元素和标签")
流一行一行写入到浏览器的。
1、jsp脚本片段,编写java代码
在jsp中的声明的java代码都会加载到 Servlet 中的service中执行
- 打印到控制台:
<%
//在控制台打印 hello
System.out.println("hello");
%>
- 打印到页面:使用out对象
<%
//在页面打印 hello
out.println("hello");
%>
2、jsp表达式,在页面输出内容
在jsp中声明的jsp表达式会被加载到service中的write()
流方法中当做参数输出
<!--会在头部导入指定包-->
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!--会在页面输出日期-->
<%=new Date()%>
3、声明成员(少用)
在jsp中以<%! %>
中声明的代码相当于声明在 Servlet的service方法外面。当做成员参数或成员方法。
<%!private String name="张三";%>
<!--在页面输出 '张三'-->
<%=name%>
4、jsp注释
<%-- --%>
<%-- 我是注释 --%>
5、jsp指令
<%@ 指令名 属性名=属性值 %>
1)page指令
定义页面是如何解析
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2)include指令
静态包含。要包含的文件不需要翻译和编译的。静态包含的内容一般不动
<%@include file="register.html"%>
3)taglib指令
在页面引入标签库
<%@ taglib 属性名=属性值 %>
4)jsp指令
<jsp:指令名 属性名=属性值>
<!--动态包含 需要翻译和编译,常用于经常变动的页面。-->
<jsp:include page="register.jsp"></jsp:include>
<!--请求转发到指定页面-->
<jsp:forward page="register.jsp"></jsp:forward>
<!--请求并指定参数-->
<jsp:forward page="register.jsp">
<jsp:param name="username" value="张三"/>
</jsp:forward>
6、jsp九大隐含对象
可以在jsp中直接使用
1)隐含对象
Throwable exception=null
代表捕获异常对象
ServletConfig config=null
代表Servlet配置信息
- 获得当前jsp对应servlet的详细信息
- 获取servlet名称
config.getServletName();
JspWriter out=null
代表在页面输出数据的out对象
out.print("你好");
Object page=this;
代表当前jsp
主要使用this
, page仅代表Object
**HttpServletResponse **response
代表当前的响应对象
- 向页面输出内容
response.getWriter().write("hello");
- 向页面输出内容
2)四大域对象
域对象用来在其他资源共享数据。都可以通过setAttribute(K,V)
的形式设置数据。通过getAttribute(K)
的形式拿到数据
PageContext pageContext=null
代表当前页面对象
获取所有的其他的隐含对象
ServletRequest request1 = pageContext.getRequest(); Object page1 = pageContext.getPage();
作为域对象共享数据。只能在当前页面共享数据,离开页面无法共享
//设置 键-值 参数 pageContext.setAttribute("username", "张三"); //通过 键 获取值 System.out.println(pageContext.getAttribute("username"));
HttpSession session=null
代表会话对象
同一次会话共享数据, 浏览单打开--开始会话。 浏览器关闭--结束会话。使用不同web应用(Chrom,IE,Egde),就是不同的会话
HttpServletRequest request
代表当前封装的请求对象
在同一个请求对象中共享数据,只要是同一次请求,就可以共享数据。转发也可以共享数据
ServletContent application =null
代表整个web应用
- application 代表当前web应用。只要在同一个web应用都可以共享数据。web应用只要不卸载都可以访问
域对象 | 作用范围 | 起始时间 | 结束时间 |
---|---|---|---|
pageContext | 当前jsp页面 | 页面加载 | 离开页面 |
request | 同一个请求 | 收到请求 | 响应 |
session | 同一个会话 | 开始会话 | 结婚会话 |
application | 当前web应用 | web应用加载 | web应用卸载 |
7、EL表达式
EL表达式都是通过属性的get方法获取的。而不是属性。所以命名要以按照javabean的规范编写
EL表达式可以存在html标签中的属性中。或者任意hmtl标签内
1)获取页面显示域中的属性值${属性名}
2)要获取某个属性的值,直接${属性名.属性}
3)若el表达式中有多个属性名相同则会从四个域从小到大寻找,找到即停止
4)当有多个属性名相同时,则我们使用:
只能取出通过setAttribute()
设置的值
pageContext域中的数据使用pageScope(封装了pageContext中的所有的属性并封装为map)获取属性
${pageScope.属性}
request域中的数据使用requestScope(封装了requestScope中的所有的属性并封装为map)获取属性
${requestScope.属性}
session域中的数据使用sessionScope(封装了sessionScope中的所有的属性并封装为map)获取属性
${sessionScope.属性}
application域中的数据使用applicationScope(封装了applicationScope中的所有的属性并封装为map)获取属性
${applicationScope.属性}
5)el表达式可以取出指定的11隐含对象中的值,自己在<% %>
定义的不能够取出。
<%
String s = "jj";
%>
<!--不能够取出-->
el字符串s的值为: ${s} <br/>
<!--能够取出-->
字符串s的值为: <%=s%> <br/>
6)当我们的属性名取出的名称出现歧义时我们可以使用['属性']
的方式取出
可以通过${pageScope['属性名']['属性']}
或者${pageScope['属性名']}
7)使用el表达式中的的pageContext获取其他隐含对象,若要获取隐含对象中的其他属性,使用 .
即可
${pageContext.request.getAttribute("user")}
8)el获取其他隐含对象的值
- param 保存的请求参数是 key -- value (value只有一个)
- $
- paramsValues 保存的请求参数是 key --value (value有多个)
- $
- cookie 保存cookie信息
${cookie.键.value}
获取指定cookie键的value
9)el表达式运算
empty 判断是否为空,为空则为true,否则会false
${empty "你好"}
也可以在el表达式中写其他表达式的元素,三门运算符等
10)获取请求的上下文路径
${pageContext.request.contextPath}
<%@ page import="org.lc.entity.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
User user = new User(1, "admin", "123456");
String s = "jj";
%>
el字符串s的值为: ${s} <br/>
字符串s的值为: <%=s%> <br/>
pageContext:<%pageContext.setAttribute("pageContextAttr", "pageContextValue");%><br/>
user:<%pageContext.setAttribute("user", user);%><br/>
request:<%request.setAttribute("user", "requestValue");%><br/>
session:<%session.setAttribute("user", "sessionValue");%><br/>
application:<%application.setAttribute("user", "applicationValue");%><br/>
<hr/>
<%-- 以前获取共享数据的方式--%>
<%-- pageContext:<%=pageContext.getAttribute("pageContextAttr")%><br/>--%>
<%-- 现在使用el表达式--%>
<%--通过${属性值}--%>
pageContext:${pageContextAttr}<br/>
<%--直接获取属性对应的对象值--%>
user:${user}<br/>
<%--获取对象的属性--%>
user:${user.password}<br/>
user:${pageScope.user.username}<br/>
request:${requestScope.user}<br/>
session:${sessionScope.user}<br/>
application:${applicationScope.user}<br/>
${pageContext.request.getAttribute("user")}
${pageContext.request.contextPath}
</body>
</html>
七、Cookie
HTTP是无状态协议
- 无状态:服务器无法分辨每次请求来自谁
所以使用cookie是让浏览器去保存一份数据,以后每次访问的时候带上相应的数据。Cookie是服务器发给浏览器保存的
1、创建cookie
cookie由我们的服务器发送给浏览器创建并发送给浏览器设置保存,每次请求时都会携带此cookie。
在servlet中创建cookie
protected void create(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //可创建多个cookie //创建cookie 键 - 值 Cookie cookie=new Cookie("username","louchen"); //把cookie发送给浏览器 response.addCookie(cookie); response.getWriter().write("cookie发给你了..."); }
我们请求创建cookie后,发现在响应头中有Set-Cookie信息,则为Cookie信息
我们发现保存在浏览器的Cookie中,发现其过期时间Expire/Max-Age 为session,即cookie为一次会话的时间。
即 只要不关闭当前浏览器都是当前会话。若打开其他浏览器或关闭此浏览器则cookie失效。
当我们每次进行其他任何请求时,都会携带此cookie。如下请求头:
2、获取cookie
servlet获取cookie
protected void get(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //cookie可能有多个 Cookie[] cookies = request.getCookies(); PrintWriter writer = response.getWriter(); for (Cookie cookie : cookies) { writer.write("cookie键:"+cookie.getName()+"-值:"+cookie.getValue()+"<br/>"); } writer.close(); } }
3、删除cookie
cookie默认的生命周期为session,即当前会话(浏览器一直不关则一直有效)
修改cookie的默认存活时间
- cooke.setMaxAge(0) 参数为秒
- 为负数时,浏览器根本不会存储此cookie。 注意:不一定有效,根据不同浏览器的规则来实现
- 为0时,删除此cookie
- > 0 时, 例如:setMaxAge(3600) ,则过期时间为3600秒后
- cooke.setMaxAge(0) 参数为秒
servlet实现删除
protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie[] cookies = request.getCookies(); PrintWriter writer = response.getWriter(); Cookie cookieR=null; for (Cookie cookie : cookies) { if (cookie.getName().equals("username")) { //设置过期时间为0,则删除 cookie.setMaxAge(0); cookieR=cookie; } } //响应此修改后的cookie给浏览器 response.addCookie(cookieR); writer.write("已删除cookie..."); writer.close(); }
我们发现响应头中的Set-Cookie的Expires已经设置为我们最大生成时间0秒
并且发现浏览器中存储的Cookie也已经删除。
4、设置cookie有效时间
servlet实现
protected void livetime(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie[] cookies = request.getCookies(); PrintWriter writer = response.getWriter(); Cookie cookieR=null; for (Cookie cookie : cookies) { if (cookie.getName().equals("username")) { //设置请求的最大生命时间为3600s cookie.setMaxAge(3600); cookieR=cookie; } } //响应此修改后的cookie给浏览器 response.addCookie(cookieR); writer.write("已持久化cookie 3600秒..."); writer.close(); }
我们发现在请求头中的Set-Cookie过期时间Expiress已经向后延期了3600秒
5、设置cookie请求路径
默认我们的每一次请求都会携带cookie。但是我们的请求往往不是每一次都需要携带cookie,所以我们可以设置cookie的请求路径
servlet设置cookie请求路径
protected void cookiePath(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置cookie Cookie cookie=new Cookie("cookieppp","lc"); //设置cookie访问的路径 即只有携带该路径 此cookie才会被存储 // /代表服务器根路径 http://localhost:8080/ //所以我们每次访问必须以 http://localhost:8080/hello/** 开始 cookie.setPath("/hello"); response.addCookie(cookie); response.getWriter().write("cookie路径已被修改"); }
我们设置后浏览器发送给服务器一个cookie,但是我们此时并没有访问cookie路径,所以此时cooke并不会存储。
当我们请求以服务器的路径开始请求时 即 http://localhost:8080/hello 我们的浏览器才会存储此cookie,并且携带此cookie进行请求。
但我们的服务器请求其他非cookie路径时,请求不会携带cookie,浏览器也不会存储其cookie
5、修改cookie
当我们要修改cookie的value值或者cookie的生存周期,我们可以通过遍历的方式通过cookie名称拿到此cookie,这样做可以,但是很麻烦。我们可以直接通过新建一个cookie来覆盖掉之前的cookie即可。
- 默认cookie的value值不能为中文,若为中文,则需要将中文编码。读取时将值进行解码
- 编码
String value = URLEncoder.encode("张三", "utf-8");
%E5%BC%A0%E4%B8%89
- 解码
String decode = URLDecoder.decode("%E5%BC%A0%E4%B8%89", "utf-8");
张三
- 编码
protected void updateCookie(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//默认cookie的value值不能为中文,若为中文,则需要将中文编码。读取时将值进行解码
String value = URLEncoder.encode("张三", "utf-8");
Cookie cookie = new Cookie("username", value);
cookie.setMaxAge(300);
response.addCookie(cookie);
response.getWriter().write("cookie值已被修改");
}
八、Session
服务器保存的技术,域对象(在整个会话期间保存的数据在任意资源都可以取到)
作用:域对象共享数据。
session就是当前会话对象,它可以共享当前会话的数据。服务器会为每个会话(重新开启浏览器,或者打开不同的浏览器)创建独立的session。只会为每个会话创建一个session,第一次获取的session是新创建的,以后获取的都是之前创建的。
当我们的请求第一次使用session时,若session没有被创建,那么服务器会创建一个session,并生成一个sessionId发送个浏览器,浏览器在cookie中保存此sessionid。每次请求时并携带此sessionId.
当我们的服务器没有session时,第一次请求服务器会为我们的浏览器再响应头中传入一个JSESSIONID保存在浏览器的cookie中.发现session的默认生命周期即为一次会话
1、创建session对象并获取session id
在servlet中实现
protected void get(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //从请求中获取session HttpSession session = request.getSession(); //判断该session是否为新创建的 boolean sessionNew = session.isNew(); //获取session id String id = session.getId(); response.getWriter().write("已经获取到session对象...是否为新创建的:"+sessionNew); response.getWriter().write("<br/>session的id为:"+id); }
2、设置sessiond的属性并获取session的属性值
servlet中设置
protected void set(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取session HttpSession session = req.getSession(); //给session设置属性 session.setAttribute("user", "louchen"); resp.getWriter().write("已经向session设置属性user..."); }
servlet 获取
protected void getValue(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); //获取sessions属性值 String user = (String) session.getAttribute("user"); response.getWriter().write("session中属性user的值为:"+user); }
3、获取session的有效时间
servlet中获取
protected void getTime(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); //以秒为单位 默认有效是时间为30分钟 int maxInactiveInterval = session.getMaxInactiveInterval(); response.getWriter().write("session的有效时间为:"+maxInactiveInterval); }
为啥是30分钟,我们的浏览器再次关闭时,再打开时为啥还是失效?。
因为我们的sessionid是保存在cookie中,当我们关闭浏览器时,cookie即失效。再一次请求时cookie为空,所以session失效
我们可以在tomcat的配置中
\apache-tomcat-7.0.40\conf\web.xml
查看session的有效时间 .单位为分钟<session-config> <session-timeout>30</session-timeout> </session-config>
4、设置session有效时间
1)在tomcat中的web.xml中配置。 或者在项目下的web.xml中配置 单位为分钟
<session-config>
<session-timeout>30</session-timeout>
</session-config>
2)延期失效 单位为秒
servlet设置
protected void updateTime(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
int i=3;
//单位秒 距离最后一次使用session开始计时
//为负数时:永不过期
//为正数时:过期时间单位为秒
session.setMaxInactiveInterval(i);
response.getWriter().write("已设置session的有效期,时间为:"+i);
}
3)立即失效
servlet设置
protected void invalid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
//session立即失效。不会延期
session.invalidate();
response.getWriter().write("session已失效...");
}
5、设置session持久化
将sessionid存在cookie中并设置过期时间。即使关闭浏览器也不会失效。
protected void persistence(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
String id = session.getId();
//把session手动存在cookie中
Cookie cookie = new Cookie("JSESSIONID", id);
//设置过期时间 24小时
cookie.setMaxAge(60 * 60 * 24);
response.addCookie(cookie);
response.getWriter().write("session已持久化");
}
九、禁用cookie重写url
我们每次请求浏览器时,当服务器没有创建session,服务器会创建session对象并向我们的浏览器发送一份sessionid存储在cookie中,若我们禁用浏览器的cookie,那么浏览器并不会存储此sessionid,导致我们在请求服务器时服务器都会创建一份新的sessionid。,即从服务器无法获取session中设置的属性值
解决方法: 在请求指定session对象中的值时在地址栏中传入sessionid即可
bancookie.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
//设置session属性
session.setAttribute("sessionAttr", "sessionValue");
%>
<a href="sessiontest.jsp">去其他页面</a>
</body>
</html>
sessiontest.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<!--获取session属性值-->
session中的值:${sessionAttr}
</body>
</html>
请求地址:·http://localhost:8080/sessiontest.jsp;jsessionid=9C592B93BFF112050536DDD98BFD6D7B
jsessionid=9C592B93BFF112050536DDD98BFD6D7B 该sessionid为在页面bancookie.jsp
页面上获取的sessionid
- 使用response中的
encodeRedirectURL()
方法优化其请求路径。自动判断,如果禁用cookie则帮我们携带上session否则不会携带<a href="<%=response.encodeRedirectURL("sessiontest.jsp")%>">去其他页面</a>
十、session的钝化和活化
现象:服务器关闭重新启动,只要浏览器没关,还是能获取到session的内容
钝化:服务器关闭后,会将session(序列化)保存到硬盘中,可以在项目中查看生成SESSION.ser的文件
活化:服务器再次启动时,把之前序列化好的文件加载进来。就会再次加载之前保存的SESSION.ser文件包含了session域中的内容
session域中的对象要能同session一起钝化到磁盘中必须实现serializable接口
十一、Filter 过滤器
Filter是用来执行过滤任务的一个接口对象,作用于:
请求一个资源(动态资源Servlet,JSP,静态资源)
来自一个资源的响应
两个都可以。既可以过滤请求,也可以过滤响应
Filter过滤请求和响应:
- filter可以拦截请求(request),可以修改请求头,请求内容等
- filter可以拦截来自服务端的响应(response),可以修改响应头和响应内容
- 放行请求
1、过滤器执行流程
实现Filter接口:
只有请求地址为 http://localhost:8080/filtertest.jsp
的 请求才能被该过滤器所过滤
请求参数携带 key
的才能被放行执行
//实现Filter接口 public class RoomFilter implements Filter { // 销毁的方法 @Override public void destroy() { } // 执行过滤的具体方法 @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("进入过滤器..."); String key = req.getParameter("key"); if (key == null || key.equals("")) { //说明没拿到key 不放行 拦截所有页面内容。只会输出响应的值 resp.getWriter().write("stop in...."); }else{ System.out.println("放行之前..."); //放行该请求 chain.doFilter(req, resp); //继续执行后面代码 System.out.println("放行之后..."); //我是经过过滤器的响应 响应对象做修改操作 resp.getWriter().write("我是给页面的响应..."); } } // 初始化方法 @Override public void init(FilterConfig config) throws ServletException { } }
配置Filer请求路径
和servlet配置类似
<url-pattern>/*</url-pattern>
过滤所有请求
<!-- 配置filter路径-->
<filter>
<!-- 配置filer名称-->
<filter-name>RoomFilter</filter-name>
<!-- filer具体类路径-->
<filter-class>org.lc.controller.RoomFilter</filter-class>
</filter>
<!-- 配置该filter过滤的路径-->
<filter-mapping>
<!-- 指定哪个filter-->
<filter-name>RoomFilter</filter-name>
<!-- 过滤的路径 只要是以 '项目路径/filtertest.jsp' 的请求即被过滤 -->
<url-pattern>/filtertest.jsp</url-pattern>
</filter-mapping>
2、过滤器生命周期
//实现Filter接口
public class RoomFilter implements Filter {
// 销毁的方法
@Override
public void destroy() {
}
// 执行过滤的具体方法
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 初始化方法
@Override
public void init(FilterConfig config) throws ServletException {
}
}
1)初始化
- 服务器启动的时候即初始化。创建filter对象调用
init()
方法 。该过滤器为单例多线程,即只创建一个单例对象
2)每次拦截请求都执行 doFilter()
方法
3)服务器停止执行destroy()
方法销毁过滤器
3、过滤器拦截路径规则
1)精确匹配
写要拦截的资源的详细资源路径
只拦截某一个具体的指定资源。
/
代表 项目路径(若配置项目名):http://localhost:8080/项目名/
<url-pattern>/filtertest.jsp</url-pattern>
只拦截/filtertest.jsp
的请求路径<url-pattern>/page/filtertest.jsp</url-pattern>
只拦截 项目page文件夹下的/page/filtertest.jsp
2)路径匹配
- 要拦截的请求路径,或者资源路径
<url-pattern>/page/*</url-pattern>
拦截page开头的所有请求,或者拦截page下的所有资源<url-pattern>/*</url-pattern>
拦截所有请求和所有资源
3)后缀匹
*.后缀名
<url-pattern>*.jsp</url-pattern>
拦截所有请求以 .jsp 结尾的资源
若要实现拦截 page文件下的所有以 *.jsp结尾的资源
<filter-mapping> <filter-name>PatternFilter</filter-name> <!--先拦截page下的所有请求--> <url-pattern>/page/*</url-pattern> </filter-mapping>
通过请求拿到对应的请求路径,然后判断请求是否以指定规则结尾的,是则拦截,否则放行
HttpServletRequest request= (HttpServletRequest) req;
将ServletRequest
请求强制转为HttpServletRequest
请求request.getRequestURI();
获取请求的URI 不包含项目路径request.getRequestURL();
获取请求的URL 包含项目路径@Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("执行doFilter..."); HttpServletRequest request= (HttpServletRequest) req; // /page/index.jsp String requestURI = request.getRequestURI(); // http://localhost:8080/page/success.jsp StringBuffer requestURL = request.getRequestURL(); System.out.println(requestURI); System.out.println(requestURL); if (requestURL.toString().endsWith(".jsp")) { //拦截 以 .jsp结尾的 }else{ //放行其他的 chain.doFilter(req, resp); } }
错误写法:
<url-pattern>/page/*.jsp</url-pattern>
×××
4)一次过滤多个拦截规则
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<!--拦截/shop2开头的所有请求-->
<url-pattern>/shop2/*</url-pattern>
<!--拦截/page开头的所有请求-->
<url-pattern>/page/*</url-pattern>
</filter-mapping>
5)拦截指定servlet的所有请求
<filter-mapping>
<filter-name>PatternFilter</filter-name>
<!--servlet的名称-->
<servlet-name>UserServlet</servlet-name>
</filter-mapping>
4、filter响应中文乱码问题
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//我们在执行放行方法之前若写入响应数据,则会造成乱码。因为resp中的响应头字符编码的头为空,所以需要手动设置字符编码
resp.setContentType("text/html;charest=utf-8");
resp.getWriter().write("执行过滤器doFilter之前");
chain.doFilter(req, resp);
//而执行doFilter方法后,响应头的中的字符编码jsp已经为我们设置,所以后续执行不乱码
resp.getWriter().write("我是给页面的响应...");
}
上述response封装完后,从上到下依次渲染数据,在doFilter
之前的渲染数据若没有配置字符编码则会乱码,在doFilter
之后的数据不会乱码,因为在doFilter
中已经对字符完成编码
ServletConfig
,ServletContext
,FilterConfig
初始化参数
5、各种ServletConfig
1)在该servlet创建的时候调用init()
方法
- 一个servlet对应一个
ServletConfig
存放该servlet的配置信息
web.xml配置
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>org.lc.controller.UserServlet</servlet-class>
<!--配置初始化参数-->
<init-param>
<!--参数名-->
<param-name>user</param-name>
<!--参数值-->
<param-value>lc</param-value>
</init-param>
</servlet>
@Override
public void init(ServletConfig config) throws ServletException {
//获取初始化参数
String user = config.getInitParameter("user");
//lc
System.out.println(user);
}
ServletContext
和FilterConfig
2)服务器启动时即初始filter调用init()
方法
ServletContext
对应整个web应用
通过FilterConfig
或者ServletConfig
获得 ServletContext
- 一个filter对应一个
FilterConfig
,存放该filer的配置信息
<!-- application初始化参数-->
<context-param>
<param-name>user</param-name>
<param-value>applicationValue</param-value>
</context-param>
<filter>
<!-- 配置filer名称-->
<filter-name>RoomFilter</filter-name>
<!-- filer具体类路径-->
<filter-class>org.lc.controller.RoomFilter</filter-class>
<init-param>
<!--filter初始化参数-->
<param-name>user</param-name>
<param-value>filterValue</param-value>
</init-param>
</filter>
// 初始化方法
@Override
public void init(FilterConfig config) throws ServletException {
//获取当前application参数 (代表整个web服务器)
ServletContext servletContext = config.getServletContext();
//获取application初始化参数
String user = servletContext.getInitParameter("user");
//applicationValue
System.out.println(user);
//获取过滤器初始化参数
String user1 = config.getInitParameter("user");
//filterValue
System.out.println(user1);
String filterName = config.getFilterName();
//该filter的别名
System.out.println(filterName);
}
6、过滤器链
若多个过滤器过滤相同的请求,则会从web.xml中从上到下依次加载过滤器,优先执行先加载的过滤器
过滤器AFilter和BFilter依次过滤 a.jsp的页面请求
<filter-name>AFilter</filter-name>
<filter-class>org.lc.controller.AFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AFilter</filter-name>
<url-pattern>/a.jsp</url-pattern>
</filter-mapping>
<filter>
<filter-name>BFilter</filter-name>
<filter-class>org.lc.controller.BFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>BFilter</filter-name>
<url-pattern>/a.jsp</url-pattern>
</filter-mapping>
AFilter
@Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("顺序 1"); //执行doFilter后,若还有相同请求的过滤器未执行则继续执行其他过滤器 chain.doFilter(req, resp); System.out.println("顺序 2"); }
BFilter
@Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("顺序 3"); chain.doFilter(req, resp); System.out.println("顺序 4"); }
a.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <% System.out.println("顺序 a.jsp"); %> <h1>我是a.jsp页面</h1> </body> </html>
依次打印:
顺序 1
顺序 3
顺序 a.jsp
顺序 4
顺序 2
由上可以过滤器相当于在doFilter中执行递归调用。
7、dispatcher
告诉服务器都拦截哪些方式到达的资源
FORWARD : 拦截转发过来的,转发到这个地址,会被拦截
INCLUDE:拦截包含的,包含这个地址的会被拦截,拦截动态包含
REQUEST(默认的):直接请求的,直接请求这个地址会被拦截,post,get请求
ERROR:发送错误,去全局配置一个错误页面会被拦截。不是errorPage指向的页面。指的是全年局的配置
<filter-mapping>
<filter-name>BFilter</filter-name>
<url-pattern>/a.jsp</url-pattern>
<!--FORWARD,INCLUDE, REQUEST, ERROR-->
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
十二、JDBC使用步骤
1、加载驱动
Class.forName("com.mysql.jdbc.Driver");
2、获得连接
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456")
3、创建运行对象
4、运行sql
5、处理运行结果
6、释放资源
十三、AJAX
1、简介
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。
2、js发送ajax请求
- onreadystatechange 事件 获取响应过程状态信息
当请求被发送到服务器时,我们需要执行一些基于响应的任务。
每当 readyState 改变时,就会触发 onreadystatechange 事件。
readyState 属性存有 XMLHttpRequest 的状态信息。
下面是 XMLHttpRequest 对象的三个重要的属性:
属性 | 描述 |
---|---|
onreadystatechange | 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。 |
readyState | 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。 0: 请求未初始化 1: 服务器连接已建立 2: 请求已接收 3: 请求处理中 4: 请求已完成,且响应已就绪 |
status | 200: "OK" 404: 未找到页面 |
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--通过ajax请求--%>
<script>
function btnAjax() {
//1、创建xhr对象,用这个对象来向服务器发送请求
let xhr = new XMLHttpRequest();
//2、定义请求发送格式
//请求类型; 请求地址; 是否异步(默认true)
//请求地址可以携带参数
xhr.open("GET", "ajax?username=zs&pw=123456", true);
//3、发送请求
//若请求有请求体,则将请求体的数据发送(仅用于 POST 请求)
// xhr.send(body);
xhr.send();
//4、获取响应的数据
//xhr中的属性
//responseText获得字符串形式的响应数据。
//responseXML 获得XML形式的响应数据。
//当请求的处理状态为4 并且 status状态为200时 代表响应处理完成。
xhr.onreadystatechange = function () {
if (!(xhr.readyState == 4 && xhr.status == 200)) {
let v = document.getElementById("text");
v.innerText = xhr.responseText;
}
}
}
</script>
<button onclick="btnAjax()">发送请求</button>
<div id="text"></div>
</body>
</html>
3、解决ajax请求缓存问题 (验证码)
生成一个随机值携带参数一同发送给服务器
$.get('ajax?username=lc&pw=123'+Math.random(),function (data) { },"text");
4、jquery发送$.post()/$.get()请求
$.get(URL,data,function(data,status,xhr),dataType)
- 发送get请求,在url中携带的参数和在data中传入的参数 都会被放到url地址栏中传输
$.post(URL,data,function(data,status,xhr),dataType)
- 发送post请求,在utl中携带的参数还是在url地址上,在data中的参数会被放在请求体中。
参数 | 描述 |
---|---|
URL | 必需。规定您需要请求的 URL。 |
data | 可选。规定连同请求发送到服务器的数据。带key-value的参数 |
function(data,status,xhr) | 可选。规定当请求成功时运行的函数。 额外的参数: data - 包含来自请求的结果数据 status(可选) - 包含请求的状态("success"、"notmodified"、"error"、"timeout"、"parsererror") xhr(可选) - 包含 XMLHttpRequest 对象 |
dataType | 可选。规定预期的服务器响应的数据类型。 "xml" - 一个 XML 文档 html" - HTML 作为纯文本 "text" - 纯文本字符串 "script" - 以 JavaScript 运行响应,并以纯文本返回 "json" - 以 JSON 运行响应,并以 JavaScript 对象返回 "jsonp" - 使用 JSONP 加载一个 JSON 块,将添加一个 "?callback=?" 到 URL 来规定回调 |
1)发送get请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<script src="/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script>
function jqueryAjax() {
$.get('ajax?user=zs&pw=123&r='+Math.random(),{"id":1111,username:"lc",password:123456},function (data) {
let parse = JSON.parse(data);
$("#text").text(parse.username+parse.id);
},'text');
}
</script>
<button onclick="jqueryAjax()">jquery发送get请求</button>
<div id="text"></div>
</body>
</html>
servlet为例
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("收到请求了"); request.setCharacterEncoding("utf-8"); String user1 = request.getParameter("user"); String id = request.getParameter("id"); String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println("url中的参数:"+user1); System.out.println("data中的参数:"+id); System.out.println("data中的参数:"+username); System.out.println("data中的参数:"+password); response.setContentType("text/html;charset=utf-8"); User user = new User(1001, "张三", "123456"); Gson gson=new Gson(); //将javabean转为字符串 方便在请求中传输 String s = gson.toJson(user); response.getWriter().write(s); }
我们可以发现get请求在url中的参数和在data中的参数都被放在在url地址栏中,后台也能获取到全部数据
2)发送post请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<script src="/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script>
function jqueryPost() {
$.post('ajax?user=zs&pw=123&r='+Math.random(),{"id":1111,username:"lc",password:123456},function (data) {
let parse = JSON.parse(data);
$("#text").text(parse.username+parse.id);
},'text');
}
</script>
<button onclick="jqueryPost()">jquery发送post请求</button>
<div id="text"></div>
</body>
</html>
后端就是 上面的get请求的后端
我们可以发现发送post请求,地址栏中的参数还是在地址栏中,但是data中的数据会方法在请求体中,不会方在url中,这意味着我们的post请求可以发送大量数据。
5、$.ajax()更灵活的其他方式
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<script src="/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script>
function jqueryOther() {
$.ajax({
//1、请求地址
url:'ajax?user=zs&pw=123&r='+Math.random(),
//2、请求类型
//请求类型(默认为get) post,get,delete,put
//注意get没有请求体只能在url中传输参数,其他类型有请求体,参数可以在请求体中
type:'get',
//3、请求参数。
//注意请求参数必须为js对象形式,不能为字符串
//例如:错误写法"{"id":"19321","username":"louc","password":"123456"}" 这样服务端会获取不到值
//允许的形式 data:'id=19321&username=louc&password=123456' 可以为参数形式
data:{"id":"19321","username":"louc","password":"123456"},
// data:'id=19321&username=louc&password=123456',
//4、是否异步 默认为异步
async:true,
//5、成功的回调
success: function (data) {
//一般data类型为字符串
let p = JSON.parse(data);
$("#text").text(p.username+""+p.id)
},
//6、失败的回调 404 403等
error(data) {
//打印错误信息
alert("错误"+data)
}
})
}
</script>
<button onclick="jqueryOther()">jquery发送其他格式请求</button>
<div id="text"></div>
</body>
</html>
6、表单序列化
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<script src="/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script>
function subForm() {
//传统获取表单属性值
// let id=$("input[name='id']").val();
// let username=$("input[name='username']").val();
// let password=$("input[name='password']").val();
// alert(id+username+password)
//序列化表单后,得到的字符串为 'id=19321&username=louc&password=123456' 形式
let formdata = $("#form1").serialize();
alert(formdata)+
$.ajax({
url:"ajax?user=zs&pw=123&r='+Math.random()",
data:formdata,
async:true,
type:'post',
success: function (data) {
//一般data类型为字符串
let p = JSON.parse(data);
$("#text").text(p.username+p.id)
},
error(data) {
alert(data)
}
})
}
</script>
<form action="#" id="form1">
id <input type="text" name="id"><br>
用户名 <input type="text" name="username"><br>
密码<input type="text" name="password"><br>
<button onclick="subForm()">提交</button>
</form>
<div id="text"></div>
</body>
</html>
十四、JSON
JSON就是表示对象的方法,格式就是按照js声明对象的格式来. json的编写必须要符合js规范。
- 在js中:属性加和不加双引号都可以。值为必须为js符合的类型
- 在java中:json字符串必须符合json规范,所有属性必须加双引号。
转换为:JSON格式要求
- key(属性) 必须有双引号
- value : 基本类型或者引用类型(对象)
//js对象写法 (也可以直接转为字符串,自动为属性加上引号 )
// let json={name:"张三", age: 22};
//json写法 属性必须加字符串
let json={"name":"张三", "age": 22};
//将json对象转为字符串
let str = JSON.stringify(json);
//{"name":"张三", "age": 22}
alert(str)
//将字符串转为json对象/js对象
let jsonstr = JSON.parse(str);
//获取js对象属性值
//张三
alert(jsonstr.name)
1、Gson的使用
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
1)javabean和字符串的转换
Gson gson=new Gson();
String str="{id:1002,username:'zs','password':123456}";
//字符串转javabean
User user = gson.fromJson(str, User.class);
//User(id=1002, username=zs, password=123456)
System.out.println(user);
//javabean转字符串
String s = gson.toJson(user);
//{"id":1002,"username":"zs","password":"123456"}
System.out.println(s);
2)List集合和字符串的转换
Gson gson=new Gson();
List<User> list=new ArrayList<>();
list.add(new User(1001,"zs","123456"));
list.add(new User(1001,"ls","1234"));
list.add(new User(1001,"ww","12356"));
//集合转字符串
String s = gson.toJson(list);
//[{"id":1001,"username":"zs","password":"123456"},{"id":1001,"username":"ls","password":"1234"},{"id":1001,"username":"ww","password":"12356"}]
System.out.println(s);
//字符串转集合
List<User> list1 = gson.fromJson(s, new TypeToken<List<User>>() {
}.getType());
//[User(id=1001, username=zs, password=123456), User(id=1001, username=ls, password=1234), User(id=1001, username=ww, password=12356)]
System.out.println(list1);
3)Map集合和字符串的转换
Gson gson=new Gson();
HashMap<String,User> hashMap=new HashMap<>();
User user1 = new User(1001, "zs", "123456");
User user2 = new User(1001, "zs", "123456");
hashMap.put("level1", user1);
hashMap.put("level2", user2);
//map集合转为字符串
String s = gson.toJson(hashMap);
//{"level1":{"id":1001,"username":"zs","password":"123456"},"level2":{"id":1001,"username":"zs","password":"123456"}}
System.out.println(s);
//字符串转为集合
HashMap<String,User> o = gson.fromJson(s, new TypeToken<HashMap<String, User>>() {
}.getType());
//{level1=User(id=1001, username=zs, password=123456), level2=User(id=1001, username=zs, password=123456)}
System.out.println(o);
十五、监听器
监听器,字面上的理解就是监听观察某个事件(程序)的发生情况,当被监听的事件真的发生了的时候,事件发生者(事件源) 就会给注册该事件的监听者(监听器)发送消息,告诉监听者某些信息,同时监听者也可以获得一份事件对象,根据这个对象可以获得相关属性和执行相关操作。
1、声明周期监听器。监听三个对象的生命周期(创建到消耗)
1)监听请求 ServletRequestListener
来一个请求就会执行一次初始化和销毁方法
public class RequestLifeListener implements ServletRequestListener {
/**
* 请求来的时候 调用requestInitialized初始化方法
* @param sre
*/
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("请求创建...");
//获取application对象 代表整个web应用
ServletContext servletContext = sre.getServletContext();
//获取当前请求对象
ServletRequest servletRequest = sre.getServletRequest();
}
/**
* 请求结束,完成响应 调用requestDestroyed销毁方法
* @param sre
*/
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("请求销毁");
}
}
web.xml
配置该监听器:
<listener>
<listener-class>org.lc.listener.RequestLifeListener</listener-class>
</listener>
2)监听整个web应用 ServletContextListener
项目启动时调用一次初始化方法 。项目停止时调用销毁方法
public class ApplicationLifeListener implements ServletContextListener {
/**
* 项目启动时调用此初始化方法
* @param servletContextEvent
*/
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ApplicationLifeListener 初始化");
//获取当前web对象
ServletContext servletContext = servletContextEvent.getServletContext();
}
/**
* 项目停止时调用此销毁方法
* @param servletContextEvent
*/
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ApplicationLifeListener销毁方法...");
}
}
<listener>
<listener-class>org.lc.listener.ApplicationLifeListener</listener-class>
</listener>
3)监听会话 HttpSessionListener
第一次使用session才会创建,session 失效(强制实现,超时)则调用相应的销毁方法
public class SessionLifeListener implements HttpSessionListener {
/**
*第一次使用session的时候 初始化sessionCreated
* @param httpSessionEvent
*/
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("SessionLifeListener创建...");
//获取当前session对象
HttpSession session = httpSessionEvent.getSession();
}
/**
* session失效的时候调用此 sessionDestroyed方法
* @param httpSessionEvent
*/
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("SessionLifeListener销毁...");
}
}
2、属性监听器。监听三个对象中的属性
ServletRequestAttributeListener
1)监听请求属性 当请求中的属性增加时调用attributeAdded方法
当请求中的属性修改时调用attributeReplaced方法
当请求中的属性移除时调用attributeRemoved方法
public class RequestAttrListener implements ServletRequestAttributeListener {
/**
* 属性增加
* @param servletRequestAttributeEvent
*/
@Override
public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
System.out.println("添加属性...");
//获取增加的属性key
String name = servletRequestAttributeEvent.getName();
//获取增加的属性值
Object value = servletRequestAttributeEvent.getValue();
System.out.println("增加的属性 key:"+name+" value:"+value);
}
/**
* 属性移除
* @param servletRequestAttributeEvent
*/
@Override
public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
System.out.println("删除属性...");
//获取删除的属性key
String name = servletRequestAttributeEvent.getName();
//获取删除的属性值
Object value = servletRequestAttributeEvent.getValue();
System.out.println("删除的属性 key:"+name+" value:"+value);
}
/**
* 属性修改
* @param servletRequestAttributeEvent
*/
@Override
public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
System.out.println("修改属性...");
//获取修改之前的属性key
String name = servletRequestAttributeEvent.getName();
//获取修改之前的属性值
Object value = servletRequestAttributeEvent.getValue();
//获取请求对象
ServletRequest servletRequest = servletRequestAttributeEvent.getServletRequest();
//获取修改之后的属性值
Object parameter =servletRequest.getAttribute(name);
System.out.println("修改之前的属性 key:"+name+" value:"+value);
System.out.println("修改之后的属性 key:"+name+" value:"+parameter);
}
}
<listener>
<listener-class>org.lc.listener.RequestAttrListener</listener-class>
</listener>
HttpSessionAttributeListener
2)监听session属性 和监听请求的用法一致。
public class SessionAttrListener implements HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
}
@Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
}
@Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
}
}
ServletContextAttributeListener
3)监听web应用属性 和监听请求的用法一致。
public class ServletContextAttrListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
}
@Override
public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
}
@Override
public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
}
}
3、sessions固有监听器
HttpSessionActivationListener
1) 监听sessions活化钝化 public class SessionActivationListener implements HttpSessionActivationListener {
@Override
public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
}
@Override
public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
}
}
HttpSessionBindingListener
2)监听一个对象是否绑定到session中(保存在session中) public class SessionBindingListener implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
}
@Override
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
}
}
十六、文件上传与下载
文件上传的必要条件:
指定input的类型
type
为file
<input type="file" name="img">
指定表单编码类型
enctype
为multipart/form-data
多部分上传 默认为编码类型为application/x-www-form-urlencoded
表单提交请求类型必须为
POST
<form action="/upload" method="post" enctype="multipart/form-data">
用户名 <input type="text" name="username"/><br>
头像:<input type="file" name="img">
<input type="submit" value="上传">
</form>
我们发现请求类型为多部分表单数据类型。并且各部分之间的分隔符对应的为 boundary后的内容。每个input项代表一个部分,部分直接由浏览生成一个分隔符分割。
后端将请求流转换为字符串形式的数据。 我们可以发现请求中所有的数据被包含在流中,无法通过getParamter()获取参数
------WebKitFormBoundaryveRHUB7FOGYYBQ3y //分隔符
Content-Disposition: form-data; name="username" //每个部分的类型和详细信息
//空行后对应的为 每个部分的值
admin
------WebKitFormBoundaryveRHUB7FOGYYBQ3y
Content-Disposition: form-data; name="img"; filename="1.jpg"
Content-Type: image/jpeg
//文件流的数据
??? JFIF ?? C #,%!*!&4'*./121%6:60:,010?? C
1、流和文件操作工具类
commons-io
处理IO
commons-fileupload
处理文件
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2、文件上传
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
用户名 <input type="text" name="username"/><br>
头像:<input type="file" name="img">
<input type="submit" value="上传">
</form>
</body>
</html>
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//不能通过 request.getParameter("username")获取参数,因为请求已经被作为流传输分成一段一段的数据部分(表单的所有项,部件)
//获取请求流对象
//ServletInputStream inputStream = request.getInputStream();
//1、创建工厂实例
DiskFileItemFactory factory = new DiskFileItemFactory();
//2、创建处理文件上传的servlet对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//3、解析文件上传请求
try {
//4、使用该servlet将请求解析为 多个数据部件对象。即每一项代表一个数据项。
List<FileItem> fileItems = fileUpload.parseRequest(request);
//遍历该数据部件
fileItems.forEach((fileItem)->{
//如果该项为 input标签中普通的 kye-value参数形式的部件
if (fileItem.isFormField()) {
//获取表单项的name值
String username = fileItem.getFieldName();
try {
//获取表单项的value值 并指定编码
String value = fileItem.getString("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//获取表单项的文件名称 (为null) 普通input只有key-value
String filename = fileItem.getName();
}else{
//否则为 文件对象
//获取input 标签name值
String fieldName = fileItem.getFieldName();
//获取文件名
String filename = fileItem.getName();
System.out.println("文件name值:"+fieldName);
System.out.println("文件名:"+filename);
try {
//得到该文件的 数据流部分
InputStream inputStream = fileItem.getInputStream();
//生成随机文件名
String s = UUID.randomUUID().toString().replaceAll("-", "");
String newfileName=s+"_"+filename;
//获取相对路径
//设置存放指定文件夹的绝对路径:
String realPath = request.getServletContext().getRealPath("/uploads");
//F:\桌面内容\springboot2\Spring+SpringMVC\servlet01\target\servlet01\uploads
System.out.println(realPath);
//创建输出流并指定文件路径
// / 代表tomcat安装的盘符 即 D:
// . 代表tomcat安装目录下的 bin文件夹下
FileOutputStream fileOutputStream = new FileOutputStream(realPath+"/"+newfileName);
//将输入流写到输出流中
IOUtils.copy(inputStream, fileOutputStream);
//关流
fileOutputStream.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
} catch (FileUploadException e) {
e.printStackTrace();
}
response.sendRedirect("/fileupload.jsp");
}
直接在服务器访问地址: http://localhost:8080/uploads/70f974b4c5544bae8ec3c55eea4c891a_1.jpg
3、获取指定文件或指定文件夹的绝对路径
String realPath = request.getServletContext().getRealPath("/uploads");
//F:\桌面内容\springboot2\Spring+SpringMVC\servlet01\target\servlet01\uploads
4、文件下载
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="download">下载图片</a>
</body>
</html>
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//本质就是告诉浏览器我给你资源别直接打开 给我下载
//把要下载的资源传给浏览器
ServletContext servletContext = request.getServletContext();
//获取该图片的绝对路径
String realPath = servletContext.getRealPath("/uploads/哈哈.jpg");
//动态获取文件类型
String mimeType = servletContext.getMimeType(realPath);
//image/jpeg
System.out.println("文件类型"+mimeType);
//设置响应头类型(这里我们是图片类型)
response.setContentType(mimeType);
// 若文件名为中文 则会无法识别 (万能解决方案)
String s = new String("哈哈".getBytes("gbk"), "iso8859-1");
//设置浏览器处理的方式 即以附件形式打开
// Content-Disposition 处置方式
// attachment 附件
// filename=文件名
response.setHeader("Content-Disposition","attachment;filename="+s);
//设置文件大小(可选) 一般不设置
// response.setContentLength();
//得到输入流
FileInputStream fileInputStream = new FileInputStream(realPath);
ServletOutputStream outputStream = response.getOutputStream();
//将输入流拷贝到响应输出流
IOUtils.copy(fileInputStream, outputStream);
//关流
outputStream.close();
fileInputStream.close();
}
点击下载,发现此时并没有打开,而是直接以附件形式打开
我们查看响应头
十七、javaweb项目部署tomcat
1、一般我们通过maven打成war包部署
2、生成的war包在指定的项目的target目录下
3、将项目的war包 servlet01.war直接复制到tomcat安装目录下的webapp目录下
4、进入tomcat安装目录下的bin目录下执行startup.bat 批处理指令
5、执行后会把webapp下的servlet01.war的war包自动解压生成项目文件夹
6、控制台启动成功,输入浏览器访问
注意:这里访问必须携带项目文件夹名称,所有在项目中有路径访问时,必须要动态获取项目文件,否则会找不到。
http://localhost:8080/servlet01/downloadimg.jsp
十八、创建自定义服务
我们每次手动执行启动startup.bat文件时不免麻烦。我们可以为此该批处理命令创建一个服务
1、以管理员身份打开cmd窗口
2、进入tomcat下的bin目录
3、使用service bat install 服务名
创建服务(Tomcat7.0)
4、启动服务
这里我们需要进入到任务管理器中才能查看到服务
5、删除服务
sc delete 服务名