Java16-Cookie&Session-JSP-EL&JSTL
1.客户端会话技术Cookie 会话技术(将来把数据存储到客户端的)
会话技术cookie是一门非常重要的技术,在我们进行javaee的服务器开发中占用较大篇幅。一次会话(浏览器访问服务器时,第一次发送请求-会话建立,直到一方断开为止)中,包含多次请求和响应(其实就是共享数据)。由于http是无状态协议-每一次请求响应之间和其他请求响应之间并无直接关联。他们做不了数据的交流交换。
例如在网站 购买三个东西,每次购买东西添加购物车,那么最后一次点击购物车结算时(这次请求也是独立),如何有那三个东西的数据。
共享数据在java中有两种会话技术:
- 客户端会话技术:Cookie(把数据存储到客户端)
- 服务器端会话技术:Session(把数据存储到服务器端)
1.2快速入门:
使用步骤:涉及到api,有多次请求和共享,因此需要两个servlet
//1.创建cookie对象(api类对象,不用request创建,new创建),绑定数据
new Cookie(String name,String value)//name是cookie名字,value是值;
//2.发送cookie对象(前一步已绑定数据,通过响应发送)
response.addCookie(Cookie cookie)
//3.获取cookie,拿到数据(新请求的请求头包含了cookie,另外servlet的request来获取cookie)
Cookie[] request.getCookies()
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.创建cookie对象 Cookie cookie = new Cookie("msg","hello"); //2.发送cookie对象 resp.addCookie(cookie); } }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies();//内部是map格式存储数据 for (Cookie cookie : cookies) { String name = cookie.getName(); String value = cookie.getValue(); System.out.println("name:"+name+"+++"+"value"+value); //name:msg+++valuehello //name:Idea-8296e770+++valuea75f694d-75d4-49c5-8c69-2a6381dfa7e4 } }
实现原理:
总结一句话:Cookie实现原理是基于响应头set-cookie和请求头cookie实现。
1.3 Cookie的细节
- 一次可不可以发送多个cookie? 可以,多次创建多次发送即可
不是只有访问demo2采用cookie,再次访问demo1也是有cookie
- cookie在浏览器中保存多长时间?
默认情况下:浏览器关闭后,浏览器内存中释放,则cookie就没有了
持久化存储:cookie对象中有个方法 setMaxAge(int seconds),传递数字:
正数:将cookie数据写到硬盘的文件中,是存货时间,超时则清空
0:删除cookie信息,意味着不保存在内存或服务器中。
负数:默认值,存放在浏览器内存中,则一关就释放
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.创建cookie对象 Cookie cookie = new Cookie("msg","hello"); Cookie cookie2 = new Cookie("msg2","hellotwo"); cookie2.setMaxAge(30); //2.发送cookie对象 resp.addCookie(cookie); resp.addCookie(cookie2); }
- cookie能不能存中文?
在tomcat8之前,cookie中不能直接存储中文数据;需要将中文数据转码,一般采用URL编码(URL编码是%数字%),有多少个字节,有多少个百分号表示。
在tomcat8之后,cookie中可以存储中文数据;
- cookie获取范围多大?
获取范围:在一个服务器tomcat中,部署了多个web 项目,那么这些项目之间的tomcat是否可以共享呢?默认情况下,cookie是不能共享,但是在cookie对象中有个方法:setPath(String path):cookie的共享是否,由path决定,设置cookie的获取范围,一般不设置,默认为当前项目的虚拟目录。
例如:cookie.setPath("/"):意味着当前服务器根目录下所有项目都可以访问。
不同tomcat服务器之间的cookie共享问题?
比方说news.baidu.com 和 tieba.baidu.com前面是二级域名,后面是一级域名,那么作为百度它肯定是cookie支持共享,如何做到呢,cookie对象提供了一个方法:setDomain( String path):设置一级域名,只要一级域名相同,那么多个服务器之间cookie可以共享。
例如: cookie.setDomain(".baidu.com"):那么baidu.com下面的二级域名就都可以使用。未来电商项目的域名映射,需要用到点.操作
1.4 cookie对象的特点和作用
特点1:cookie存储数据在客户端:不安全,容易丢失和拦截;Cookie 把所有要保存的数据通过 HTTP 协议的头部从客户端传递到服务端,又从服务端再传回到客户端,所有的数据都存储在客户端的浏览器里,所以这些 Cookie 数据可以被访问到,如果cookie被人拦截了,那人就可以取得所有的信息。即使加密也与事无补,因为拦截者并不需要知道cookie的意义,他只要原样转发cookie就可以达到目的了
特点2:浏览器对单个cookie的大小有限制(4kb以内)以及同一个域名的总cookie数量也有限制(20个以内)Cookie数量和长度的限制:每个域的cookie总数是有限的,IE6或更低版本最多20个cookie;IE7和之后的版本最后可以有50个;Firefox最多50个;chrome和Safari没有做硬性限制。cookie的长度也有限制,最好将cookie控制在4095B以内。否则会被截掉
作用1:cookie一般用于存储少量,不太敏感的数据,例如在用户没登录情况下,完成服务器对客户端的身份识别()
1.5 cookie案例分析--
需求:记住上一次访问的时间,如果访问一个servlet 如果是第一次访问,就说欢迎首次访问如果响应不是第一次访问,则提示;你上次响应时间为:显示时间字符串。
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1,获取所有cookie resp.setContentType("text/html;charset=utf-8"); Cookie[] cookies = req.getCookies(); boolean flag=false; resp.setContentType("text/html;charset='utf-8':"); if (cookies != null && cookies.length > 0) { //2.遍历cookie的数组 for (Cookie cookie : cookies) { String name = cookie.getName(); if ("lasttime".equals(name)){ flag=true; //1.有该cookie,不是第一次访问 //2. 设置cookie的value //3.获取当前时间字符串,重新设置cookie值,重新发送cookie Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String str_date = simpleDateFormat.format(date); String str_date2 = URLEncoder.encode(str_date, "utf-8"); cookie.setValue(str_date2); //4. 设置cookie的存活时间 cookie.setMaxAge(60*60*24*30); resp.addCookie(cookie); //4. 获取cookie的value值 String value = cookie.getValue(); value = URLDecoder.decode(value, "utf-8"); resp.getWriter().write("<h1>欢迎回来,您上次访问时间为</h1>"+value); break; } } } if(cookies==null||cookies.length==0|| flag==false){ // 没有 第一次访问 Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String new_date = simpleDateFormat.format(date); new_date = URLEncoder.encode(new_date, "utf-8"); Cookie cookie = new Cookie("lasttime",new_date); resp.addCookie(cookie); resp.getWriter().write("<h1>欢迎你的到来,现在时间是</h1>"+new_date); } }
2.JSP(java的动态资源有servlet和jsp)-java服务器端页面-特殊页面,既可以定义html标签,也可以写jiava代码--目的是简化书写
为什么简化代码了呢,因为需要后端动态获取时候,给予jsp简化了后端代码输出。浏览器访问jsp资源时,请求到后端服务器,服务器解析请求消息,找到是否有index.jsp资源,如果没有就报错,如果有就会将jsp转换为.java文件,但此时浏览器还是识别不了,jvm会将java文件生成编译为.class字节码文件,最终由.class字节码文件提供访问,因此我们得出一个结论,这个.class文件其实是编译后的servlet文件。
也就是说,jsp本质上是一个servlet
它里面有service方法
2.2.JSP脚本
jsp脚本意思是定义声明java代码的方式,脚本有三种形式:
方式1:<%java代码%>:定义的方法在service中,service可以定义哪些,那么该脚本就可以定义哪些。
方法2:<%! java代码%>:定义的成员变量,在jsp转换的java类中,与service方法并行。
方法3:<%=java代码%>:定义的代码会输出到页面上,输出语句中定义什么,他就可以定义什么
2.3JSP内置对象
JSP提供了由容器实现和管理的内置对象,也可以称之为隐含对象,由于JSP使用Java作为脚本语言,所以JSP将具有强大的对象处理能力,并且可以动态创建Web页面内容。但Java语法在使用一个对象前,需要先实例化这个对象,这其实是一件比较烦琐的事情。JSP为了简化开发,提供了一些内置对象,用来实现很多JSP应用。在使用JSP内置对象时,不需要先定义这些对象,直接使用即可。在JSP中一共预先定义了9个这样的对象,分别为request、response、session、application、out、pageContext、config、page和exception。
JSP包括很多技术,包括Java Bean、自定义标签(Custom Tags)、EL表达式(Expression Language)、JSTL标准标签类库(Java Standard Tag Library)等。这些强大成熟的技术使得JSP在视图层(View)有很大的优势。
JSP内置对象包括request对象、response对象、session对象、out对象、application对象、page对象、exception对象、pageContext对象、config对象。这些对象在客户端和服务器端交互的过程中分别完成不同的功能。为什么内置对象可以直接使用呢,因为这些内置对象最终会放在javaservlet对象的service方法中执行。而service方法中,只要对这些对象有声明,即可使用。例如我们的代码都会在out之后。下图是转换后的_jspService方法中信息。
2.3.1.Request对象
Request对象是javax.servlet.http.HttpServletRequest类的实例。代表请求对象,主要用于接受客户端通过HTTP协议连接传输到服务器端的数据。比如表单中的数据、网页地址后带的参数等。
2.3.2.Response对象
Response对象是javax.servlet.http.HttpServletResponse类的实例。代表响应对象,主要用于向客户端发送数据。它会先于out.write()方法先输出,即便是定义在out.write之后。
2.3.3.Out对象
Out对象是javax.servlet.jsp.JspWriter类的实例。主要用于向客户端浏览器输出数据。一般建议out对象,不用response
2.3.4.session对象
Session 对象是javax.servlet.http.HttpSession类的实例。主要用来保持在服务器与一个客户端之间需要保留的数据,比如在会话期间保持用户的登录信息等,会话状态维持是Web应用开发者必须面对的问题。当客户端关闭网站的所有网页或关闭浏览器时,session对象中保存的数据会自动清除。由于Htp协议是一个无状态协议,不保留会话间的数据,因此通过session对象扩展了htp的功能。比如用户登录一个网站之后,登录信息会暂时保存在session对象中,打开不同的页面时,登录信息是可以共享的,一旦用户关闭浏览器或退出登录,就会清除session对象中保存的登录信息。
2.3.5.Application对象
Application对象是javax.servlet.ServletContext类的实例。主要用于保存用户信息,代码片段的运行环境;它是一个共享的内置对象,即一个容器中的多个用户共享一个application对象,故其保存的信息被所有用户所共享。
2.3.6.PageContext对象
PageContext对象是javax.servlet.jsp.PageContext类的实例。用来管理网页属性,为JSP页面包装页面的上下文,管理对属于JSP中特殊可见部分中已命名对象的访问,它的创建和初始化都是由JSP容器来完成的。
2.3.7.Config对象
Config对象是javax.servlet.ServletConfig类的实例。是代码片段配置对象,表示Servlet的配置。
2.3.8.Page(相当于this)对象
Page对象是javax.servlet.jsp.HttpJspPage类的实例。用来处理JSP网页,它指的是JSP页面对象本身,或者说代表编译后的servlet对象,只有在JSP页面范围之内才是合法的。
2.3.9.Exception对象
Exception对象是java.lang.Throwable类的实例。处理JSP文件执行时发生的错误和异常只有在JSP页面的page指令中指定isErrorPage=“true”后,才可以在本页面使用exception对象。
需要说明的是,pageContext中的属性默认在当前页面是共享的;session中的属性在当前session中是共享的;application对象中的属性则对所有页面都是共享的。
2.4. jsp特点
JSP的内置对象主要有以下特点:
1、由JSP规范提供,不用编写者实例化;
2、通过Web容器实现和管理;
3、所有JSP页面均可使用;
4、只有在脚本元素的表达式或代码段中才可使用(<%=使用内置对象%>或<%使用内置对象%>)
2.5 jsp分类
按照内置对象的功能来划分,可以分为以下四类:
1、输出输入对象:request对象、response对象、out对象;
2、通信控制对象:pageContext对象、session对象、application对象;
3、Servlet对象:page对象、config对象;
4、错误处理对象:exception对象。
2.6jsp作用域
(1)application范围:作用范围起始于服务器开始运行,application对象被创建之时;终止于服务器关闭之时。
(2)session范围:有效范围是整个用户会话的生命周期内。每个用户请求访问服务器时一般就会创建一个session对象,用户断开退出时session对象失效。服务器对session对象有默认的时间限定,如果超过该时间限制,session会自动失效,而不管用户是否已经终止连接,这主要是出于安全性的考虑。
(3)request范围:在一个JSP页面向另一个JSP页面提出请求到请求完成之间,在完成请求后此范围即结束。
(4)page 范围:有效范围是当前页面。
<%@ page import="java.net.URLDecoder" %> <%@ page import="java.util.Date" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page import="java.net.URLEncoder" %> <%@ page contentType="text/html;charset=UTF-8" language="java"%> <html> <head> <title>heihei</title> </head> <body> <% Cookie[] cookies = request.getCookies(); boolean flag=false; if (cookies != null && cookies.length > 0) { //2.遍历cookie的数组 for (Cookie cookie : cookies) { String name = cookie.getName(); if ("lasttime".equals(name)){ //4. 获取cookie的value值 String value = cookie.getValue(); value = URLDecoder.decode(value, "utf-8"); response.getWriter().write("<h1>欢迎回来,您上次访问时间为</h1>"+value); flag=true; //1.有该cookie,不是第一次访问 //2. 设置cookie的value //3.获取当前时间字符串,重新设置cookie值,重新发送cookie Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String str_date = simpleDateFormat.format(date); String str_date2 = URLEncoder.encode(str_date, "utf-8"); cookie.setValue(str_date2); //4. 设置cookie的存活时间 cookie.setMaxAge(60*60*24*30); response.addCookie(cookie); break; } } } if(cookies==null||cookies.length==0|| flag==false){ // 没有 第一次访问 Date date = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String new_date = simpleDateFormat.format(date); new_date = URLEncoder.encode(new_date, "utf-8"); Cookie cookie = new Cookie("lasttime",new_date); response.addCookie(cookie); response.getWriter().write("<h1>欢迎你的到来,现在时间是</h1>"+new_date); } %> </body> </html>
2.2JSP深入学习的指令、注释和内置对象
指令:用于配置jsp页面,导入资源文件
- 格式:<%@ 指令名称 属性键=值对%>
<%@ page contentType="text/html;charset=UTF-8" language="java"
buffer=“8kb”%> buffer是缓存区大小
指令名称:
- page :配置jsp页面的
- include :页面包含的,导入页面的资源文件
- taglib:导入资源,例如标签库
其中page指令包含的属性常见的有
- contentType:等同于response.setContentType();设置响应体的解析类型,不过它contentType可以设置响应体的mime类型和字符集,另外还可以设置当前jsp页面的编码,当然低级工具中最好用pageEncoding=“GBK”来共同约束,否则只有高级开发工具才能识别并应用到当前jsp页面的编码。
- buffer=“16kb”,out是输出流,一般建议其大小为大一点,否则输出流限制就麻烦了
- language=“java”
- import:导包 <%page import="java.util.List"%>
- errorPage:当前页面发生异常后,会自动跳转到指定的错误页面
- isErrorPage:标识当前页面是否也是错误页面。
当isErrorPage=“true”,就可以使用exception对象,获取错误内容,进而得到错误原因,也就可以放入日志文件。
其中include指令包含的属性常见的有:其实include就是解决那些重复页面,比方说页面头部信息,基本一样,就可以用它。
file属性:<% include file=“top.jsp”%>
其中include指令包含的属性常见的有:
- prefix="",前缀,也就是别名
- uri=""路径,参考全面的,必须引入lib后才行
JSP注释:
- html<!-- --> 只能注释html,不显示,但页面的html还能看见
- jsp<%-- --%>可以注释所有,且页面html直接跳过看不见
2.2 jsp内置对象总结
对象名 真实类型 作用
* pageContext PageContext 仅当前页面共享数据,获取其他内置对象
* request HttpServletRequest 一次请求访问的多个资源(转发)
* session HttpSession 一次会话的多个请求间
* application ServletContext 唯一,所有用户间共享数据
* response HttpServletResponse 响应对象
* page Object 当前页面(Servlet)的对象 this引用
* out JspWriter 输出对象,数据输出到页面上
* config ServletConfig Servlet的配置对象
* exception Throwable(父类) 异常对象
2.3. MVC开发模式-代码书写规范
MVC开发模式类型:
M:Model,模型---JavaBean充当
* 完成具体的业务操作,如:查询数据库,封装对象
V:View,视图------JSP充当
* 展示数据
C:Controller,控制器-----Servlet充当
* 获取用户的输入(请求参数)
* 调用模型
* 将数据交给视图进行展示(域对象共享数据)
* 优缺点:
1. 优点:
1. 耦合性低,方便维护,可以利于分工协作
2. 重用性高
2. 缺点:
1. 使得项目架构变得复杂,对开发人员要求高
三层架构:软件设计架构,M(模型)V(视图)C(控制)是开发架构模式
1. 界面层(表示层):用户看的得界面。用户可以通过界面上的组件和服务器进行交互
2. 业务逻辑层:处理业务逻辑的。
3. 数据访问层:操作数据存储文件。
2.4 EL表达式 替换jsp中java代码的表达式技术,后面减少在jsp中写java代码。
1. 概念:Expression Language 表达式语言
2. 作用:替换和简化jsp页面中java代码的编写
3. 语法:${表达式}
4. 注意:
* jsp默认支持el表达式的。如果要忽略el表达式,要么 设置jsp中page指令中:isELIgnored="true" 忽略当前jsp页面中所有的el表达式;或者要么 \${表达式} :忽略当前这个el表达式。
5. 表达式使用语法:
1. 运算:
1. 算数运算符: + - * /(div也可) 除法 %(mod也可) 取余
2. 比较运算符: > < >= <= == !=
3. 逻辑运算符: &&(and也可) ||(or 也可) !(not 也可)
4. 空运算符: empty:用于判断字符串、集合、数组对象是否为null或者长度是否为0
* ${empty list}:判断字符串、集合、数组对象是否为null或者长度为0
* ${not empty str}:表示判断字符串、集合、数组对象null且长度>0
2. 获取值
1. el表达式只能从域对象中获取值
2. 语法两种取值方式:
1. ${域名称.键名}:从指定域中获取指定键的值
* 域名称:
1. pageScope 对应 --> pageContext域对象
2. requestScope 对应--> request域对象
3. sessionScope 对应--> session域对象
4. applicationScope 对应--> application(ServletContext)
* 举例:在request域中存储了name=张三
* 获取:${requestScope.name}
没有显示空,不是null,不打乱布局,例如我们修改之前长串代码
![image image](https://img2020.cnblogs.com/blog/1447473/202101/1447473-20210121163454294-152421281.png)
2. ${键名}:依次从最小的域中查找是否有该键对应的值,直到找到。
3. 获取对象、List集合、Map集合的值
1. 对象:${域名称.键名.属性名}取值
* 本质上会去调用对象的getter方法
2. List集合:${域名称.键名[索引]}
3. Map集合:
* ${域名称.键名.key名称}
* ${域名称.键名["key名称"]}
map中键是一种引用。
3. 隐式对象:
* el表达式中有11个隐式对象,类似jsp里面内置对象,有差别
* pageContext:
* 获取jsp其他八个内置对象
* ${pageContext.request.contextPath}:动态获取虚拟目录,获取它是把get去掉,前面小写即可,如getRequest----request
2.5 JSTL标签 替换jsp中java代码的表达式技术
1. 概念:JavaServer Pages Tag Library JSP标准标签库
* 是由Apache组织提供的开源的免费的jsp标签 <标签>
2. 作用:用于简化和替换jsp页面上的java代码
3. 使用步骤:
1. 导入jstl相关jar包
2. 引入标签库:taglib指令: <%@ taglib %>
3. 使用标签
4. 常用的JSTL标签
1. if标签:相当于java代码的if语句
1. 属性:
* test 必须属性,接受boolean表达式
* 如果表达式为true,则显示if标签体内容,如果为false,则不显示标签体内容
* 一般情况下,test属性值会结合el表达式一起使用
2. 注意:
* c:if标签没有else情况,想要else情况,则可以再定义一个c:if标签
2. choose标签:相当于java代码的switch语句
1. 使用choose标签声明 相当于switch声明
2. 使用when标签做判断 相当于case
3. 使用otherwise标签做其他情况的声明 相当于default
3. foreach标签:相当于java代码的for语句,1遍历容器,2重复操作
varStatus表示循环状态对象,它对象属性index代表序号,count代表循环次数,其中s是别名,varStatus=“s”
5. 练习:
* 需求:在request域中有一个存有User对象的List集合。需要使用jstl+el将list集合数据展示到jsp页面的表格table中
隔行变色
2.6 三层架构
三层架构:软件设计架构思想1. 界面层(表示层)SpringMvc:用户看的得界面。可以通过界面上的组件和服务器进行交互,将数据提交,提交数据也是界面层组件工作,向下给业务逻辑层。
2. 业务逻辑层Spring:处理业务逻辑的。最复杂。具体向下是数据层。
3. 数据访问层Mybatis:操作数据存储文件。
案例:用户信息的增删改查操作
分析:servlet jsp mysql jdbc duird beanutils tomcat
步骤:环境搭建(建库、导jar包)、编码-测试-部署
展望:未来前端完成静态页面后,由开发者完成jsp输出
起步:本地启动,看看静态页面跳转和显示有无问题,部分返回或增加删除按钮可能需要jsp协助完成。
编码分析:
使用左父右子的好处是,一旦代码逻辑有问题,直接改实现类。
编写步骤:
1.domain-编写User实体类-javabean
2.web-index.html内容复制进index.jsp,同时进行页面跳转跳转:
<div align="center"> <a href="list.html" style="text-decoration:none;font-size:33px">查询所有用户信息 </a> </div> <div align="center"> <a href="${pageContext.request.contextPath}/userListServlet" style="text-decoration:none;font-size:33px">查询所有用户信息 </a> </div>
3-4-5-6实体类编写
<div class="container"> <h3 style="text-align: center">用户信息列表</h3> <table border="1" class="table table-bordered table-hover"> <tr class="success"> <th>编号</th> <th>姓名</th> <th>性别</th> <th>年龄</th> <th>籍贯</th> <th>QQ</th> <th>邮箱</th> <th>操作</th> </tr> <c:forEach items="${userList}" var="user" varStatus="s"> <tr> <td>${s.count}</td> <td>${user.name}</td> <td>${user.gender}</td> <td>${user.age}</td> <td>${user.address}</td> <td>${user.qq}</td> <td>${user.email}</td> <td><a class="btn btn-default btn-sm" href="update.html">修改</a> <a class="btn btn-default btn-sm" href="">删除</a></td> </tr> </c:forEach> <tr> <td colspan="8" align="center"><a class="btn btn-primary" href="add.html">添加联系人</a></td> </tr> </table> </div>
@WebServlet("/userListServlet") public class UserListServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //调用UserService查询 UserService userService = new UserServiceImpl(); //调用findAll方法 List<User> userList = userService.findAll(); //将userList存入request域中 request.setAttribute("userList",userList); //转发到list.jsp request.getRequestDispatcher("/list.jsp").forward(request,response); } /** * 用户管理的业务接口 */ public interface UserService { /** * 查询所有用户信息 * @return */ public List<User> findAll(); } public class UserServiceImpl implements UserService { private UserDao dao= new UserDaoImpl(); /** * 业务逻辑是调用dao层来执行查询 * @return */ @Override public List<User> findAll() { return dao.findAll(); } } /** * javaBean 对应着数据库的字段 * */ public class User { private int id; private String name; private String gender; private int age; private String address; private String qq; private String email; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getQq() { return qq; } public void setQq(String qq) { this.qq = qq; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", gender='" + gender + '\'' + ", age=" + age + ", address='" + address + '\'' + ", qq='" + qq + '\'' + ", email='" + email + '\'' + '}'; } } /** * 用户操作的dao */ public interface UserDao { public List<User> findAll(); } public class UserDaoImpl implements UserDao { private JdbcTemplate template= new JdbcTemplate(JDBCUtils.getDataSource()); @Override public List<User> findAll() { /** * 使用jdbc操作数据库 */ //定义sql String sql= "select * from user"; //执行sql List<User> users = template.query(sql, new BeanPropertyRowMapper<User>(User.class)); return users; } }
登录功能 -调整页面,加入验证码功能
BeanUtils.populate( Object bean, Map properties ),
这个populate方法会遍历map<key, value>中的key,如果bean对象user中有这个属性,就把这个map的key对应的value值赋给bean的属性的值。
3.服务器端会话技术Session(把数据存储到服务器端)
3.1.基本概念
服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中(也就是httpSeesion中)
其实request可以共享数据,servletContext可以共享数据,session也可以共享数据,都是域对象。也有getAttribute(String name)和setAttribute()和removeAttribute方法
3.2.1.获取session对象:request对象.getSession();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //使用session共享数据 //1.获取session HttpSession session = request.getSession(); session.setAttribute("msg","hello"); } 没有跳转 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取数据 //1.获取session对象 HttpSession session = request.getSession(); //2.获取数据 Object msg = session.getAttribute("msg"); System.out.println(msg); }
3.2.3细节:
细节1:当客户端关闭,服务器不关闭,两次获取session是同一个么?
默认情况下不是,当客户端关闭,cookie头已经消失,自然不是一个;
new Cookie+并设置键JSESSIONID和生命周期长点可解决:
细节2:客户端不关闭,服务器端关闭,两次获取session是同一个么?
不是,因为session地址是服务器分配,服务器关闭则重新分配
但我们需要数据不丢失,例如我们网上买个东西,服务器突然关闭,那怎么办。此处需要一个概念:session的钝化和活化:
session钝化:在服务器正常关闭前,将session对象序列化到硬盘上,这是一个序列化过程,tomcat已经帮我们做了。
session活化:在服务器启动后,将session文件转化为内存中的session对象.这是一个反序列化过程。tomcat已经帮我们做了。
但我们idea可以完成钝化过程,但无法完成这个反序列化-活化过程,我们需要本地tomcat,因为idea重新编译时,会导致work清空,但不影响我们部署。
细节3:session的失效时间?
失效时间1:服务器关闭时候,销毁
失效时间2:session对象调用invalidate().自我销毁。
失效时间3:session对象默认30分钟,session-config
3.3:session特点:
特点1:session用于存储一次会话的多次请求的数据,存在服务器端,例如我们使用重定向,重定向是两次请求,共享数据,我们可以用session,而不用application-context(范围太大)
特点2:session可以存储任意类型,任意大小数据setAtttruibute(String name,Object value)
区别:
在ASP.NET WEB页面开发中,经常会需要保存一些信息,以便在不同的页面或时间段都能够访问到。这其中便会用到Session和Application。
Session 、Application 和 HttpContext 都是在服务器上开辟了一个内存空间,将一些信息存储到该内存空间中。
Session :Session 会对每一个客户端(浏览器)在服务器端划分一个区域,该区域用于存储该客户端的一些信息,每一个客户端对应服务器端唯一的一个区域空间;Session默认的生存周期为20分钟,当Session超出20分钟或用户主动删除Session,则Session被销毁。
Cookie:存储在客户端,有数据大小限制。
Application :Application 对于每一个WEB网站在服务器端划分一个区域,但对于同一个WEB网站而言,Application 是共享的,也就是说,每一个客户端(浏览器)都可以访问同一个Application值;Application的生存周期为当前WEB网站启动直至关闭。
HttpContext :HttpContext 有一个 Items属性的键值对,可以使用 HttpContext.Items["name"] = "Tom" 的方式来存储值。但HttpContext只对当前请求的响应管道内(生命周期)有效,一旦当前管道关闭或者被销毁,则HttpContext.Items的值也随之被销毁。(所以若使用HttpContext存储值,那么使用Response.Redirect("页面地址")的方式会使得页面周期被强行关闭而导致HttpContext被销毁;但是使用Server.Transfer("页面地址") 的方式并不会使管道被关闭,所以HttpContext依旧会被保存下来。)
3.4 案例:
访问带有验证码的登录页面login.jsp
用户输入用户名、密码和验证码
- 如果用户名和密码有错误,跳转登录页面,提示用户名或密码错误
- 如果验证码有错误,跳转登录页面,提示验证码错误
- 如果全部输入正确,则跳转到主页success.jsp,显示用户名,欢迎您。
这里面需要注意的是,验证码的刷新和显示其实是一个servlet,最后一起提交是一个servlet请求,我们需要将生成 的验证码存储到一个session中,这样我们后端获取其session与用户输入的验证码进行比对。
@WebServlet("/loginServlet") public class loginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1 设置request编码 req.setCharacterEncoding("utf-8"); //2. 获取参数map String username = req.getParameter("username"); String password = req.getParameter("password"); String checkCode = req.getParameter("checkCode"); //3.先判断验证码 //3.1 先获取此前生成的验证码 HttpSession session = req.getSession(); String checkCode_session = (String) session.getAttribute("checkCode_session"); // 销毁验证码 session.removeAttribute("checkCode_session"); //3.2 验证码比较 if(checkCode_session!=null && checkCode_session.equalsIgnoreCase(checkCode)){ // 忽略大小写比较字符串,比较是否一样,一样 if ("zhangsan".equals(username)&&"root".equals(password)){ //登录成功 //存储信息,用户信息 session.setAttribute("user",username); // 重定向到success.jsp resp.sendRedirect(req.getContextPath()+"/success.jsp"); }else { //登录失败 req.setAttribute("login_error","用户名或密码错误"); // 转发登录页面 req.getRequestDispatcher("/login.jsp").forward(req,resp); } }else { // 忽略大小写比较字符串,比较是否一样,不一样 req.setAttribute("checkCode_error","验证码错误"); // 转发登录页面 req.getRequestDispatcher("/login.jsp").forward(req,resp); } } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1><%=request.getSession().getAttribute("user")%>,欢迎您</h1> </body> </html>
<html> <head> <title>login</title> <script> window.onload=function () { var elementById = document.getElementById("img"); elementById.onclick=function () { this.src="/day13_tomcat/CheckCodeServlet?time="+new Date().getTime(); } } </script> <style> div{ color: red; } </style> </head> <body> <form action="/day13_tomcat/loginServlet" method="POST"> <table> <tr> <td>用户名</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码</td> <td><input type="text" name="password"></td> </tr> <tr> <td>验证码</td> <td><input type="text" name="checkCode"></td> </tr> <tr> <td colspan="2"><img id="img" src="/day13_tomcat/CheckCodeServlet"></td> </tr> <tr> <td colspan="2"><input type="submit" value="登录"></td> </tr> </table> </form> <div><%=request.getAttribute("checkCode_error") ==null ? "": request.getAttribute("checkCode_error")%></div> <div><%=request.getAttribute("login_error")==null? "" :request.getAttribute("login_error")==null%></div> </body> </html>
@WebServlet("/CheckCodeServlet") public class CheckCodeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int width=100; int height=30; //1.创建图片对象,在内存中的图片 BufferedImage img = new BufferedImage(width,height, BufferedImage.TYPE_INT_RGB); //2.1美化图片-填充背景色 Graphics graphics = img.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="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; Random random=new Random(); //创建随机角标,并存储 StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < 4; i++) { int index = random.nextInt(str.length()); char c = str.charAt(index); stringBuilder.append(c); graphics.drawString(c+"",width/5*i,height/2); } String checkCode_session = stringBuilder.toString(); HttpSession session = req.getSession(); session.setAttribute("checkCode_session",checkCode_session); //4.设置划线 graphics.setColor(Color.green); for (int i = 0; i < 10; i++) { int x1 = random.nextInt(width); int x2 = random.nextInt(width); int y1 = random.nextInt(height); int y2 = random.nextInt(height); graphics.drawLine(x1,y1,x2,y2); } //3. 将图片传到页面中,由于此时我们的图片是在内存中,我们需要用对象将其传送,write需要图片对象,图片后缀名,响应对象输出流 ImageIO.write(img,"jpg",resp.getOutputStream()); } }