week12_day06_Session

1.tomcat虚拟映射
tomcat部署应用:
1.直接部署:webapps目录下新建应用
2.虚拟映射:部署在其他位置,相当于映射到webapps目录下
Conf/catalina/localhost 应用名.xml文件。

1.1.虚拟目录映射
假如我不把项目放在D:\developmer_tools\apache-tomcat-7.0.78\webapps下,我就放在桌面,怎么让Tomcat帮我启动它呢?

Context: 具体的某个服务, 某个应用
所以按道理来讲,应该配置Context,配置完后一定要重启Tomcat。
1.1.1.第一种: conf下server.xml之中host节点下面
Path是虚拟目录名称,docBase是Web应用所在目录。

1.1.2.第二种: conf:
到目录:D:\developmer_tools\apache-tomcat-7.0.78\conf\Catalina\localhost下
创建一个xml, 给用户使用的应用名, 就是xml的文件名

在Tomcat6中,不再建议在server.xml文件中配置context元素,细节查看tomcat服务器关于context元素的说明。
让tomcat自动映射: tomcat服务器会自动管理webapps目录下的所有web应用,并把它映射成虚似目录。换句话说,tomcat服务器webapps目录中的web应用,外界可以直接访问。

课后练习:
之前部署在IDEA的tomcat中的项目,现在我需要你将这个项目部署在自己安装的tomcat中。(利用的就是上述的第二种方式)
如果遇到一些匪夷所思的bug,就用上述的方式试试。

Tomcat组成、架构
http://域名:端口号/应用名/资源
请求处理过程:connector > engine > host > Context > servlet、静态资源、filter、listener
大娃娃套小娃娃。
什么叫Context?webapps下面的目录叫Context,虚拟映射映射的xml的docBase指向的路径也是一个Context。

2.重难点
1.servlet(IDEA如何开启一个新的tomcat实例排除)、context
2.Request:获取请求参数、中文乱码、request域
3.Response:输出字符数据、字节数据、中文乱码、页面跳转
4.FileUpload:引入组件之前的那些操作(获取请求参数API不再适用),中文乱码、文件名乱码
5.Cookie、session


3.2.Session
服务器给每个浏览器创建了一块区域,专门用来存放数据。只要是一个浏览器的行为,均可以把这些数据存放在这个session中。浏览器和某个sessionn对象做了一个绑定。
你去理发,这个时候给你在它的系统里面录入了一条会员信息,用户名是你的手机号(JsessionID),下次去理发,直接报手机号(JsessionID),它在自己的系统里面搜了一下,发现你的会员信息。

3.2.1.session如何和浏览器关联在一起
session如果利用cookie,能不能实现呢?
cookie如何实现的?利用响应报文中的set-Cookie响应头,以及请求报文中的Cookie请求头,已经搭建好的基础架构。

session可不可以利用cookie来实现呢?可不可以把session的凭证将其放入到cookie中,这个时候cookie带回去以及发送回来的全部都是session的凭证。

整个流程:浏览器第一次访问服务端,服务端创建一个session对象,并发送一个cookie给浏览器(set-Cookie:JsessionID=xxx)。当浏览器再次访问服务器的时候,会带上这个cookie(Cookie:JsessionID=xxx),服务器就可以通过cookie找到其对应的session对象。

1.2.2.session的使用
在这里插入图片描述
提供一种通过多个页面请求或访问网站来标识用户并存储有关该用户的信息的方法。

在什么情况下会创建session呢?
通过request.getSession()/getSession(boolean create)API来创建session对象
在这里插入图片描述
返回与此请求关联的当前会话,或者如果请求没有会话,则创建一个会话。
在这里插入图片描述

返回与此请求关联的当前HttpSession,如果没有当前会话且create为true,则返回新会话。
如果create为false且请求没有有效的HttpSession,则此方法返回null。
在这里插入图片描述

ServletSession :

package com.cskaoyan.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/session")
public class ServletSession extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        //获取JSESSIONID
        String id = session.getId();
        System.out.println(id);
    }
}

当第一次访问/session时,set-Cookie响应头
在这里插入图片描述
第二次访问/session,
在这里插入图片描述
请求头中包含了Cookie请求头,此时响应头中没有再次响应set-Cookie了
原因在于cookie请求头中返回的JSESSIONID是一个有效的session id,服务器根据id找到了对应的session对象。

