Servlet应用——JavaWeb教案(四)
文章目录
- JavaWeb组件
- 1. ServletRequest
- 1. 介绍
- Request体系
- **获取请求消息数据**
- **获取请求行**
- 获取请求头
- 获取超链接的所有头信息
- 防盗链操作
- 获取请求体
- 其他功能
- 1. 获取请求参数通用方式
- 2. 请求转发:
- 1. 转发给servlet
- 2. 转发给jsp页面
- 3. 共享数据
- 1. 转发给servlet
- 2. 转发给JSP
- 中文乱码问题
- 2. 如何同时运行两个项目,部署两个服务器
- 1. 创建一个空的项目工程文件
- 2. 创建项目名称
- 创建两个子模块 pro_01和pro_02
- 分别添加框架支持
- 分别添加Tomcat服务
- 分别启动Tomcat服务
- 3. 案例(见下案例)
- 2. ServletResponse
- 1. 介绍
- Response体系
- 设置响应消息
- 2. 案例
- 1. 重定向
- **a. 资源跳转的方式**
- **b. 两种方式实现,推荐选择第二种。**
- **c. 重定向和转发的区别**
- 区别
- 什么时候用转发,什么时候用重定向
- d. 路径写法
- e. 动态路径获取
- 2. 服务器输出字符数据到浏览器
- 3. 服务器输出字节数据
- 4. 验证码响应(见下案例)
- 3. ServletContext
- 1. 介绍
- 2. **获取方式**
- 3. 功能
- 1. 获取MIME类型
- 2. 域对象:共享数据
- 3. 获取文件的真实路径
- 4. 案例
- 文件下载
- 注意:
- 需求:
- 分析:
- 步骤:
- 代码展示
- 中文文件名称无法显示问题
- 4.Cookie和Session
- 优秀blog
- 会话技术
- 介绍
- Cookie和Session之间的区别和联系
- Cookie
- cookie介绍
- Cookie细节
- 1. 一次请求发送多个Cookie
- 2. Cookie的存活时间
- 3. Cookie是否支持中文数据
- 存储中文数据
- 存储特殊字符如:空格
- 4. cookie的共享问题
- 一个服务器,一个web项目
- 同一个服务器的两个web项目
- 在多个服务器之间,能否实现数据共享
- Cookiie总结
- 案例
- 记录访问时间
- Session
- Session介绍
- Session原理
- Session细节
- 1. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
- 2.客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
- 3. session的失效时间?
- 4. Session和cookie区别
- 5. Filter和Listener
- Filter
- 介绍
- 概念
- **入门:**
- 拦截路径
- Filter细节
- 1. 过滤器执行流程
- 2. 过滤器生命周期方法
- 3. 过滤器配置详解
- 4. 过滤器链
- Listener
- 6. 案例
- 1. 用户登陆(request)
- 案例需求:
- 逻辑图示:
- 报错:
- 核心代码
- 参数传递优化(BeanUtils)
- 扩展1:BeanUtils.工具类
- 扩展2:使用JdbcTemplate工具类
- 2. 验证码响应(response)
- 3. 用户登陆(session)
- 4. 用户登陆(Filter)
- 7. 问题汇总
- 1. WEB-INF资源问题
- 1. 首页在WEB-INF下
- 2. 获取WEB-INF指定jsp文件
- 3. 静态资源在WEB-INF下
- 4. Tomcat项目Jar包问题
- 2. 乱码问题
- Tomcat日志乱码
- URL解码
- 请求响应
- 控制台打印乱码
- 3. 路径问题汇总
- 4. 设置basepath路径
JavaWeb组件
1. ServletRequest
1. 介绍
request和response对象是由服务器创建的,我们来使用他们。
request是来获取请求消息,response设置响应消息。
解析
Request体系
获取请求消息数据
获取请求行
GET /servlet /demo1?name=zhangsan HTTP/1.1
获取请求方式: GET
String getMethod()
获取虚拟目录: /servlet
String getContextPath()
获取Servlet路径: /demo1
String getServletPath( )
获取get方式请求参数: name= zhangsan
String getQueryString()
获取请求URI : /servlet / demo1
string getRequestURI():
获取url : http://localhost/servlet / demo1
- StringBuffer getRequestURL()
- URL:统一资源定位符: http://localhost/day14/demo1 中华人民共和国 url+文件路径
- URI :统一资源标识符: /day14/demo1 共和国 文件路径
获取协议及版本: HTTP/1.1
String getProtocol()
获取客户机的IP地址:
String getRemoteAddr()
@WebServlet("/req/demo1")
public class RequestDemo1 extends HttpServlet {protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("username:"+req.getParameter("username"));System.out.println("password:"+req.getParameter("password"));System.out.println("参数集合");Map<String, String[]> parameterMap = req.getParameterMap();for (String key:parameterMap.keySet()) {String[] paramers = parameterMap.get(key);System.out.println(Arrays.toString(paramers));}System.out.println("请求方法"+req.getMethod());System.out.println("请求虚拟路径"+req.getContextPath());System.out.println("请求接口路径"+req.getServletPath());System.out.println("请求URI"+req.getRequestURI());System.out.println("请求URL"+req.getRequestURL().toString());}protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}}
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title><link rel="stylesheet" href="/servlet/css/http.css">
</head><body>
<a href="/servlet/req/demo1">发送请求</a>
<form action="/servlet/req/demo1" method="get"><h1>发送get请求</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="text"><br><input type="submit" value="提交">
</form>
<br><form action="/servlet/req/demo1" method="post"><h1>发送post请求</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="text"><br><input type="submit" value="提交">
</form></body>
</html>
获取请求头
String getHeader(String name ) :通过请求头的名称获取请求头的值
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1、获取user-agent的头数据//告诉服务器我的浏览器版本String userHeader = request.getHeader("user-agent");System.out.println(userHeader);//判断浏览器版本if (userHeader.contains("Chrome")) {System.out.println("谷歌");} else {System.out.println("火狐");}System.out.println(".....................");//2、获取referer的头数据String refer = request.getHeader("referer");System.out.println(refer); //当直接在浏览器输入时,此时为空 问题:为什么我把链接复制到文档里面打开也是null。。//当我通过页面访问时,就可以获取访问地址 http://localhost:8888/servlet/Login.html }
Enumeration getHeaderNames() ;获取所有的请求头名称
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取所有请求头Enumeration<String> headerNames= request.getHeaderNames();System.out.println("...................");System.out.println(headerNames); // org.apache.tomcat.util.http.NamesEnumerator@275fd6f4while(headerNames.hasMoreElements()){String name = headerNames.nextElement();//获取头的数据String value =request.getHeader(name);System.out.println(value);} }
获取超链接的所有头信息
<a href="/servlet/demo3">访问服务器</a>
防盗链操作
首先我们部署两个项目,一个项目是存放另一个项目的超链接,一个项目是进行消息反馈。
即一个自制电影网站,一个优酷网站。通过自制网站访问优酷内容,和优酷自身访问优酷内容。比较其差别。
项目一(优酷):
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String referer = request.getHeader("referer");System.out.println(referer);if(referer.contains("youku")){System.out.println("看电影没门");response.setContentType("text/html;charset=utf-8");response.getWriter().write("盗我的视频,没有门");}else解{System.out.println("请看电影");response.setContentType("text/html;charset=utf-8");response.getWriter().write("请看电影哈");}
}// 访问链接:<a href="/youku/server">战狼</a>
refer:http://localhost:8800/servlet/
项目二(自建小网站):
访问链接:访问的是项目一
<a href="http://localhost:8081/youku/server">战狼(盗版)</a>
refer:http://localhost:8808
获取请求体
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
步骤:
- 获取流对象
- BufferedReader getReader() 字符输入流,可操作字符串类型的数据
- ServletInputStream getInputstream() 字节输入流,可操作所有类型的数据,图片,文档等df
- 相关内容在文件上传中
- 再从流对象中获取数据
@WebServlet("/req/demo1")
public class ServletDemoTwo extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取请求消息体,即请求参数//1.获取字符流BufferedReader bf =request.getReader();//2.获取数据String line= null;//line=参数,如果为空,这说明没有数据了while ((line=bf.readLine())!=null){System.out.println(line);}}
发送请求:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title><link rel="stylesheet" href="/servlet/css/http.css">
</head><body>
<a href="/servlet/req/demo1">发送请求</a>
<form action="/servlet/req/demo1" method="get"><h1>发送get请求</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="text"><br><input type="submit" value="提交">
</form>
<br><form action="/servlet/req/demo1" method="post"><h1>发送post请求</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="text"><br><input type="submit" value="提交">
</form></body>
</html>
其他功能
1. 获取请求参数通用方式
- String getParameter(String name) :根据参数名称获取参数值 username=zh&password=12
html代码
<form action="/servlet/req/demo1" method="get"><h1>发送get请求</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="text"><br><input type="submit" value="提交">
</form>
<br><form action="/servlet/req/demo1" method="post"><h1>发送post请求</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="text"><br><input type="submit" value="提交">
</form>
servlet代码
@WebServlet("/req/demo1")
public class RequestDemo1 extends HttpServlet {protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("username:"+req.getParameter("username"));System.out.println("password:"+req.getParameter("password"));}protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}
}
运行结果:
String[ ] getParametervalues(String name ) ;根据参数名称获取参数值的数组hobby=xx&hobby=game
Enumeration getParameterNames( ):获取所有让求的参数名称
Map<String, String[]> getParameterMap( ) :获取所有参数的map集合
Servlet代码
@WebServlet("/req/demo2")
public class RequestDemo2 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//一、根据参数名称获取参数值String parameter= request.getParameter("username");System.out.println(parameter); //1427421650//二、根据参数值名称获取参数值数组 针对于复选框String[] hobbies = request.getParameterValues("hobby");for (String hobby:hobbies) {System.out.println(hobby);//篮球,足球,橄榄球}//三、获取所有参数名称Enumeration<String> parameterNames = request.getParameterNames();while (parameterNames.hasMoreElements()){String name= parameterNames.nextElement();System.out.println(name); //username password hobby}//四、获取所有参数名称及其参数值,以键值对的方式保存Map<String, String[]> parameterMap = request.getParameterMap();//遍历方法1for(String key:parameterMap.keySet()) {System.out.println(key);for (int a=0;a<parameterMap.get(key).length;a++){System.out.println(parameterMap.get(key)[a]);}System.out.println(".......................");}//遍历方法2Map<String, String[]> parameterMap1= request.getParameterMap();Set<String> keyset = parameterMap1.keySet();for (String name: keyset) {//获取键和值String [] values = parameterMap.get(name);System.out.println(name);for (String value:values) {System.out.println(value);}System.out.println("...............................");}}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request,response);}
}
JSP代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<form action="/servlet/req/demo2" method="get"><input type="username" name="username" placeholder="用户名"><br><input type="username" name="password" placeholder="密码"><input type="checkbox" name="hobby" value="篮球">篮球<input type="checkbox" name="hobby" value="足球">足球<input type="checkbox" name="hobby" value="橄榄球">橄榄球<br><input type="submit" value="提交">
</form></body>
</html>
2. 请求转发:
我们在写代码时,不可能将代码都写在一个类里面,所以当服务器访问Servlet接口时,通常是一个接口将信息分担给另一个接口,这样便于维护。
步骤:
- 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
- 使用RequestDispatcher对象来进行转发: forward(ServletRequest request, ServletResponse response)
Dispatcher:调度
特点:
- 浏览器地址栏路径不发生变化
- 只能转发到当前服务器内部资源中。
- 转发是一次请求
1. 转发给servlet
发送请求到ForwardDemo1,之后转发到ForwardDemo2 ,再由ForwardDemo2转发到ForwardDemo3
jsp
<a href="/servlet/forward/demo1">please enter me to servlet 03</a>
ForwardDemo1
@WebServlet("/forward/demo1")
public class ForwardDemo1 extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//方法一:创建对象进行转发RequestDispatcher requestDispatcher = request.getRequestDispatcher("/forward/demo2");requestDispatcher.forward(request,response);System.out.println("hello i am demo1");//转发后,当demo2执行完,后面的代码继续执行。//方法二:直接进行转发 注意:在同一个servlet中不能进行两次转发//request.getRequestDispatcher("/forward/demo3").forward(request,response);}
}
ForwardDemo2
@WebServlet("/forward/demo2")
public class ForwardDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.getRequestDispatcher("/forward/demo3").forward(request,response);System.out.println("hello i am demo2");}
}
ForwardDemo3
@WebServlet("/forward/demo3")
public class ForwardDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("hello i am demo3");}
}
结果:
2. 转发给jsp页面
请求连接
<a href="/servlet/forward/demo4">please enter me to forward01.jsp </a>
@WebServlet("/forward/demo4")
public class ForwardDemo4 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.getRequestDispatcher("/request/forward01.jsp").forward(req,resp);}
}
前端页面forward01.jsp
- 使用EL表达式接收
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<h1>hello demo4</h1>
</body>
3. 共享数据
**域对象:**一个有作用范围的对象,可以在范围内共享数据
request域: 代表一次请求的范围,一般用于请求转发的多个资源中共享数据
方法:
- void setAttribute(String name , object obj) :存储数据
- object getAttitude(String name) :通过键获取值
- void removeAttribute(String name) :通过键移除键值对
1. 转发给servlet
存储数据到request域中
<a href="/servlet/forward/demo5">please enter me to servlet 05 </a>
@WebServlet("/forward/demo5")
public class ForwardDemo5 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setAttribute("name","Tom");//转发给servletreq.getRequestDispatcher("/forward/demo6").forward(req,resp);}
}
在其他类中获取数据
@WebServlet("/forward/demo6")
public class ForwardDemo6 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Object name = req.getAttribute("name");System.out.println(String.valueOf(name));}
}
结果:
2. 转发给JSP
@WebServlet("/forward/demo7")
public class ForwardDemo7 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setAttribute("name","Lily");req.getRequestDispatcher("/request/forward01.jsp").forward(req,resp);}
}
jsp获取数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<h1>hello demo4</h1>
<span>${name}</span>
</body>
</html>
结果:
中文乱码问题
发送get请求时,不会发生乱码问题。因为Tomcat8已解决这种问题。
当发送post请求时,如果请求内容为中文,会出现乱码问题。
解决措施:请求参数前声明一下编码格式 request.setCharacterEncoding(“utf-8”);
servlet
@WebServlet("/mess-code/demo1")
public class MessCodeDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println(req.getParameter("username"));System.out.println(req.getParameter("password"));}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//出现乱码问题,没有解决。使用req.getParameter("")获取参数设置utf-8可以解决BufferedReader reader = req.getReader();String line = null;while ((line=reader.readLine())!=null){//使用URLDecoder进行解码System.out.println(URLDecoder.decode(line,"utf-8"));}}
}
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<h1>乱码问题:</h1>
<form action="/servlet/mess-code/demo1" method="get"><h1>发送get请求</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="text"><br><input type="submit" value="提交">
</form>
<br><form action="/servlet/mess-code/demo1" method="post"><h1>发送post请求</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="text"><br><input type="submit" value="提交">
</form>
结果:
**解决方式:**设置字符编码
@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("post请求");req.setCharacterEncoding("UTF-8");System.out.println(req.getParameter("username"));System.out.println(req.getParameter("password"));}
2. 如何同时运行两个项目,部署两个服务器
1. 创建一个空的项目工程文件
2. 创建项目名称
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ihkIdErZ-1652970980045)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211220110904991.png)]
创建两个子模块 pro_01和pro_02
注意:配置jdk版本。
分别添加框架支持
分别添加Tomcat服务
注意:端口号的Name不能一样。
分别启动Tomcat服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzzeBt1J-1652970980049)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211220111149430.png)]
3. 案例(见下案例)
2. ServletResponse
1. 介绍
Response体系
设置响应消息
设置响应行
格式 HTTP/1.1 200 OK
设置状态码:set
设置响应头
setHeader(String name, String value)
设置响应体
使用步骤:
获取输出流
字符输出流: PrintWriter getWriter()
字节输出流: Servletoutputstream getoutputStream()
使用输出流,将数据输出到客户端浏览器
@WebServlet("/reqs/demo1")
public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String message = "hello response";PrintWriter out = resp.getWriter();out.println("<html><body>");out.println("<h1>" + message + "</h1>");out.println("</body></html>");}
}
2. 案例
1. 重定向
a. 资源跳转的方式
b. 两种方式实现,推荐选择第二种。
RedirectDemo1
@WebServlet("/redirect/demo1")
public class RedirectDemo1 extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setAttribute("message", "tom");//方式一//1.1设置状态码resp.setStatus(302);//1.2设置响应头locationresp.setHeader("location", "/servlet/redirect/demo2");//方式二(简单,优先使用)注意:转发需要加虚拟路径//resp.sendRedirect("/servlet/redirect/demo2");}
}
RedirectDemo2
@WebServlet("/redirect/demo2")
public class RedirectDemo2 extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//重定向不支持数据共享Object msg = req.getAttribute("message");System.out.println(msg);String message = "hello i am demo2";PrintWriter out = resp.getWriter();out.println("<html><body>");out.println("<h1>" + message + "</h1>");out.println("</body></html>");}
}
c. 重定向和转发的区别
区别
面试经常考这两个的区别(forword和redirect的区别)
重定向:
页面跳转,地址栏发生变化
重定向可以访问其他站点(服务器),比如访问百度
重定向是两次请求,不能使用request域来共享数据
requset.setAttribute("name","Tom");//设置共享资源
response.sendRedirect("https://www.baidu.com/");//访问百度Object msg = request.getAttribute("msg"); //获取共享资源
System.out.println(msg); // null 获取不到
转发:
- 页面跳转,地址栏不发生改变
- 转发只能访问当前服务器下的资源
- 转发是一次请求,可以使用request域来共享资源
什么时候用转发,什么时候用重定向
(101条消息) 什么时候使用请求转发?而什么时候又该使用请求重定向?请求转发和请求重定向的区别,及使用_后端阿一的博客-CSDN博客_转发什么时候用重定向
d. 路径写法
路径分类:
**相对路径 :**当前资源和目录资源之间相对的位置关系。 ./ 开头
./当前目录 …/上一级目录
**绝对路径:**通过绝对路径,可以确定唯一资源。 以 / 开头
- 如:http://localhost:8888/Response/servletDemo2 /Response/servletDemo2 ,前面的http可以省略
- 判断定义的路径给谁用的,判断请求将来从哪里发出(虚拟目录什么时候使用)
- 客户端浏览器使用,需要添加虚拟目录(项目所在文件夹的名称)
- 如网页上的超链接,点击访问服务器,是客户端浏览器使用,需要添加虚拟目录
- ,,重定向
- 服务器使用,不需要添加虚拟目录
- 转发操作,是服务器内部访问的
- 注意:重定向是服务器将状态码和路径传递给客户端,客户端再通过路径访问服务器,是客户端浏览器使用,需要添加虚拟目录
- 客户端浏览器使用,需要添加虚拟目录(项目所在文件夹的名称)
//重定向:需要虚拟目录 /servlet为虚拟路径
resp.sendRedirect("/servlet/redirect/demo2");//转发:不需要虚拟目录
request.getRequestDispatcher("/forward/demo3").forward(request,response);
e. 动态路径获取
问题:我们通过上述代码可以看出,填写的虚拟目录的路径是固定的/servlet,那么问题来了,以后假如我写非常多的接口,突然我把虚拟目录的路径改了,那么我就得把所有的虚拟目录的路径改了,这样就太麻烦了。所以我们在实际的编程中,都是通过动态获取虚拟目录的方式
动态获取虚拟目录
//动态获取虚拟目录
String contextPath = request.getContextPath();
response.sendRedirect(contextPath+"/redirect/demo2");
2. 服务器输出字符数据到浏览器
步骤:
- 获取字符输出流
- 输出数据
ResponseDemo2
@WebServlet("/reqs/demo2")
public class ResponseDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取字符输出流PrintWriter writer = resp.getWriter();//输出数据writer.write("hello world");//输出html格式数据writer.write("<h1>hello world<h1>");//输出中文数据writer.write("你好,我的世界");//中文乱码}
}
- 中文乱码问题
- 浏览器默认的字符编码根据操作系统的语言环境有关,win10的为GBK(中文简体)或gb312
- 如果服务器的字符是new出来的,也是GBK,如果是获取的,字符编码是Tomcat定义的ISO-8859-1
- 解决:获取流对象之前,设置编码格式 response.setContentType(“text/html;charset=utf-8”)
@WebServlet("/reqs/demo2")
public class ResponseDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//解决乱码//方式一:
// resp.setContentType("text/html;charset=utf-8");//方式二://1.1获取流对象之前,把流的默认编码“ISO-8859-1”设置为:utf-8resp.setCharacterEncoding("utf-8");//1.2告诉浏览器,服务器发送的数据的编码,建议浏览器使用此编码resp.setHeader("content-type","text/html;charset=utf-8");//获取字符输出流PrintWriter writer = resp.getWriter();//输出数据writer.write("hello world");//输出html格式数据writer.write("<h1>hello world<h1>");//输出中文数据writer.write("你好,我的世界");//中文乱码}
}
3. 服务器输出字节数据
步骤:
- 获取字节输出流
- 输出数据
ResponseDemo3
@WebServlet("/reqs/demo3")
public class ResponseDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//方式2 设置编码格式resp.setContentType("text/html;charset=utf-8");ServletOutputStream outputStream = resp.getOutputStream();outputStream.write("hello world".getBytes());}
}
4. 验证码响应(见下案例)
3. ServletContext
1. 介绍
概念:
代表整个web应用,可以和程序的容器(服务器)来通信
2. 获取方式
1.通过request对象获取
request . getServletContext();
2.通过HttpServlet获取
this .getServletContext();
ServletContextDemo1
@WebServlet("/context/demo1")
public class ServletContextDemo1 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {ServletContext servletContext = request.getServletContext();ServletContext servletContext1 = this.getServletContext();System.out.println(request==this);//falseSystem.out.println(servletContext == servletContext1);//trueSystem.out.println(servletContext);}
}
3. 功能
1. 获取MIME类型
MIME类型:在互联网通信过程中定义的一种文件数据类型
格式:大类型/小类型 如:text/html image/jpeg ;
获取: string getMimeType(String file)
@WebServlet("/context/demo1")
public class ServletContextDemo1 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String file = "a.jpg";String mimeType = servletContext.getMimeType(file);System.out.println(mimeType);}
}
2. 域对象:共享数据
a. setAttribute(String name , object value)
b. getAttribute(String name)
c. removeAttribute(String name )
ServletContextDemo2设置共享数据
@WebServlet("/context/demo2")
public class ServletContextDemo2 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.getServletContext().setAttribute("username","tom");response.sendRedirect(request.getContextPath()+"/context/demo3");}
}
ServletContextDemo3获得共享数据
@WebServlet("/context/demo3")
public class ServletContextDemo3 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Object username = request.getServletContext().getAttribute("username");System.out.println(username);PrintWriter printWriter = response.getWriter();printWriter.println("hello i am"+username);}
}
注意:
ServletContext对象范围:所有用户请求的数据。。 张三请求的数据,张四也可以看到
该对象在服务器中贮存的时间特别长,从开始到销毁,所以用的时候需要非常谨慎。
3. 获取文件的真实路径
获取文件(服务器)的真实路径 String getRealPath(“”);
文件在不同路径下的获取方法
@WebServlet("/context/demo4")
public class ServletContextDemo4 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {ServletContext servletContext = request.getServletContext();//获取当前路径String realPath1 = servletContext.getRealPath("/");System.out.println(realPath1);//获取类路径下文件String realPathA = servletContext.getRealPath("/WEB-INF/classes/ConfigurationA");System.out.println(realPathA);//获取WEB-INF下文件String realPathB = servletContext.getRealPath("/WEB-INF/ConfigurationB");System.out.println(realPathB);//获取web下文件String realPathC= servletContext.getRealPath("/ConfigurationC");System.out.println(realPathC);}
}
真实路径
G:\ComputerStudy\cloudnote\knowledge\knowledges\mystudy\AllCode\JavaWebCode\knowledges_show\out\artifacts\knowledges_show_war_exploded\
G:\ComputerStudy\cloudnote\knowledge\knowledges\mystudy\AllCode\JavaWebCode\knowledges_show\out\artifacts\knowledges_show_war_exploded\WEB-INF\classes\ConfigurationA
G:\ComputerStudy\cloudnote\knowledge\knowledges\mystudy\AllCode\JavaWebCode\knowledges_show\out\artifacts\knowledges_show_war_exploded\WEB-INF\ConfigurationB
G:\ComputerStudy\cloudnote\knowledge\knowledges\mystudy\AllCode\JavaWebCode\knowledges_show\out\artifacts\knowledges_show_war_exploded\ConfigurationC
4. 案例
文件下载
在浏览器中超链接的图片可以直接查看,但是某些视频需要下载获取.
注意:
在tomcat服务中,静态资源要保存在web目录下,否则无法直接访问图片。
需求:
页面显示超链接
点击超链接后弹出下载提示框
完成图片文件下载
分析:
超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求(图片可以直接被浏览器解析,但是视频是不可以的)
任何资源都必须弹出下载提示框
使用响应头设置资源的打开方式: content-disposition: attachment ;filename=xxx
步骤:
定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
定义Servlet
获取文件名称
使用字节输入流加载文件进内存
指定response的响应头: content- disposition: attachment; filename=xxx
将数据写出到response输出流
代码展示
file.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<a href="/servlet/img/lion.jpg">狮子图片</a>
<a href="/servlet/img/球员.jpg">球员图片</a>
<a href="/servlet/img/movie.mp4">柯南视频</a>
<br>
<a href="/servlet/file?filename=lion.jpg">狮子图片下载</a>
<a href="/servlet/file?filename=球员.jpg">球员图片下载</a>
<a href="/servlet/file?filename=movie.mp4">柯南视频下载</a></body>
</html>
FileHandle接口:
接口中,在获取文件名之前,先解析文件名。
@WebServlet("/file")
public class FileHandle extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1.获取请求参数,文件名称String fileName = request.getParameter("filename");System.out.println(fileName); //photo//2.使用字节流,加载文件进内存//2.1找到文件所在的服务器路径ServletContext servletContext = request.getServletContext();String realPath = servletContext.getRealPath("/img/" + fileName);//图片的真是路径//2.2使用字节流关联FileInputStream fis = new FileInputStream(realPath);//自己理解为,通过此步骤,可以获取从浏览器把文件保存到本地的权限//3.设置response响应头//3.1设置响应头的数据类型:content-type //因为我们也不知道文件的类型,所以需要设置。String mimeType = servletContext.getMimeType(fileName);response.setHeader("content-type", mimeType);//3.1.1解决中文文件名问题//获取user-agent请求头String agent = request.getHeader("user-agent");//3.2设置响应头的打开方式response.setHeader("content-disposition", "attachment;filename=" + DownLoadUtils.getFileName(agent, fileName));//filename设置的为下载提示框的名字//4.将输入流的数据写出到输出流中ServletOutputStream sos = response.getOutputStream();byte[] buff = new byte[1024 * 8];int len = 0;while ((len = fis.read(buff)) != -1) {sos.write(buff, 0, len);}fis.close();//输入流关闭}
}
文件夹:
中文文件名称无法显示问题
<a href="/servlet/file?filename=球员.jpg">球员图片下载</a>
解决思路
获取客户端使用的浏览器版本信息
根据不同的浏览器版本信息,设置filename的编码 式不同
封装的代码
import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;public class DownLoadUtils {public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {if (agent.contains("MSIE")) {// IE浏览器filename = URLEncoder.encode(filename, "utf-8");filename = filename.replace("+", "");} else if (agent.contains("Firefox")) {//火狐浏览器BASE64Encoder base64Encoder = new BASE64Encoder();filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";} else {// 其它浏览器filename = URLEncoder.encode(filename, "utf-8");}return filename;}
}
4.Cookie和Session
优秀blog
(104条消息) Session是怎么实现的?存储在哪里?_码农的博客-CSDN博客_session怎么存储数据
cookie和session的详解和区别
https://www.cnblogs.com/accolade/p/10817529.html
会话技术
介绍
Cookie和Session属于会话技术,cookie存储在浏览器上,session存储在服务器中。
**会话:**一次会话中包含多次请求和响应。
**一次会话:**浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止。
就好比打电话,A给B打电话,接通之后,会话开始,直到挂断电话,该次会话就结束了,而浏览器访问服务器,就跟打电话一样,浏览器A给服务器发送请求,访问web程序,该次会话就已经接通,其中**不管浏览器发送多少请求(就相当于接通电话后说话一样),都视为一次会话,**直到浏览器关闭,本次会话结束。其中注意,一个浏览器就相当于一部电话,如果使用火狐浏览器,访问服务器,就是一次会话了,然后打开google浏览器,访问服务器,这是另一个会话,虽然是在同一台电脑,同一个用户在访问,但是,这是两次不同的会话。
**功能:**在一次会话的范围内的多次请求间,共享数据
方式:
- 客户端会话技术: Cookie
- 服务器端会话技术: Session
Cookie和Session之间的区别和联系
假如一个咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:
该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。但是http协议本身是无状态的
发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。也就是cookie。 顾客就相当于浏览器,cookie如何工作,下面会详细讲解
发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。(session)
由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。
举例:
网上购物时,添加一次购物车,就是一次请求,我将多个物品添加到购物车,就是多次请求,最后计算总价时,就需要将之前请求的数据进行一下总和,cookie就是做这个总和的。
Cookie
cookie介绍
会员卡的第二种方案。
客户端会话技术,将数据保存到客户端。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
使用步骤:
- 创建cookie对象,绑定数据 new Cookie(String name,String value )
- 发送cookie对象 response.addCookie(Cookie c);
CookieDemo1
@WebServlet("/cookie/demo1")
public class CookieDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {//1.创建cookie对象Cookie c = new Cookie("msg", "hello");//2.发送cookieresponse.addCookie(c);}
}
- 获取cookie,拿到数据
CookieDemo2
@WebServlet("/cookie/demo2")
public class CookieDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {//3.获取cookieCookie[] cookies = request.getCookies();if(cookies!=null){for (Cookie c:cookies) {String name = c.getName();String value =c.getValue();System.out.println(name+":"+value); //msg:hello}}}
}
两次请求的response和request头对比
第一次请求:
第一次请求的请求头是没有cookie的,如果有,是浏览器缓存,清除一下即可。
第二次请求:
总结:
- 第一次请求时,是获取浏览器中的数据,所以第一次request请求未携带任何cookie信息。
- 第一次请求完毕后,返回的response将第一次创建的cookie数据返回
- 第二次请求时,浏览器拿着第一次response返回的cookie数据发送到服务器
Cookie细节
1. 一次请求发送多个Cookie
CookieDemo3
@WebServlet("/cookie/demo3")
public class CookieDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {//1.创建cookie对象Cookie c1 = new Cookie("msg","hello");Cookie c2 = new Cookie("asd","hsdlo");Cookie c3 = new Cookie("csa","heffo");
//2.发送cookieresponse.addCookie(c1);response.addCookie(c2);response.addCookie(c3);}
}
CookieDemo2
@WebServlet("/cookie/demo2")
public class CookieDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {//3.获取cookieCookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie c : cookies) {String name = c.getName();String value = c.getValue();System.out.println(name + ":" + value); //msg:hello}}}
}
CookieDemo4
@WebServlet("/cookie/demo4")
public class CookieDemo4 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {//什么都没写,但是request的请求头里面保存着之前cookie返回的数据}
}
第一次请求数据,response的响应头
第二次请求时,resquest的请求头
第三次请求时,resquest的请求头
响应头
从以上的几次请求响应可以看出,除了第一次访问,其他访问请求cookie一直在都在浏览器访问服务器中作为数据传递。不管你调没调用获取cookie的方法。
2. Cookie的存活时间
可以自由设置,默认是关闭浏览器,cookie就没用了。
cookie.setMaxAge(expiry); //设置cookie被浏览器保存的时间。
expiry:单位秒,默认为-1,
expiry=-1:代表浏览器关闭后,也就是会话结束后,cookie就失效了,也就没有了。
expiry>0:代表浏览器关闭后,cookie不会失效,仍然存在。并且会将cookie保存到硬盘中,直到设置时间过期才会
expiry=0:删除cookie。不管是之前的expiry=-1还是expiry>0,当设置expiry=0时,cookie都会被浏览器给删除。
测试:
@WebServlet("/cookie/demo5")
public class CookieDemo5 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {Cookie cookie = new Cookie("name","TOM");//设置cookie一个小时后过期//cookie.setMaxAge(60*60);//设置cookie马上到期(清除cookie)cookie.setMaxAge(0);response.addCookie(cookie);}
}
3. Cookie是否支持中文数据
- 在tomcat 8之前cookie中不能直接存储中文数据。需要将中文数据转码,一般采用URL编码(%E3)
- 在tomcat 8之后,cookie支持中文数据。但是对于特殊字符比如空格不支持。还得使用URL编码和URL解码
存储中文数据
@WebServlet("/cookie/demo6")
public class CookieDemo6 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {Cookie[] cookies = request.getCookies();for (int i = 0; i < cookies.length; i++) {System.out.println(cookies[i].getValue());}Cookie cookie = new Cookie("name", "张三");cookie.setMaxAge(60 * 60);response.addCookie(cookie);}
}
存储特殊字符如:空格
Cookie cookie = new Cookie("name", "张 三 ");
报错:
原因:
某些特殊的字符,例如:空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号都不能作为Cookie的内容。
解决方案:
对于cookie存储特殊字符,我们可以先把数据用URL编码转化,存储到cookie里,我们需要cookie的时候,再用URL解码。
需要用到两个方法
URLEncoder类的静态方法encode()
URLDecoder类的静态方法decode()
@WebServlet("/cookie/demo6")
public class CookieDemo6 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {Cookie[] cookies = request.getCookies();for (int i = 0; i < cookies.length; i++) {System.out.println(URLDecoder.decode(cookies[i].getValue(),"utf-8"));}Cookie cookie = new Cookie("name", URLEncoder.encode("张 三 ","utf-8"));cookie.setMaxAge(60 * 60);response.addCookie(cookie);}
}
4. cookie的共享问题
一个服务器,一个web项目
默认情况下,cookie共享servlet路径下的第一层路径
@WebServlet("/cookie/demo7")
@WebServlet("/cookie/demo8")
@WebServlet("/cookie-v/demo9")
默认情况下,demo7,demo8下数据cookie共享,demo9下数据不共享。
解决措施:cookie.setPath(“/虚拟路径”);
CookieDemo7
@WebServlet("/cookie/demo7")
public class CookieDemo7 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {//1.创建cookie对象Cookie c1 = new Cookie("name","Tom");Cookie c2 = new Cookie("age","18");Cookie c3 = new Cookie("sex","man");
//2.发送cookieresponse.addCookie(c1);response.addCookie(c2);response.addCookie(c3);}
}
CookieDemo8
控制台输出对应Cookie数据
@WebServlet("/cookie/demo8")
public class CookieDemo8 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {//3.获取cookieCookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie c : cookies) {String name = c.getName();String value = c.getValue();System.out.println(name + ":" + value); //msg:hello}}}
}
CookieDemo9
控制台没有输出对应Cookie数据
@WebServlet("/cookie-v/demo9")
public class CookieDemo9 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {//3.获取cookieCookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie c : cookies) {String name = c.getName();String value = c.getValue();System.out.println(name + ":" + value); //msg:hello}}}
}
假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
默认情况下cookie不能在多个web项目共享
setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录setPath(“/cookie”)
如果要共享,则可以将path设置为"/" setPath(“/”)
项目一:
@WebServlet("/one/demo1") public class ServletDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("cookie1");Cookie[] cookies = req.getCookies();if (cookies!=null){for (int i = 0; i <cookies.length ; i++) {System.out.println("cookie1:"+cookies[i].getValue());}}Cookie cookie = new Cookie("name","cookieOne");cookie.setPath("/");resp.addCookie(cookie);} }
项目二:
@WebServlet("/two/demo1") public class ServletDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("cookie2");Cookie[] cookies = req.getCookies();if (cookies!=null){for (int i = 0; i <cookies.length ; i++) {System.out.println("cookie2:"+cookies[i].getValue());}}} }
同一个服务器的两个web项目
如果我没有设置 setPath(“/”)
设置了 setPath(“/”)
在多个服务器之间,能否实现数据共享
假如:www.tieba.baidu.com和www.new.baidu.com这两个网站肯定都属于百度的项目,但是这两个项目肯定不会运行在一个服务器中,但是这两个网页之间肯定有某些数据的共享。
- 二者的域名为都有共同成分.baidu.com。这是一级域名。.news或.tieba 这是二级域名。
- 只要我们保证两个网页的一级域名相同,那么我们就可以实现不同浏览器之间的数据共享。
- setDomain(String path) :如果设置一级域名相同, 那么多个服务器之间cookie可以共享
- setDomain(" . baidu. com") ,那么tieba. baidu. com和news . baidu. com中cookie可以共享
Cookiie总结
工作流程:
servlet创建cookie,保存少量数据,发送浏览器。
浏览器获得服务器发送的cookie数据,将自动的保存到浏览器端。
下次访问时,浏览器将自动携带cookie数据发送给服务器。
cookie操作
1.创建cookie:new Cookie(name,value)
2.发送cookie到浏览器:HttpServletResponse.addCookie(Cookie)
3.servlet接收cookie:HttpServletRequest.getCookies() 浏览器发送的所有cookie
cookie特点
每一个cookie文件大小:4kb , 如果超过4kb浏览器不识别
只能存字符串
一个web站点(web项目):发送20个
3.一个浏览器保存总大小:300个
cookie 不安全,可能泄露用户信息。浏览器支持禁用cookie操作。
默认情况生命周期:与浏览器会话一样,当浏览器关闭时cookie销毁的。—临时cookie
作用:
cookie一般用于存出少量的不太敏感的数据
在不登录的情况下,完成服务器对客户端的身份识别
案例
记录访问时间
需求:
在服务器中的Servlet判断是否有一个名为lastTime的cookie
- 有:不是第一次访问
- 响应数据:欢迎回来,您上次访问时间为:2021年02月21日 21:02:22
- 写回Cookie: lastTime=2021年02月21日 21:04:59
- 没有:是第一次访问
- 响应数据:您好,欢迎您首次访问
- 写回Cookie: lastTime=2021年02月21日 21:04:59
@WebServlet("/cookie/visit")
public class CookieItem extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//设置响应消息体的编码格式response.setContentType("text/html;charset=utf-8");//1、判断是否有cookie 先获取所有的cookie、Cookie[] cookies = request.getCookies();//1.1设置一个关于是否有lastname的cookie判断boolean flag = false;//2.遍历cookie数组if (cookies != null && cookies.length > 0) {for (Cookie cookie : cookies) {//3.获取cookie的名称String name = cookie.getName();//4.判断名称是否是:lastnameif ("lastTime".equals(name)) {//有该cookie 表示不是第一次访问flag = true;//响应数据//获取cookie的数据String value = cookie.getValue();System.out.println("解码前" + value);//URL解码value = URLDecoder.decode(value, "utf-8");System.out.println("解码后" + value);response.getWriter().write("<h1>欢迎回来,您上次的访问时间为" + value + "</h1>");//此处为中文消息,需要设置响应消息体//设置cookie的值,现在的值//获取当前时间的字符串,重新设置cookie的值,重新发送Date date = new Date();System.out.println("修改前" + date);//设置时间的格式,默认的为美国的时间格式SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");//设置日期时间的格式//设置时区,以下三种方式,任选其一。
// sdf.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
// sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));sdf.setTimeZone(TimeZone.getTimeZone("Etc/GMT-8"));String str_date = sdf.format(date);System.out.println("编码前" + str_date);//URL编码str_date = URLEncoder.encode(str_date, "utf-8");System.out.println("编码后" + str_date);//设置cookiecookie.setValue(str_date);//设置cookie的存活时间.存活一个月cookie.setMaxAge(60 * 60 * 24 * 30);//发送cookieresponse.addCookie(cookie);//找到名字为lastname的cookie之后,就不要循环了break;}}}if (cookies == null || cookies.length == 0 || flag == false) {Date date = new Date();//设置时间的格式,默认的为美国的时间格式SimpleDateFormat sdf = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");//设置日期时间的格式//设置时区,以下三种方式,任选其一。
// sdf.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
// sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));sdf.setTimeZone(TimeZone.getTimeZone("Etc/GMT-8"));//将时间生成字符串String str_date = sdf.format(date);//URL编码str_date = URLEncoder.encode(str_date, "utf-8");System.out.println("编码后" + str_date);//设置cookieCookie cookie = new Cookie("lastTime", str_date);cookie.setValue(str_date);//设置cookie的存活时间.存活一个月cookie.setMaxAge(60 * 60 * 24 * 30);//发送cookieresponse.addCookie(cookie);//响应数据//获取cookie的数据String value = cookie.getValue();//URL解码value = URLDecoder.decode(value, "utf-8");System.out.println("解码后" + value);response.getWriter().write("<h1>你好,欢迎您首次访问</h1>" + value);//此处为中文消息,需要设置响应消息体}
Session
Session介绍
会员卡的第三种方案。
服务器端会话技术,在一次会话的多次请求间共 享数据,将数据保存在服务器端的对象中。HttpSession。
使用步骤:
获取HttpSession对象 :
- HttpSession session = request . getSession();
使用HttpSession对象 :
- object getAttribute(String name)
- void setAttribute(String name, object value)
- void removeAttribute(String name)
demo1:创建数据
@WebServlet("/session/demo1")
public class SessionDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取session对象HttpSession session = req.getSession();//2.存储数据session.setAttribute("name","Tome");}
}
demo2:获取数据
@WebServlet("/session/demo2")
public class SessionDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//使用session共享数据//1.获取session对象HttpSession session = req.getSession();//2.获取数据Object name = session.getAttribute("name");System.out.println(name);}
}
原理:
session是依赖于cookie的。
第一次访问,请求头里面无cookie
第二次访问请求头里面包含第一次返回的cookie
他们的JESSIONID的值是一样的,这也就是为什么一次会话,确定的session对象是同一个。
强制清除浏览器cookie后
关闭后重新打开,session会话是否结束
Session原理
参考文章:(104条消息) Session是怎么实现的?存储在哪里?_码农的博客-CSDN博客_session怎么存储数据
session在访问tomcat服务器HttpServletRequest的getSession(true)的时候创建,tomcat的ManagerBase类提供创建sessionid的方法:随机数+时间+jvmid。
存储在服务器的内存中,tomcat的StandardManager类将session存储在内存中,也可以持久化到file,数据库,memcache,redis等。客户端只保存sessionid到cookie中,而不会保存session,session销毁只能通过invalidate或超时,关掉浏览器并不会关闭session。
在Java中是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建。
在创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有Session id;当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。
Session细节
1. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
- 默认情况下:不是。
- 如果需要相同,则可以创建cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
@WebServlet("/session/demo3")
public class SessionDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//使用session共享数据//1.获取session对象HttpSession session = req.getSession();session.setAttribute("name","Lily");//打印sessionSystem.out.println(session);//2. 设置cookie过期时间。Cookie cookie =new Cookie("JSESSIONID",session.getId());cookie.setMaxAge(60*60);resp.addCookie(cookie);}
}
2.客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
- session对象都是存在服务器里面的,如果关闭服务器后,再打开,那么就又重新创建了两个服务器对象,肯定不一样。
- 不是同一个,但是要确保对象不丢失(假如我添加了一些商品到购物车中,我有点事情出去了,京东服务器重启了,那么我的购物车的内容不都清空了吗?)
- session钝化
- 在服务器正常关闭之前,将session对象系列化到硬盘上
- session活化
- 在服务器启动后,将session文件转化为内存中的session对象即可
- session钝化
- session钝化和活化Tomcat已经自动帮我们解决了,只不过我们是用IDEA时,IDEA不会活化,获取不到,所以我们在本地部署Tomcat即可。
3. session的失效时间?
session什么时候被销毁?
- 服务器关闭
- session对象调用invalidate() 。
- session默认失效时间30分钟
设置session过期时间的方式
选择性配置修改(全局配置)
<session-config><session-timeout>30</session-timeout></session-config>
在web-xml里面,可以进行相关的配置。
通过session对象进行修改
HttpSession session = req.getSession(); session.setMaxInactiveInterval(100);
4. Session和cookie区别
- session存储数据在服务器端,Cookie在客户端
- session没有数据大小限制,Cookie有
- session数据安全,Cookie相对于不安全
5. Filter和Listener
JavaWeb三大组件:Servlet,Filter,Listener
Filter
介绍
概念
生活中的过滤器:净水器,空气净化器,土匪、
web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。
过滤器的作用:
一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤…
入门:
1.步骤:
1.定义一个类,实现接口Filter(见下面的注解)
2.重写方法
3.配置拦截路径
拦截路径
方式一:在web.xml中配置
创建过滤器
public class FilterDemo implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//放行前,通常执行request的相关代码System.out.println("");filterChain.doFilter(servletRequest,servletResponse);//放行后,通常执行response的相关代码System.out.println("");}@Overridepublic void destroy() {} }
<?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"><filter><filter-name>demo1</filter-name><filter-class>filter.FilterDemo</filter-class></filter><filter-mapping><filter-name>demo1</filter-name> <!-- 过滤所有路径--><url-pattern>/*</url-pattern></filter-mapping> </web-app>
方式二:使用注解中配置@WebFilter(“/*”)
@WebFilter("/*") //所有页面被访问之前都被过滤 public class FilterDemo implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//放行前,通常执行request的相关代码System.out.println("");//放行,进行数据访问filterChain.doFilter(servletRequest,servletResponse);//放行后,通常执行response的相关代码System.out.println("");}@Overridepublic void destroy() {} }
Filter细节
1. 过滤器执行流程
1.执行过滤器(通常操作request相关数据)
2.执行放行后的资源(servlet中的代码)
3.回来执行过滤器放行代码下边的代码(通常操作response相关数据)
@WebFilter("/loginSec") //拦截该页面,此时页面数据访问不到
public class FilterDemo implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//放行前,通常执行request的相关代码System.out.println("");filterChain.doFilter(servletRequest,servletResponse);//放行后,通常执行response的相关代码System.out.println("");}@Overridepublic void destroy() {}
}
2. 过滤器生命周期方法
init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
doFilter:每一次请求被拦截资源时,会执行。执行多次
destroy :在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源
3. 过滤器配置详解
拦截路径配置:
@WebFilter(“/*”)
1.具体资源路径: /index. jsp 只有访问index. jsp资源时,过滤器才会被执行
2.拦截目录,/user/* 访问/user下的所有资源时,过滤器都会被执行
3.后缀名拦截: *.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
4.拦截所有资源: /* 访问所有资源时,过滤器都会被执行|
拦截方式配置:
浏览器访问服务器可以直接访问,也可以访问服务器后通过servlet转发到其他的servlet,即服务器内部访问。
注解配置:
设置dispatcherTypes属性
REQUEST :默认值。浏览器直接请求资源
FORWARD :转发访问资源
INCLUDE : 包含访问资源
ERROR :错误跳转资源
ASYNC :异步访问资源
//拦截方式配置 当请求或访问index.jsp时,会被拦截
@WebFilter(value = "/index.jsp" ,dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
@WebFilter(value = "/index.jsp" ,dispatcherTypes = DispatcherType.FORWARD)
xml配置
4. 过滤器链
执行顺序:如果有两个过滤器:过滤器1和过滤器2
1.过滤器1
2.过滤器2
3.资源执行
4.过滤器2
5.过滤器1
过滤器先后顺序问题:
- 注解配置:按照类名的字符串比较规则比较,值小的先执行
如: AFilter 和BFilter, AFilter就先执行 了。
- web. xml配置: 谁定义在上边,谁先执行
FilterDemo1
@WebFilter("/filter/*")
public class FilterDemo1 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//放行前,通常执行request的相关代码System.out.println("i am filter1 begin");filterChain.doFilter(servletRequest,servletResponse);//放行后,通常执行response的相关代码System.out.println("i am filter1 over");}@Overridepublic void destroy() {}
}
FilterDemo2
@WebFilter("/filter/*")
public class FilterDemo2 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//放行前,通常执行request的相关代码System.out.println("i am filter2 begin");filterChain.doFilter(servletRequest,servletResponse);//放行后,通常执行response的相关代码System.out.println("i am filter2 over");}@Overridepublic void destroy() {}
}
FilterTest1
@WebServlet("/filter/test1")
public class FilterTest1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("I am test1");}
}
FilterTest2
@WebServlet("/filter/test2")
public class FilterTest2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("I am test2");}
}
结果
Listener
概念: Listener表示监听器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。
事件监听机制:
事件:一件事情
事件源:事件发生的地方
监听器:一个对象
注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码
监听实现
- 实现ServletContextListener接口 :监听ServletContext对象的创建和销毁
void contextDestroyed(ServletContextEvent sce) : ServletContext对象被销毁之前会调用该方法
void contextInitialized(ServletContextEvent sce) : ServletContext对象创建后会调用该方法
监听器可以监听就是在application,session,request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件
Listener分类:
JavaWeb中提供了8个监听器。
创建Listener
- 实现ServletContextListener接口
- 添加@WebListener注解
@WebListener
public class ListenerDemo implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {//加载资源System.out.println("服务器启动了。监听器,执行了。");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {//释放资源.System.out.println("服务器关闭了。监听器,执行了。");}
}
6. 案例
1. 用户登陆(request)
案例需求:
编写login. html登录页面username & password两个输入框
使用Druid数据库连接池技术,操作mysql数据库中tb_students_info表(这里使用的是DRUID链接池)
使用JdbcTemplate技术封装JDBC(使用返回的数据直接封装)
登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
登录失败跳转到FailServlet展示:登录失败,用户名或密码错误
逻辑图示:
[外链图片转存中…(img-AwbxpJPO-1652970980102)]
报错:
Could not initialize class cn.itcast.Util.JDBCUtils
[外链图片转存中…(img-DZj6wmxL-1652970980103)]
解决:
jar包放在lib文件夹里面,而我却放在了libs里面
核心代码
建表语句
CREATE TABLE `tb_students_info` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id\r\n',`username` varchar(255) NOT NULL DEFAULT '' COMMENT '用户名',`password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码',`name` varchar(255) NOT NULL DEFAULT '' COMMENT '姓名',`dept_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '部门id',`age` smallint(6) unsigned NOT NULL DEFAULT '0' COMMENT '年龄',`sex` bit(1) NOT NULL DEFAULT b'0' COMMENT '性别 0 男 1 女',`height` smallint(6) unsigned NOT NULL DEFAULT '0' COMMENT '身高',`money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '存款',`login_date` datetime DEFAULT NULL COMMENT '登陆时间',PRIMARY KEY (`id`),KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;/*Data for the table `tb_students_info` */insert into `tb_students_info`(`id`,`username`,`password`,`name`,`dept_id`,`age`,`sex`,`height`,`money`,`login_date`) values (3,'2019002','2019002','Henry',2,100,'',185,'0.00','2021-12-08 23:27:08'),(4,'2019003','2019003','Jane',1,22,'',162,'100.00','2021-12-08 23:27:44'),(5,'2019004','2019004','Jim',6,21,'\0',175,'50.00','2021-12-08 23:27:47'),(6,'2019005','2019005','John',5,25,'',172,'50.00','2021-12-04 23:27:52'),(7,'2019006','2019006','Lily',1,0,'\0',165,'0.00','2021-11-30 23:27:57'),(8,'2019007','2019007','Susan',1,20,'',170,'0.00','2021-11-02 23:28:01'),(9,'2019008','2019008','Thomas',4,35,'\0',178,'0.00','2021-12-03 23:28:06'),(10,'2019009','2019009','Tom',3,15,'',165,'0.00','2021-12-26 23:28:10'),(11,'2019010','2019010','Jerry',1,15,'\0',170,'0.00','2021-12-08 23:27:47'),(22,'2019012','2019012','王五',1,18,'\0',178,'0.00','2021-12-11 00:00:00'),(23,'2019012','2019012','王五',1,18,'\0',178,'100.00','2021-12-11 00:00:00'),(24,'2019012','2019012','王五',1,18,'\0',178,'100.00','2021-12-12 13:56:25');
实体类
public class Student {private Long id;private String username;private String password;private String name;private Long deptId;private int age;private Boolean sex;private int height;private BigDecimal money;private LocalDateTime login_date;public Student() {}public Student(Long id, String username, String password, String name, Long deptId, int age, Boolean sex, int height, BigDecimal money, LocalDateTime login_date) {this.id = id;this.username = username;this.password = password;this.name = name;this.deptId = deptId;this.age = age;this.sex = sex;this.height = height;this.money = money;this.login_date = login_date;}public BigDecimal getMoney() {return money;}public void setMoney(BigDecimal money) {this.money = money;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Long getDeptId() {return deptId;}public void setDeptId(Long deptId) {this.deptId = deptId;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Boolean getSex() {return sex;}public void setSex(Boolean sex) {this.sex = sex;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public LocalDateTime getLogin_date() {return login_date;}public void setLogin_date(LocalDateTime login_date) {this.login_date = login_date;}@Overridepublic String toString() {return "Student{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", name='" + name + '\'' +", deptId=" + deptId +", age=" + age +", sex=" + sex +", height=" + height +", money=" + money +", login_date=" + login_date +'}';}
}
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<form action="/student_system/login" method="post">用户名:<input type="text" name="username">密码:<input type="text" name="password"><input type="submit" value="登陆">
</form>
</body>
</html>
请求代码:
@WebServlet("/login")
public class Login extends HttpServlet {private static final IStudentService studentService = new StudentServiceImpl();@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1.设置编码request.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");//2.获取请求参数String username = request.getParameter("username");String password = request.getParameter("password");if ("".equals(username) || "".equals(password) || username == null || password == null) {request.setAttribute("message", "fail");request.getRequestDispatcher("/login/fail.jsp").forward(request, response);return;}//3.查询数据库Student student = studentService.login(username, password);if (student == null) {request.setAttribute("message", "fail");request.getRequestDispatcher("/login/fail.jsp").forward(request, response);} else {//获取所有学生List<Student> allStundents = studentService.getAllStundents();request.setAttribute("message", allStundents);
// //获取当前学生
// request.setAttribute("message", student);request.getRequestDispatcher("/login/homepage.jsp").forward(request, response);}}}
homepage.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
${message}</body>
</html>
fail.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
${message}</body>
</html>
参数传递优化(BeanUtils)
问题:
进行参数的共享时,我们是将每个参数对象先保存起来,再进行共享。那么问题来了,我们按照之前的共享的方式,一次只能保存一个数据,假如我有多个数据要传递呢?
之前的方式
//2.获取请求参数 并封装成对象 String username = request.getParameter("username");String password = request.getParameter("password");User loginUser = new User(1, username, password);
使用BeanUtils工具类
简化数据封装
引入BeanUtils下载网站:http://commons.apache.org/proper/commons-beanutils/
导入jar包
[外链图片转存中…(img-jTzUynbR-1652970980105)]
**注意:**commons-logging这个jar包一定要导入,作用:
[外链图片转存中…(img-1QK4RnRc-1652970980106)]
简单使用:
@WebServlet("/add")
public class AddStudent extends HttpServlet {private static final IStudentService studentService = new StudentServiceImpl();@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1.设置编码request.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");//2.获取参数Map<String, String[]> parameterMap = request.getParameterMap();//3.创建空对象Student student = new Student();//4. 使用beanutils封装try {BeanUtils.populate(student, parameterMap);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}student.setDeptId(18L);student.setMoney(BigDecimal.valueOf(100.00));student.setSex(false);student.setLogin_date(LocalDateTime.now());int i = studentService.addStudent(student);if (i <= 0) {request.setAttribute("message", "插入失败");request.getRequestDispatcher("/login/fail.jsp").forward(request, response);} else {List<Student> allStudents = studentService.getAllStundents();request.setAttribute("message", allStudents);request.getRequestDispatcher("/login/homepage.jsp").forward(request, response);}}
}
扩展1:BeanUtils.工具类
简化数据封装用于封装JavaBean的
JavaBean :标准的Java类
- 要求:
- 类必须被public修饰
- 必须提供空参的构造器
- 成员变量必须使用private修饰
- 提供公共setter和getter方法
- 功能:封装数据
- 要求:
概念:
成员变量:
- 属性: setter和getter方法截取后的产物
- 例如: getUsername() --> Username–> username
- 假如我有一个成员变量交name,但是他的set/get方法为getHisName,那么截取后的产物为hisName。hisName为属性,name为成员变量。但是大多数情况下,属性的成员变量的名称是一样的
- 属性: setter和getter方法截取后的产物
方法:
setProperty( )。操作的是属性,而不是成员变量
设置对象1.成员变量gender setSex/getSexBeanUtils.setProperty(user,"gender","male");System.out.println(user); // User{id=0, username='null', password='null', gender='null'}BeanUtils.setProperty(user,"sex","male");System.out.println(user); // User{id=0, username='null', password='null', gender='male'}根据结果我们可以看出,成员变量gender,但他的set/get方法是sex。此时属性和成员变量不一样。而setProperty( )操作的是属性,所以运行出来的结果不一样
- getProperty( )
//获取对象
try {String genders = BeanUtils.getProperty(user, "sex");System.out.println(genders); male} catch (NoSuchMethodException e) {e.printStackTrace();}
- populate(object obj,Map map) :将map集合的键值对信息,封装到对应的JavaBean对象中
//A.1获取参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
//A.2创建一个User对象
User loginUser = new User();
//A.3使用BeanUtils封装
try {BeanUtils.populate(loginUser,parameterMap);
} catch (IllegalAccessException e) {e.printStackTrace();
} catch (InvocationTargetException e) {e.printStackTrace();
}
扩展2:使用JdbcTemplate工具类
使用参考:[mystudy/Mysql备课课件/3. JDBC的基本使用.md · Zhang-HaoQi/Knowledge - 码云 - 开源中国 (gitee.com)](https://gitee.com/zhang-haoqi/knowledge/blob/develop/mystudy/Mysql备课课件/3. JDBC的基本使用.md)
public class StudentDaoImpl implements IStudentDao {private JdbcTemplate template = new JdbcTemplate(DRUIDDateSource.getDataSource());//获取一条学生数据@Overridepublic Student login(String username, String password) {String sql = "SELECT * FROM tb_students_info WHERE username=? AND password=?";Student student = template.queryForObject(sql,new BeanPropertyRowMapper<Student>(Student.class),username,password);System.out.println(student);return student;}//获取所有学生@Overridepublic List<Student> getAllStudent() {String sql = "SELECT * FROM tb_students_info ";List<Student> query = template.query(sql, new BeanPropertyRowMapper<Student>(Student.class));System.out.println(query);return query;}//添加学生@Overridepublic int addStudent(Student student) {String sql = "INSERT INTO tb_students_info VALUE (?,?,?,?,?,?,?,?,?,?)";Date loginDate = new Date(Date.from(student.getLogin_date().atZone(ZoneOffset.ofHours(8)).toInstant()).getTime());int update = template.update(sql,null,student.getUsername(),student.getPassword(),student.getName(),student.getDeptId(),student.getAge(), student.getSex(), student.getHeight(), student.getMoney(), loginDate);return update;}
}
2. 验证码响应(response)
Servlet代码
@WebServlet("/reqs/verify")
public class VerifyCodingDemo extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1.创建一个对象,在内存中存图片(验证码图片对象)int width =100;int height= 50;BufferedImage image =new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//宽,高,格式//2.美化图片//2.1化背景颜色Graphics graphics = image.getGraphics();//画笔对象graphics.setColor(Color.pink);//设置画笔颜色graphics.fillRect(0,0,width,height);//填充一个蓝色的矩形 填充的位置和大小//2.2画边框graphics.setColor(Color.BLUE);//设置颜色graphics.drawRect(0,0,width-1,height-1);//画边框//2.3写验证码String str ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; //验证码包含的所有字符数字Random random = new Random();//画验证码验证符for (int i = 1; i < 5; i++) {int s = random.nextInt(str.length());//随机获取字符串的角标,长度在字符串长度的范围内char c = str.charAt(s);//获取随机的字符graphics.drawString(c+"",i*20,25);//字符串的内容和位置}//2.4画干扰线graphics.setColor(Color.black);for (int i = 0; i < 10; i++) {int x1 = random.nextInt(100);int x2 = random.nextInt(100);int y1 = random.nextInt(50);int y2 = random.nextInt(50);graphics.drawLine(x1,y1,x2,y2);}//3.将图片输入到页面展示ImageIO.write(image,"jpg",response.getOutputStream());//输出对象,后缀名,输出流输出}
}
JSP代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script>window.onload = function () {//点击图片切换//获取图片let elementById = document.getElementById("img");//更改图片,其实就是更改图片的路径,因为我们的图片是随机生成的,所以只需要图片重新加载一次即可。elementById.onclick = function () {// elementById.src="/Response/checkCodeServlet";//这时你会发现点击图片没有变化,这是因为之前浏览器已经访问过这个路径了,此时路径的内容已经存储在了浏览器的内存中,所以访问的还是之前的图片// 解决措施:假意传参. 如果参数传入相同的值,那么就会造成只能切换一次。跟上面一个道理。所以要传一个随机的值// elementById.src="/Response/checkCodeServlet?1";//因为随机数有时候也可能相同,所以遇到这种情况,我们可以通过把时间戳当做参数来解决。let date = new Date().getTime();elementById.src = "/servlet/reqs/verify?" + date;alert(data)}//点击文字切换let elementById1 = document.getElementById("change");elementById1.onclick = function () {let date = new Date().getTime();elementById.src = "/servlet/reqs/verify?" + date;}}</script>
</head>
<body>
<!--图片的点击切换-->
<img id="img" src="/servlet/reqs/verify">
<a id="change">看不清,换一张</a>
</body>
</html>
3. 用户登陆(session)
session免登录
需求:
- 第一次登录之后,进入主页,看到所有学生的数据
- 第二次不需要登录,直接请求登录接口即可获取所有学生数据
[外链图片转存中…(img-mKrMYDIZ-1652970980108)]
login.jsp
<form action="/students_system/loginSec" method="post">用户名:<input type="text" name="username">密码:<input type="text" name="password"><input type="submit" value="登陆"><br>${message}
</form>
登录接口
@WebServlet("/loginSec")
public class LoginSec extends HttpServlet {private static final IStudentService studentService = new StudentServiceImpl();@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IOException {//1.设置编码request.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");HttpSession session = request.getSession();//2. 判断当前用户服务器是否已保存Student user = (Student) session.getAttribute("user");if (user != null) {response.sendRedirect("/students_system/all");return;}//3.获取请求参数String username = request.getParameter("username");String password = request.getParameter("password");if ("".equals(username) || "".equals(password) || username == null || password == null) {request.setAttribute("message", "失败:用户名和密码不能为null");request.getRequestDispatcher("/login/login.jsp").forward(request, response);return;}//4.查询数据库Student student = studentService.login(username, password);if (student == null) {request.setAttribute("message", "失败:用户不存在");request.getRequestDispatcher("/login/login.jsp").forward(request, response);} else {session.setAttribute("user",student);response.sendRedirect("/students_system/all");}}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IOException {this.doPost(request, response);}}
AllStudent
@WebServlet("/all")
public class AllStudent extends HttpServlet {private static final IStudentService studentService = new StudentServiceImpl();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {List<Student> allStudents = studentService.getAllStundents();req.setAttribute("message", allStudents);req.getRequestDispatcher("/login/homepage.jsp").forward(req, resp);}
}
homepage.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
${message}</body>
</html>
4. 用户登陆(Filter)
session案例里面,我们在登陆接口里面判断了当前用户是否进行登陆。
问题:
- 如果用户登陆过后,再次访问时,访问的不是登陆接口,而是主页,那么我们还需要在主页的接口里判断用户是否登陆。代码冗余。
- 用户没有登录,直接调用主页接口,是可以调用的。
改善:
- 用户是否登陆的判断添加到过滤器里面,请求非登陆页面时,在此接口统一判断。
- 非登陆页面,不能直接进行接口访问。
[外链图片转存中…(img-emdxwaXW-1652970980110)]
登陆页面loginThird.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title><link rel="stylesheet" href="/students_system/css/login.css">
</head>
<body>
<form action="/students_system/loginThird" method="post">用户名:<input type="text" name="username">密码:<input type="text" name="password"><input class="sub-but" type="submit" value="登陆"><br>${message}
</form>
</body>
</html>
接口LoginThird
@WebServlet("/loginThird")
public class LoginThird extends HttpServlet {private static final IStudentService studentService = new StudentServiceImpl();@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IOException {//1.设置编码request.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");//2.获取sessionHttpSession session = request.getSession();Student user = (Student) session.getAttribute("user");if (user != null) {//有用户数据,重定向到主页response.sendRedirect("/students_system/all");return;}//3.获取请求参数String username = request.getParameter("username");String password = request.getParameter("password");if ("".equals(username) || "".equals(password) || username == null || password == null) {//失败request.setAttribute("message", "失败:用户名和密码不能为null");request.getRequestDispatcher("/login/loginThird.jsp").forward(request, response);return;}//3.查询数据库Student student = studentService.login(username, password);if (student == null) {//失败request.setAttribute("message", "失败:用户不存在");request.getRequestDispatcher("/login/loginThird.jsp").forward(request, response);} else {//重定向到主页session.setAttribute("user",student);response.sendRedirect("/students_system/all");}}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IOException {this.doPost(request, response);}}
过滤器FilterDemo
@WebFilter("/*")
public class FilterDemo extends HttpFilter {@Overrideprotected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {req.setCharacterEncoding("utf-8");res.setContentType("text/html;charset=utf-8");//不拦截的资源String[] urls = {"/login/loginSec.jsp","/login/loginThird.jsp","/login/login.jsp","/login/fail.jsp","/css/*","/loginThird"};String url = req.getRequestURL().toString();System.out.println(url);for (int i = 0; i < urls.length; i++) {if (url.contains(urls[i])){//放行不进行拦截的资源chain.doFilter(req,res);//如果不return,代码执行完后,还进入过滤器继续执行。return;}}//通过session判断用户是否已登录HttpSession session = req.getSession();Student user = (Student) session.getAttribute("user");if (user != null) {System.out.println("登陆未过期,免登陆");chain.doFilter(req,res);}else {System.out.println("登陆过期,请重新登陆");req.setAttribute("message", "请重新登陆");req.getRequestDispatcher("/login/loginThird.jsp").forward(req, res);}}
}
7. 问题汇总
1. WEB-INF资源问题
WEB-INF目录是不对外开放的,外部没办法直接访问到。所有只能通过映射来访问,比如映射为一个action或者servlet通过服务器端跳转来访问到具体的页面。这样可以限制访问,提高安全性。
1. 首页在WEB-INF下
通过xml设置欢迎页来解决。首页登陆页面。
正常登录页是首页,如果一定要放在webinf下面的话。xml中添加
<welcome-file-list><welcome-file>/WEB-INF/view/Login.jsp</welcome-file>
</welcome-file-list>
2. 获取WEB-INF指定jsp文件
通过转发的形式获取
@WebServlet("/forward/demo7")
public class ForwardDemo7 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.getRequestDispatcher("/WEB-INF/homepage.jsp").forward(req,resp);}
}
3. 静态资源在WEB-INF下
WEB-INF目录下文件访问资源文件时,可以忽略WEB-INF这一层目录.如index.jsp 要用css目录里的一个css文件.
<link rel="StyleSheet" href="../css/index.css" type="text/css" />这样就行了。
4. Tomcat项目Jar包问题
在使用Tomcat服务器进行开发的时候,如果需要使用第三方jar包,需要在WEB-INF下创建lib文件夹,将jar包复制进去,并右键add as library,否则可能在使用的时候出现类可以正常创建对象,但是使用报错情况。
[外链图片转存中…(img-xcbv9T0T-1652970980111)]
2. 乱码问题
Tomcat日志乱码
[(102条消息) Java-IDEA2020-IDEA或者启动Tomcat控制台中文乱码解决_gaogzhen的博客-CSDN博客_idea启动tomcat控制台乱码](https://blog.csdn.net/gaogzhen/article/details/107307459#:~:text=每次用新的 tomcat 和idea都会遇到 tomcat控制台中文乱码 问题,故在此整理。 基本上都是需要修改 tomcat,的 启动 参数(如果本身idea编码都统一设置为utf-8了)打开 tomcat 下的bin目录,找到catalina.bat文件找到 JAVA_OPTS 参数,在其中加上 -Dfile.encoding%3DUTF-8(用的7.0.82版本的设置为UTF-8,8.0.20版本的需要设置为GBK,)
URL解码
URLDecoder包含将 String 转换为 application/x-www-form-urlencoded MIME 格式的静态方法。
MIME 格式:定义的一种文件数据类型。格式:大类型/小类型 如:text/html image/jpeg ;
使用URLDecoder和URLEncoder对中文进行处理 - asflex - ITeye博客
请求响应
- 获取请求参数中文乱码
//1. 使用requset.getParameter("")
request.setCharacterEncoding("utf-8");System.out.println(requset.getParameter("username"));
System.out.println(requset.getParameter("password"));//2. 使用字符流乱码 需要使用URLDecoder解码
BufferedReader reader = req.getReader();
String line = null;
while ((line=reader.readLine())!=null){System.out.println(URLDecoder.decode(line,"utf-8"));
}
响应数据乱码
//方式一: resp.setContentType("text/html;charset=utf-8"); //方式二: //1.1获取流对象之前,把流的默认编码“ISO-8859-1”设置为:utf-8 resp.setCharacterEncoding("utf-8"); //1.2告诉浏览器,服务器发送的数据的编码,建议浏览器使用此编码 resp.setHeader("content-type","text/html;charset=utf-8");
控制台打印乱码
- idea设置编码格式全为utf-8
3. 路径问题汇总
/表示根路径
在前端页面,表示的是当前主机地址和端口号。即:localhost:8080
//jsp页面中发送请求,需要添加/servlet虚拟路径 <a href="/servlet/blog/add">添加blog</a>
在servlet服务中,表示的是当前服务器的虚拟路径。即localhost: 8080/servlet/
//servlet服务中进行转发,不需要添加/servlet虚拟路径 request.getRequestDispatcher("/hello.jsp").forward(request,response);
虚拟路径什么时候使用
- 浏览器使用:如:超链接,表单提交,发送请求时,需要添加虚拟路径。
- 服务器使用:如:转发,不需要添加虚拟路径。
4. 设置basepath路径
设置basepath:https://blog.csdn.net/weixin_33800593/article/details/92942073
Servlet应用——JavaWeb教案(四)相关推荐
- JavaWeb --第四章Maven详解
JavaWeb --第四章Maven详解 文章目录 Maven Maven架构管理工具 下载安装Maven 配置环境变量 阿里云镜像 本地仓库 在IDEA中使用Maven 创建一个普通的Maven项目 ...
- 做计算机的小卫士教案,小学信息技术教案四年级上环保小卫士
<小学信息技术教案四年级上环保小卫士>由会员分享,可在线阅读,更多相关<小学信息技术教案四年级上环保小卫士(2页珍藏版)>请在人人文库网上搜索. 1.小学信息技术教案第 周 第 ...
- 计算机ata考试教案,ATA办公软件考级教案(四).docx
ATA办公软件考级教案(四) 创业学校ATA办公考级教案案例名称:搜集排版与工具应用的技巧(四)科目:ATA教学对象:C1301 1302课时:2备课人:王翔一.教材内容分析文章的排版离不开字符的设置 ...
- 小班关于计算机运用的教案,实用的小班教案四篇
实用的小班教案四篇 作为一位杰出的老师,就不得不需要编写教案,借助教案可以提高教学质量,收到预期的教学效果.那要怎么写好教案呢?下面是小编收集整理的小班教案4篇,欢迎阅读,希望大家能够喜欢. 小班教案 ...
- 未来的计算机 教案,精选计算机教案四篇
精选计算机教案四篇 作为一名老师,通常会被要求编写教案,教案是教学蓝图,可以有效提高教学效率.那么教案应该怎么写才合适呢?下面是小编为大家收集的计算机教案4篇,欢迎大家借鉴与参考,希望对大家有所帮助. ...
- 小学计算机教案四年级上册,重大版小学信息技术教案四年级上册教案.doc
重大版小学信息技术教案四年级上册教案 小学信息技术四年级上册 教 案 学科: 小学信息技术 年级: 四年级 上册 姓名: XXXX 时间: XXXXXXXXXX 学期教学计划 教材分析本册教材为重大出 ...
- Servlet之javaweb应用(二)
一. 使用 JavaEE 版的 Eclipse 开发动态的 WEB 工程(JavaWEB 项目) 1). 把开发选项切换到 JavaEE 2). 可以在 Window -> Show View ...
- 文明使用计算机 教案,四年级信息技术下册 文明在我身边教案 华中师大版
<四年级信息技术下册 文明在我身边教案 华中师大版>由会员分享,可在线阅读,更多相关<四年级信息技术下册 文明在我身边教案 华中师大版(3页珍藏版)>请在人人文库网上搜索. 1 ...
- JavaWeb总结(四)—JSP深入解析
一.JSP域对象 1.JSP属性范围(域对象范围) JSP提供了四个域对象,分别是pageContext.request.session.application. pageContext: 属性范围仅 ...
最新文章
- 从不同视角筛选数据:可视化之前最关键的工作
- C# 学习笔记(19)操作SQL Server下
- mysql 硬盘空间不够_mysql磁盘空间不足的查排
- FIR数字滤波器设计频率抽样法MATLAB仿真
- 九大排序算法-C语言实现及详解
- ExtJs 备忘录(2)—— Form表单(二) [ 控件封装 ]
- 仿58网,赶集网,百姓网swfupload图片上传效果(asp.net 2.0)
- 【动画】当我们在读写Socket时,我们究竟在读写什么?
- angularJs service
- 小米3g刷高格固件_小米路由器3G版padavan老毛子原版固件
- 记录微信会员卡开卡跳过领取到卡包直接到激活页面
- Qt导航栏 QListWidget
- 如何正确获取支付宝网页支付的APPID、私钥、支付宝公钥
- 招行线上笔试java_今天参加了招行科技的在线笔试
- Joomla 插件 attachments 使用时出现不支持ZIP文件上传的错误
- 移动端怎么让底部固定_逆冬:移动端排名应该怎么做?两种匹配移动端实战排名干货分享!...
- CF888G - Xor-MST(顺带学习Borůvka算法)
- Kotlin 只读变量
- 旅游商城会员中心HTML,旅游特产商城功能_旅游特产商城系统-思途旅游CMS
- 近十年计算机病毒侵害柱形图,全国2002年10月自学考试计算机应用基础真题