Java16-Cookie&Session-JSP-EL&JSTL

1.客户端会话技术Cookie 会话技术(将来把数据存储到客户端的)

       会话技术cookie是一门非常重要的技术,在我们进行javaee的服务器开发中占用较大篇幅。一次会话(浏览器访问服务器时,第一次发送请求-会话建立,直到一方断开为止)中,包含多次请求和响应(其实就是共享数据)。由于http是无状态协议-每一次请求响应之间和其他请求响应之间并无直接关联。他们做不了数据的交流交换。

      例如在网站 购买三个东西,每次购买东西添加购物车,那么最后一次点击购物车结算时(这次请求也是独立),如何有那三个东西的数据。

共享数据在java中有两种会话技术:

  • 客户端会话技术:Cookie(把数据存储到客户端)

image

  • 服务器端会话技术:Session(把数据存储到服务器端)


1.2快速入门:

使用步骤:涉及到api,有多次请求和共享,因此需要两个servlet

image

image

image

//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
        }
    }

实现原理:

image

1610867053(1)image

总结一句话:Cookie实现原理是基于响应头set-cookie和请求头cookie实现。

1.3 Cookie的细节

  • 一次可不可以发送多个cookie? 可以,多次创建多次发送即可


1610867587(1)image

image不是只有访问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代码--目的是简化书写

image

     为什么简化代码了呢,因为需要后端动态获取时候,给予jsp简化了后端代码输出。浏览器访问jsp资源时,请求到后端服务器,服务器解析请求消息,找到是否有index.jsp资源,如果没有就报错,如果有就会将jsp转换为.java文件,但此时浏览器还是识别不了,jvm会将java文件生成编译为.class字节码文件,最终由.class字节码文件提供访问,因此我们得出一个结论,这个.class文件其实是编译后的servlet文件。

1611050773(1)

也就是说,jsp本质上是一个servlet


image

它里面有service方法

image

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方法中信息。

image

2.3.1.Request对象

Request对象是javax.servlet.http.HttpServletRequest类的实例。代表请求对象,主要用于接受客户端通过HTTP协议连接传输到服务器端的数据。比如表单中的数据、网页地址后带的参数等。

2.3.2.Response对象

Response对象是javax.servlet.http.HttpServletResponse类的实例。代表响应对象,主要用于向客户端发送数据。它会先于out.write()方法先输出,即便是定义在out.write之后。

image

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:标识当前页面是否也是错误页面。

1611210650(1)

1611210626(1)

当isErrorPage=“true”,就可以使用exception对象,获取错误内容,进而得到错误原因,也就可以放入日志文件。


其中include指令包含的属性常见的有:其实include就是解决那些重复页面,比方说页面头部信息,基本一样,就可以用它。

file属性:<% include file=“top.jsp”%>

image

其中include指令包含的属性常见的有:

  • prefix="",前缀,也就是别名
  • uri=""路径,参考全面的,必须引入lib后才行

image

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开发模式-代码书写规范

image

MVC开发模式类型:
M:Model,模型---JavaBean充当
         * 完成具体的业务操作,如:查询数据库,封装对象
V:View,视图------JSP充当
         * 展示数据
C:Controller,控制器-----Servlet充当
         * 获取用户的输入(请求参数)
         * 调用模型
         * 将数据交给视图进行展示(域对象共享数据)

image


     * 优缺点:
         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表达式。

1611213388(1)image
5. 表达式使用语法:
1. 运算:
        1. 算数运算符: + - * /(div也可) 除法 %(mod也可)  取余
        2. 比较运算符: > < >= <= == !=
       3. 逻辑运算符: &&(and也可) ||(or 也可) !(not 也可)
        4. 空运算符: empty:用于判断字符串、集合、数组对象是否为null或者长度是否为0 
            * ${empty list}:判断字符串、集合、数组对象是否为null或者长度为0
           * ${not empty str}:表示判断字符串、集合、数组对象null且长度>0

1611214977(1)1611214992(1)


2. 获取值
    1. el表达式只能从域对象中获取值
    2. 语法两种取值方式:
         1. ${域名称.键名}:从指定域中获取指定键的值
                 * 域名称:
                     1. pageScope           对应 --> pageContext域对象
                     2. requestScope       对应--> request域对象
                     3. sessionScope        对应--> session域对象
                     4. applicationScope  对应--> application(ServletContext)
                 * 举例:在request域中存储了name=张三
                 * 获取:${requestScope.name}