第一次访问
在这里插入图片描述
紧接着,关闭浏览器
在这里插入图片描述
原因在于:session传递给客户端时,内部设置的cookie的maxage为负数,仅在当前会话内有效,关闭浏览器,则cookie失效,cookie失效,则请求的时候不会携带JSESSIONNID,当遇到getSession这句代码时,就会触发逻辑,去判断是否有携带一个有效的JSESSIONID,如果没有,则重新创建一个新的session对象,把session的id通过cookie传回去。

3.2.3.问题一:关闭浏览器,session对象会销毁吗
不会。那么会一直存在吗?其实也不会。一段时间不使用的话,会被销毁。
3.2.4.问题二:关闭浏览器,session里面的数据会怎么样?
session对象以及session里面的数据,全部处于一种不可达的状态。不可访问到。
3.2.5.问题三:关闭服务器,session对象会销毁吗
会。关闭服务器或者卸载应用都会销毁session对象。

3.2.6.session存取数据
setAttribute/getAttribute/removeAttribute

session域、context、request域
Request:仅仅在一次请求内有效
Context:当前应用内只有一个。不同的servlet之间也可以进行共享数据。
Session:当前应用内有多少个浏览器,就有多少个session。范围也比较广。不同的servlet之间也可以进行共享数据。

ServletSession :

package com.cskaoyan.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/session")
public class ServletSession extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        session.setAttribute("name","session");
        //获取JSESSIONID
        String id = session.getId();
        System.out.println(id);
    }
}

session中存数据,在session2中取数据

ServletSession02 :

package com.cskaoyan.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @author shihao
 * @create 2020-06-28 10:07
 */
@WebServlet("/session02")
public class ServletSession02 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        String name = (String) session.getAttribute("name");
        System.out.println(name);
    }
}

在这里插入图片描述
3.2.6.1.session中的数据默认是会话级别的,关闭浏览器则失效
在这里插入图片描述
原因:创建session时,发送给浏览器的cookie是会话级别的,仅存在浏览器内存中,关闭浏览器,则cookie失效。
如果想持久化保存,则需要设置maxAge:

ServletSession :

package com.cskaoyan.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet("/session")
public class ServletSession extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        //cookie的name必须为JSESSIONID
        //你创建的这个cookie和浏览器内部的cookie是两个cookie,只不过当你关闭浏览器的时候
        //浏览器内部的cookie就失效了,而你创建的cookie存活了下来
        Cookie cookie = new Cookie("JSESSIONID", session.getId());
        cookie.setMaxAge(60 * 60);
        response.addCookie(cookie);
        session.setAttribute("name", "session");
        //获取JSESSIONID
        String id = session.getId();
        System.out.println(id);
    }
}

ServletSession02和上面的ServletSession02一样,没有做任何修改

关闭浏览器,再次访问session2,就可以取的到里面的数据。

3.2.6.2.问题三:关闭服务器,session中的数据会丢失吗?
不会。但是在IDEA中你可能会看到不同的结果,但是这是由于IDEA造成的,不是session原本的性质。
如何去观察呢?需要用到一个tomcat manager管理系统
1.webapps目录下manager不要删
2.到tomcat的conf目录下配置 tomcat-users.xml

<role rolename="manager-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui"/>

配置上面两行代码。
3.重新启动IDEA的tomcat,让他读取配置文件
4.到浏览器中输入:域名:端口号/manager,即:http://localhost:8080/manager
到管理系统中将应用停止,然后重新启动

打印出以下数据的整个流程:
1.访问http://localhost:8080/app/session
2.访问http://localhost:8080/app/session02
3.访问http://localhost:8080/manager,并在其中先stop应用再start应用
4.访问http://localhost:8080/app/session02
在这里插入图片描述

当应用被卸载或者服务器被关闭,内存中的session数据会被序列化到硬盘中,当应用重新被加载或者服务器开启,序列化的session文件会被重新读取到内存中,里面存储的sessionid会被分给一个新的session对象,同时将数据也一并给这个session对象。

秽土转生。人死了,需要用到一个新的躯体(新的session对象),然后将原先这个人的灵魂(session的id以及里面的数据)注入到这个躯体里面。
在这里插入图片描述

将应用停止,会发现在当前目录下多出一个session.ser文件,然后重新加载当前应用,会发现文件消失(文件内容会被加载到服务器内存中,然后文件被删除)

但是如果你们采用其他方式,可能看到不同的结果,比如Redeploy。关闭IDEA的tomcat重新打开等操作。
这个时候,可能也可以看到文件被序列化到硬盘,然后也消失,但是此时需要注意,这个文件是被删除了,而不是被加载到内存中。因为IDEA的tomcat每次在启动的时候,会到本地tomcat安装目录去读取最新配置文件。(这是IDEA自身的问题,不用纠结)
你也可以通过利用本地安装的tomcat来部署你的这个应用来验证。(用前面讲的虚拟映射之方式二)

3.2.7.session的生命周期
session的创建:
Request.getSession()/getSession(boolean create)
session的销毁:
关闭服务器、应用卸载session对象会被销毁
但是session中的数据不会丢失,session可以序列化
对于session中的数据而言,数据消失的方法有如下:
1.session.removeAttribute(仅仅是将一个属性给移除)
2.Session.invalidate()(将整个session对象失效)
3.一段时间不访问,session中的数据失效
在web.xml中:tomcat默认设置session为30min的有效期。
在这里插入图片描述
3.2.8.案例1
购物车案例。实现如下的功能:
在这里插入图片描述
3.2.8.1.Druid.properties文件放在哪
在这里插入图片描述
上图左侧是开发目录,右侧是部署目录。

可以放在src目录下,那么在最终部署目录中,位于classes目录下。
位于该目录下,有一个好处,就是可以利用类加载器来获取绝对路径。
c3p0-config.xml文件,放在src目录下,文件名称不要变,这个时候就可以直接得到datasource。

版本一:
本段代码位于IndexServletBak中:

Connection connection = null;
PreparedStatement psmt = null;
ResultSet resultSet = null;
try {
    Class.forName("com.mysql.jdbc.Driver");
    connection = DriverManager.getConnection("", "", "");
    psmt = connection.prepareStatement("select * from product");
    resultSet = psmt.executeQuery();
    List<Product> productList = new ArrayList<>();
    while (resultSet.next()) {
        String name = resultSet.getString("name");
        double price = resultSet.getDouble("price");
        Product product = new Product(name, price);
        productList.add(product);
    }
    //处理完毕list拿到
} catch (ClassNotFoundException | SQLException e) {
    e.printStackTrace();
} finally {
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (psmt != null) {
        try {
            psmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (resultSet != null) {
        try {
            resultSet.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

上面是标准的JDBC代码。但是实际开发过程中我们不会去这么写。
频繁的创建销毁connection对象,顶不住。用数据库连接池

版本二:
DruidUtils:
这里面我们巧妙的利用了类加载器来帮助我们找到对应的配置文件信息,类加载器加载类找到package。
用这个工具类我们可以很方便的得到dataSource和connection

package com.cskaoyan.cart.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class DruidUtils {

    private static DataSource dataSource;

    static {
        //Druid 符合java数据库连接的规范的 所以肯定有一个datasource
        //如果放在WEB-INF下,必须要提供context或者相关(ServletContext.getRealpath获取绝对路径)
        //但是如果这么做,工具类就无法在se项目中使用
        //可以利用类加载器去获取一个绝对路径
        //类加载器有一个API可以直接定位到classes目录
        try {
            //要求就是文件必须放置在src目录下
            //DruidUtils.class.getClassLoader()直接定位到DruidUtils类的classes目录,
            //getResourceAsStream("druid.properties")定位到其目录下的druid.properties文件
            InputStream inputStream = DruidUtils.class.getClassLoader().
                    getResourceAsStream("druid.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static DataSource getDataSource(){
        return dataSource;
    }

    /**
     *  Class.forName("com.mysql.jdbc.Driver");
     *  connection = DriverManager.getConnection("", "", "");
     *  今后获取连接使用下面代码即可,不要自己去getConnection
     *  因为上面代码没有数据库连接池
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

然后就可以将IndexServletBak的这两行代码:
Class.forName(“com.mysql.jdbc.Driver”);
connection = DriverManager.getConnection("", “”, “”);
改成IndexServletBak2中的这行代码:
connection = DruidUtils.getConnection();

版本三:

3.2.8.2.Dbutils
3.2.8.2.1.关于传不传datasource的讨论
用DBUtils工具类,可以简化JDBC代码
在这里插入图片描述

如果不传datasource,那么你需要传一个connection,那么就需要自己去手动关闭connection
如果传了datasource,那么就不需要自己去手动关闭connection
所以一般情况下是传datasource的。

3.2.8.2.2.关于query源码的解读
在这里插入图片描述
通过比对可以发现,query就是对JDBC代码的封装。

要记住下面三个Handler
在这里插入图片描述

3.2.9.session依赖于cookie,如果cookie禁用怎么办
Response.encodeURL

3.2.10.案例2

3.2.11.三个域的区别
Context:和浏览器没有区别的数据,所有浏览器看起来都一样
Session:区分浏览器。购物车、个人访问记录、个人足迹
Request:一次请求。转发两个组件之间。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-玫瑰少年-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值