1611215224(1)1611215194(1)

有显示空,不是null,不打乱布局,例如我们修改之前长串代码


image

         2. ${键名}:依次从最小的域中查找是否有该键对应的值,直到找到。

  3. 获取对象、List集合、Map集合的值

image
      1. 对象:${域名称.键名.属性名}取值
                     * 本质上会去调用对象的getter方法

image1611215224(1)1611215877(1)

   image


   2. List集合:${域名称.键名[索引]}

   3. Map集合:
                     * ${域名称.键名.key名称}
                     * ${域名称.键名["key名称"]}

1611216753(1)1611216769(1)image即便list里面是对象也可以。

1611217162(1)1611217169(1)

1611217269(1)image

map中键是一种引用。
  

3. 隐式对象:
         * el表达式中有11个隐式对象,类似jsp里面内置对象,有差别
         * pageContext:
             * 获取jsp其他八个内置对象
                 * ${pageContext.request.contextPath}:动态获取虚拟目录,获取它是把get去掉,前面小写即可,如getRequest----request
   image      

路径编写   image


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标签

image


         2. choose标签:相当于java代码的switch语句
             1. 使用choose标签声明                     相当于switch声明
             2. 使用when标签做判断                     相当于case
             3. 使用otherwise标签做其他情况的声明        相当于default

image

        3. foreach标签:相当于java代码的for语句,1遍历容器,2重复操作

imageimage

image

varStatus表示循环状态对象,它对象属性index代表序号,count代表循环次数,其中s是别名,varStatus=“s”

imageimage

image

    5. 练习:
         * 需求:在request域中有一个存有User对象的List集合。需要使用jstl+el将list集合数据展示到jsp页面的表格table中

imageimage

隔行变色

image


2.6 三层架构

三层架构:软件设计架构思想
    1. 界面层(表示层)SpringMvc:用户看的得界面。可以通过界面上的组件和服务器进行交互,将数据提交,提交数据也是界面层组件工作,向下给业务逻辑层。
    2. 业务逻辑层Spring:处理业务逻辑的。最复杂。具体向下是数据层。
    3. 数据访问层Mybatis:操作数据存储文件。


image

案例:用户信息的增删改查操作

分析:servlet jsp mysql jdbc duird beanutils tomcat

步骤:环境搭建(建库、导jar包)、编码-测试-部署

展望:未来前端完成静态页面后,由开发者完成jsp输出

起步:本地启动,看看静态页面跳转和显示有无问题,部分返回或增加删除按钮可能需要jsp协助完成。

编码分析:

使用左父右子的好处是,一旦代码逻辑有问题,直接改实现类。

1611278648(1)

编写步骤:

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>&nbsp;<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的属性的值。

image

image

image

image

image

image

image



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.2. 原理


image

3.2.3细节:

细节1:当客户端关闭,服务器不关闭,两次获取session是同一个么?

默认情况下不是,当客户端关闭,cookie头已经消失,自然不是一个;

new Cookie+并设置键JSESSIONID和生命周期长点可解决:

image


细节2:客户端不关闭,服务器端关闭,两次获取session是同一个么?

不是,因为session地址是服务器分配,服务器关闭则重新分配

1611192939(1)

但我们需要数据不丢失,例如我们网上买个东西,服务器突然关闭,那怎么办。此处需要一个概念:session的钝化和活化:

session钝化:在服务器正常关闭前,将session对象序列化到硬盘上,这是一个序列化过程,tomcat已经帮我们做了。

session活化:在服务器启动后,将session文件转化为内存中的session对象.这是一个反序列化过程。tomcat已经帮我们做了。

但我们idea可以完成钝化过程,但无法完成这个反序列化-活化过程,我们需要本地tomcat,因为idea重新编译时,会导致work清空,但不影响我们部署。


image


细节3:session的失效时间?

失效时间1:服务器关闭时候,销毁

失效时间2:session对象调用invalidate().自我销毁。

失效时间3:session对象默认30分钟,session-config

image


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与用户输入的验证码进行比对。

image

@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());
    }
}





posted @ 2021-01-14 19:46  芒果侠  阅读(144)  评论(0编辑  收藏  举报