【JAVA】Servlet开发

news2024/11/16 11:55:25

目录

HttpServlet

HttpServletRequest

 HttpServletResponse

错误页面

设置网页自动刷新时间

构造重定向相应

js发起http请求 

服务器端对js发起的http请求进行处理

前端获取后端数据,添加到当前页面的末尾,代码示例: 

 前后端交互:引入数据库(存储数据)

1)引入数据库依赖

​编辑

​编辑2)建库建表

3)编写数据库代码

整体代码 

利用Cookie和Session实现登录逻辑

1.登录页面(html)

2.通过一个Servlet处理上述的登录请求

3.网站主页,通过另一个servlet生成的动态页面


掌握如下三个类,就可以完成Servlet的大部分开发了

  1. HttpServlet
  2. HttpServletRequest
  3. HttpServletResponse

URI:唯一资源标识符

URL:唯一资源定位/地址符

HttpServlet

HttpServlet继承这个类,重写里面的方法,目的就是为了把咱们子集定义的代码,“插入到”tomcat中,HttpServlet的常用方法如下:

方法名称
调用时机
init
HttpServlet 实例化之后被调用一次,初始化
destory
HttpServlet 实例不再使用的时候调用一次,释放资源
service
收到 HTTP 请求的时候调用
doGet
收到 GET 请求的时候调用 ( service 方法调用 )
doPost
收到 POST 请求的时候调用 ( service 方法调用 )
doPut/doDelete/doOptions/...
收到其他请求的时候调用 ( service 方法调用 )
我们实际开发的时候主要重写 doXXX 方法 , 很少会重写 init / destory / service
init还是比较有用的;service一般会doGet/doPost替代;destory一般用不上,说了不算,算了不说,因为一个Servlet不用了,说明Tomcat要关闭了,而Tomcat关闭有两种方式:
  1. 直接干掉Tomcat进程,完全来不及调用destory的;
  2. 通过8005管理端口,给Tomcat发送一个“停机”指令,这个时候是能够执行destory的。

init / destory / service 这三个方法都不需要手动调用,会被tomcat在合适的时机,自动调用,咱们写好代码,让别人来帮忙调用,这种方式就叫做 “框架” ,也就是一个程序的主体部分,都已经被其他大佬们写完了,有些细节内容,允许咱们插入咱们自己写的自定义的逻辑.

HttpServletRequest

Tomcat 通过 Socket API 读取 HTTP 请求 ( 字符串 ), 并且按照 HTTP 协议的格式把字符串解析成
HttpServletRequest 对象 .
核心方法
方法
描述
String getProtocol()
返回请求协议的名称和版本。
String getMethod()
返回请求的 HTTP 方法的名称,例如, GET POST PUT
String getRequestURI()
从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该
请求的 URL 的一部分。
String getContextPath()
返回指示请求上下文的请求 URI 部分。
String getQueryString()
返回包含在路径后的请求 URL 中的查询字符串。
InputStream
getInputStream()
用于读取请求的 body 内容 . 返回一个 InputStream 对象 .
Enumeration
getParameterNames()
返回一个 String 对象的枚举,包含在该请求中包含的参数的名
称。
String getParameter(String
name)
以字符串形式返回请求参数的值,或者如果参数不存在则返回
null
String[]
getParameterValues(String
name)
返回一个字符串对象的数组,包含所有给定的请求参数的值,
如果参数不存在则返回 null
Enumeration
getHeaderNames()
返回一个枚举,包含在该请求中包含的所有的头名。
String getHeader(String
name)
以字符串形式返回指定的请求头的值。
String
getCharacterEncoding()
返回请求主体中使用的字符编码的名称。
String getContentType()
返回请求主体的 MIME 类型,如果不知道类型则返回 null
int getContentLength()
以字节为单位返回请求主体的长度,并提供输入流,或者如果
长度未知则返回 -1

上述的方法都是get系列方法(都是读方法),没有set系列(没有写方法),当前拿到的HttpServletRequest,这些数据的内容已经确定下来了,程序员是不应该修改的.

前端将数据交给后端

除了query string之外,还可以通过http请求的body来传递参数(POST)。

(1)直接通过form表单

        (body的格式就是query string的格式)

        Content-Type:application/x-www-form-urlencoded

(2)直接使用json

        (body的格式就是json)

        Content-Type:application/json

这三种方式本质上是等价的,都是把键值对数据交给服务器,前两种方法servlet天然支持的,json这种方法需要引入第三方库。

在java中,json的第三方库是非常多的,这里我们使用jackson(jackson是spring官方推荐的库,也被spring集成起来了)

步骤如下:

1)下载导入jackson到项目中,通过maven

Maven Repository: Search/Browse/Explore (mvnrepository.com)

(2)使用jackson

一个类两个方法

ObjectMapper

  • 把json字符串,映射成java的一个对象(read方法)
  • 把一个java对象,映射成json字符串(write方法)
Request request = objectMapper.readValue(req.getInputStream(),Request.class);

1.核心工作就是把json字符串,映射成java 对象,参数就是json字符串(json字符串是在http的body中的,就需要通过HttpServletRequest中的getInputStream来获取到)

此处把这个流对象直接传给readValue,readValue内部就会读取InputStream中的所有数据(http请求的body,也就是json字符串),进一步尝试解析

2.按照json的格式,进行解析,把json字符串,解析成map(键值对)

3.把map转换成对象,在方法的第二个参数

如下示例:

import com.fasterxml.jackson.databind.ObjectMapper;

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 java.io.IOException;
import java.io.ObjectInputValidation;


class Request()
{
    public String username;
    public String password;
}
class Response{
    public boolean ok;
}

@WebServlet("/json")
public class JsonParameterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        Request request = objectMapper.readValue(req.getInputStream(),Request.class);
        System.out.println("username="+request.username);
        System.out.println("password="+request.password);

        Response response=new Response();
        response.ok=true;
        //把响应对象转成json字符串
        String respJson=objectMapper.writeValueAsString(response);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

 HttpServletResponse

        Servlet 中的 doXXX 方法的目的就是根据请求计算得到相应 , 然后把响应的数据设置到
HttpServletResponse 对象中 . 然后 Tomcat 就会把这个 HttpServletResponse 对象按照 HTTP 协议的格式 , 转成一个字符串 , 并通过 Socket 写回给浏览器 .

核心方法

方法
描述
void setStatus(int sc)
为该响应设置状态码
void setHeader(String name, String value)
设置一个带有给定的名称和值的 header. 如果 name 已经存在 , 则覆盖旧的值
void addHeader(String
name, String value)
添加一个带有给定的名称和值的 header. 如果 name 已经存在 , 不覆盖旧的值, 并列添加新的键值对
void setContentType(String type)
设置被发送到客户端的响应的内容类型。
void setCharacterEncoding(String
charset)
设置被发送到客户端的响应的字符编码( MIME 字符集)例如,UTF-8
void sendRedirect(String location)
使用指定的重定向位置 URL 发送临时重定向响应到客户端
PrintWriter getWriter()
用于往 body 中写入文本格式数据
OutputStream getOutputStream()
用于往 body 中写入二进制格式数据

错误页面

resp.sendError(404,"这个页面是一个错误页面")

设置网页自动刷新时间

resp.setHeader("refresh","1");

构造重定向相应

resp.setStatus(302);
resp.setHeader("Location","https://www.baidu.com");

header需要有一个Location属性,描述要跳转到哪里 ,除了这种写法外,还有如下另一种方法:

resp.sendRedirect("https://www.baidu.com");

使用ajax,需要先引入JQeury第三方库

jquery (v3.7.1) - jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作更加简单。 | BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务icon-default.png?t=N7T8https://www.bootcdn.cn/jquery/

链接如上,选择如下链接

在script标签引入jquery

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

js发起http请求 

        // 把用户填写的内容,发送给服务器,让服务器来保存
        // $ 是jquery提供的全局变量,ajax是$的一个方法
        // ajax的参数是一个js对象,可以有很多属性
        let body={
            "from":from,
            "to":to,
            "message":message
        }
        //上述body是一个js对象,还需要转换成json字符串
        let jsonString = JSON.stringify(body)
        $.ajax({
            type:'post',
            url:'message',
            contentType:'application/json; charset=utf8',
            data:jsonString,
            //这里的body与上面的body不是同一个,是响应报文的正文
            success:function(body){
                // 这个回调就是收到响应之后要执行的代码了
            }

        });

此处success回调函数,不是立即执行的,而是在浏览器收到服务器返回的成功这样的响应的时候,才会执行function ,这个函数的第一个参数,是响应数据的body中的内容。

服务器端对js发起的http请求进行处理

import com.fasterxml.jackson.databind.ObjectMapper;

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 java.io.IOException;
import java.util.ArrayList;

class Message{
//    确保java代码中类的属性名字和json中的属性名字保持一致,才能够自动填充
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    private List<Message> messageList = new ArrayList<>();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用jackson读取前端发来的数据,把这个数据保存到服务器这边
        Message message = objectMapper.readValue(req.getInputStream(),Message.class);
        System.out.println("请求中收到的message:"+ message);
        //保存数据最简单的是直接内存中存储,使用集合类
        //但是这样做一旦重启服务器,一切数据都没有了,一般存储到数据库中
        messageList.add(message);
        //返回一个响应
        resp.setStatus(200);
        resp.getWriter().write("ok");
    }
}

这里启动服务器后,不能直接访问/message,需要访问.html路径

当浏览器要向服务器获取资源, 服务器方使用List类型的数组存储历史数据,转成的json字符串就是一个json数组,jackson自动遍历List里的每个元素,把每个元素分别转成json字符串。

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
        resp.setContentType("application/json;charset=utf-8");
        String respJson = objectMapper.writeValueAsString(messageList);
        resp.getWriter().write(respJson);
    }

上述代码setStatus和setContentType必须在getWriter前面,否则不会生效 

    $.ajax{

        type:'get',

        url:'message',

        success:function(body){

            //处理服务器返回的响应数据。(json格式的数组)

        }

    }

//当响应中,header带有ContentType:"application/json",JQuery就会自动把json字符串解析成js对象了,如果没有带ContentType:"application/json"就需要通过js代码JSON.parse方法手动把json字符串转成js对象

前端获取后端数据,添加到当前页面的末尾,代码示例: 

    $.ajax{
        type:'get',
        url:'message',
        success:function(body){
            //处理服务器返回的响应数据。(json格式的数组)
            //由于响应中已经有ContentType:"application/json"了,就不需要使用parse方法手动转换了
            //body = JSON.parse(body);
            
            //拿到 container这个元素
            let containerDiv = document.querySelector('.container');
            //处理服务器返回的响应数据。(json格式的数组了)
            for(let i=0;i<body.length;i++)
            {
                //body是一个数组,此时的message也就是js对象了
                //这个message对象里有三个属性:from、to、message
                let message = body[i];
                
                //根据message对象构建html片段,把这个片段给显示到网页上
                //createElement 方法就能构造一个html标签
                //此时就得到了<div></div>
                let div = document.createElement('div');
                //还需要给这个div设置一个属性
                div.className = 'row';
                //设置内容
                div.innerHTML = message.from + " 对 " + message.to + " 说:" +message.message;
                //将这个div添加到containerDiv末尾
                containerDiv.appendChild(div);
            }
        }

 前后端交互:引入数据库(存储数据)

1)引入数据库依赖

Maven仓库链接

Maven Repository: Search/Browse/Explore (mvnrepository.com)

搜索MySQL,选择如下:

这里选择5.1.47版本

复制如下代码

复制到pom.xml里


2)建库建表

create database if not exists message_wall charset utf8;
use message_wall;
-- 删表的目的是为了防止之前数据库里有一样的表
drop table if exists message;
use message_wall;
create table message(`from` varchar(1024),`to` varchar(1024),message varchar(1024));

3)编写数据库代码

// 1. 创建数据源
private DataSource dataSource = new MysqlDataSource();
((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3307/message_wall?characterEncoding=utf8&useSSL=false");
((MysqlDataSource) dataSource).setUser("root");
((MysqlDataSource) dataSource).setPassword("root");
// 2. 建立连接
Connection connection = dataSource.getConnection();

// 3. 构造 SQL
String sql = "insert into message values(?, ?, ?)";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, message.from);
statement.setString(2, message.to);
statement.setString(3, message.message);

// 3. 执行 SQL
statement.executeUpdate();

// 4. 回收资源
statement.close();
connection.close();

整体代码 

MessageServlet.java

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

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.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

class Message {
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();

    // 引入数据库, 此时 messageList 就不再需要了.
    // private List<Message> messageList = new ArrayList<>();

    private DataSource dataSource = new MysqlDataSource();

    @Override
    public void init() throws ServletException {
        // 1. 创建数据源
        ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3307/message_wall?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("root");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 读取前端发来的数据, 把这个数据保存到服务器这边.
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        System.out.println("请求中收到的 message: " + message);
        // 最简单的办法, 直接在内存中保存. 可以使用一个集合类, 把所有收到的 message 都存到内存中.
        // 很明显, 保存到内存, 并非是一个非常合理的办法. 后续一旦重启服务器, 数据丢失了.
        // 相比之下, 把这个数据持久化存储到数据库中, 更科学的.
        // messageList.add(message);
        // 插入数据库
        try {
            save(message);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        // 返回一个响应
        resp.setStatus(200);
        resp.getWriter().write("ok");
        // resp.setContentType("application/json");
        // resp.getWriter().write("{ ok: true }");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过这个方法来处理当前获取消息列表的 get 请求. 不需要解析参数, 直接返回数据即可.
        resp.setStatus(200);
        resp.setContentType("application/json; charset=utf8");
        // 从数据库查询
        List<Message> messageList = null;
        try {
            messageList = load();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        String respJson = objectMapper.writeValueAsString(messageList);
        resp.getWriter().write(respJson);
    }

    private void save(Message message) throws SQLException {
        // 通过这个方法把 message 插入到数据库中

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造 SQL
        String sql = "insert into message values(?, ?, ?)";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1, message.from);
        statement.setString(2, message.to);
        statement.setString(3, message.message);

        // 3. 执行 SQL
        statement.executeUpdate();

        // 4. 回收资源
        statement.close();
        connection.close();
    }

    private List<Message> load() throws SQLException {
        // 通过这个方法从数据库读取到 message.

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造 SQL
        String sql = "select * from message";
        PreparedStatement statement = connection.prepareStatement(sql);

        // 3. 执行 sql
        ResultSet resultSet = statement.executeQuery();

        // 4. 遍历结果集合
        List<Message> messageList = new ArrayList<>();
        while (resultSet.next()) {
            Message message = new Message();
            message.from = resultSet.getString("from");
            message.to = resultSet.getString("to");
            message.message = resultSet.getString("message");
            messageList.add(message);
        }

        // 5. 回收资源
        resultSet.close();
        statement.close();
        connection.close();

        // 6. 返回 messageList
        return messageList;
    }
}

 message_wall.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙</title>
    <style>
        /* * 通配符选择器, 是选中页面所有元素 */
        * {
            /* 消除浏览器的默认样式. */
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 600px;
            margin: 20px auto;
        }

        h1 {
            text-align: center;
        }

        p {
            text-align: center;
            color: #666;
            margin: 20px 0;
        }

        .row {
            /* 开启弹性布局 */
            display: flex;
            height: 40px;
            /* 水平方向居中 */
            justify-content: center;
            /* 垂直方向居中 */
            align-items: center;
        }

        .row span {
            width: 80px;
        }

        .row input {
            width: 200px;
            height: 30px;
        }

        .row button {
            width: 280px;
            height: 30px;
            color: white;
            background-color: orange;
            /* 去掉边框 */
            border: none;
            border-radius: 5px;
        }

        /* 点击的时候有个反馈 */
        .row button:active {
            background-color: grey;
        }
    </style>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
</head>
<body>
<div class="container">
    <h1>表白墙</h1>
    <p>输入内容后点击提交, 信息会显示到下方表格中</p>
    <div class="row">
        <span>谁: </span>
        <input type="text">
    </div>
    <div class="row">
        <span>对谁: </span>
        <input type="text">
    </div>
    <div class="row">
        <span>说: </span>
        <input type="text">
    </div>
    <div class="row">
        <button id="submit">提交</button>
    </div>
    <!-- <div class="row">
        xxx 对 xx 说 xxxx
    </div> -->
</div>

<script>
    // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示.
    // 点击的时候, 获取到三个输入框中的文本内容
    // 创建一个新的 div.row 把内容构造到这个 div 中即可.
    let containerDiv = document.querySelector('.container');
    let inputs = document.querySelectorAll('input');
    let button = document.querySelector('#submit');
    button.onclick = function() {
        // 1. 获取到三个输入框的内容
        let from = inputs[0].value;
        let to = inputs[1].value;
        let msg = inputs[2].value;
        if (from == '' || to == '' || msg == '') {
            return;
        }
        // 2. 构造新 div
        let rowDiv = document.createElement('div');
        rowDiv.className = 'row message';
        rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
        containerDiv.appendChild(rowDiv);
        // 3. 清空之前的输入框内容
        for (let input of inputs) {
            input.value = '';
        }
        // 4. 把用户填写的内容, 发送给服务器. 让服务器来保存.
        //    $ 是 jquery 提供的全局变量. ajax 就是 $ 的一个方法.
        //    ajax 的参数是一个 js 对象, 可以有很多属性
        let requestBody = {
            "from": from,   // from 变量里的值, 就是第一个输入框的内容, "张三"
            "to": to,       // to 变量的值, 就是第二个输入框的内容, "李四"
            "message": msg  // msg 变量的值, 就是第三个输入框的内容, "我喜欢你很久了"
        };
        // 上述 body 是一个 js 对象, 还需要转成 json 字符串.
        let jsonString = JSON.stringify(requestBody);
        $.ajax({
            type: 'post',
            url: 'message',
            contentType: 'application/json; charset=utf8',
            data: jsonString,
            success: function(responseBody) {
                // 这个回调就是收到响应之后要执行的代码了.
                // 前端使用 console.log 打印日志到控制台. (chrome 开发者工具的控制台)
                console.log("responseBody: " + responseBody);
            }
        });
    }

    // 直接在 script 里面写的 js 代码, 就是在页面加载时被执行到的.
    // 发起一个 get 请求, 从服务器获取到数据
    // get 请求不需要 body, 也就不需要上述 data 和 contentType 属性了.
    $.ajax({
        type: 'get',
        url: 'message',
        success: function(body) {
            // 由于响应中已经有 Content-Type: application/json 了, 就不需要使用 parse 方法手动转换了.
            // body = JSON.parse(body);

            // 拿到 container 这个元素
            let containerDiv = document.querySelector('.container');
            // 处理服务器返回的响应数据. (json 格式的数组了)
            for (let i = 0; i < body.length; i++) {
                // body 是一个数组, 此时 message 也就是 js 对象了.
                // 这个 message 对象里, 有三个属性, from, to, message
                let message = body[i];

                // 根据 message 对象构建 html 片段, 把这个片段给显示到网页上.
                // createElement 方法就能构造出一个 html 标签.
                // 此时就得到了 <div></div>
                let div = document.createElement('div');
                // 还需要往里面设置一个 属性 , class="row" (设置这个属性, 是为了让 css 能够给这个元素设置一些样式)
                // 此时就得到了 <div class="row"></div>
                div.className = 'row';
                // 给这个 div 里设置内容
                // 此时就得到了 <div class="row">张三 对 李四 说: 我喜欢你很久了</div>
                div.innerHTML = message.from + " 对 " + message.to + " 说: " + message.message;
                // 把 div 添加到 containerDiv 的末尾
                containerDiv.appendChild(div);
            }
        }
    });
</script>
</body>
</html>

利用Cookie和Session实现登录逻辑

Cookie是客户端存储数据的机制

Session是服务器存储数据的机制(不算持久化存储)

1.登录页面(html)

2.通过一个Servlet处理上述的登录请求

通过这个Servlet读取用户名和密码,并且验证是否登录成功。

如果登陆成功,就会给当前用户,创建一个会话 ,并且把得到的sessionid,通过cooki返回给客户端,客户端就把cookie保存起来了。

3.网站主页,通过另一个servlet生成的动态页面

在这个页面中,就会把刚才这里的用户数据给显示到页面上。

getSession(true)

这个方法,就是根据请求的cookie中的sessionid,查询服务器的hash表,找到对应的session对象。如果cookie中没有sessionid(首次登录的时候,就是没有的)或者sessionid没有查找到对应的session对象,就可以创建出一个session对象出来。

参数为true,允许不存在时自动创建;false不能创建,直接返回null

//可以给会话中保存一些自定义的数据,通过Attribute的方式来保存
HttpSession session = req.getSession(true);
//此处Atrribute也是键值对,这里的内容存储什么都可以
session.setAttribute("username",username);
session.setAttribute("loginTime",System.currentTimeMillis());
//此处相当于登录成功,让页面跳转网站首页
resp.sendRedirect("index");

此处的getSession会创建新会话

1)生成sessionid和HttpSession对象

2)把上述sessionid和HttpSession对象保存到内存hash表中

3)把sessionid设置到响应报文的header中的 Set-Cookie字段

浏览器拿到响应,就会把这个Set-Cookie的内容保存到浏览器的Cookie中 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1526757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序 nodejs+vue+uninapp学生在线选课作业管理系统

基于微信小程序的班级作业管理助手使用的是MySQL数据库&#xff0c;nodejs语言和IDEA以及微信开发者工具作为开发工具&#xff0c;这些技术和工具我在日常的作业中都经常的使用&#xff0c;并且因为对编程感兴趣&#xff0c;在闲暇时间也进行的进行编程的提高&#xff0c;所以在…

Leet code 1658 将x减到0的最小操作数

解题思路&#xff1a;滑动窗口 主要思想&#xff1a;正难逆简 题目需要左找一个数 右找一个数 我们不如直接找中间最长的一连串子数让这串子树和为 数组子树和减去X 找不到就返回 -1 滑动窗口双指针从左端出发&#xff0c;进行 进窗口 判断 出窗口 更新结果四个步骤 代码…

bugreport中查看开发者选项动画时长缩放日志

首先打开开发者选项&#xff0c;抓取一份bugreport解压后找到bugreport-机型-时间点.zip文件&#xff0c;然后再解压此文件 解压后进入该文件&#xff0c;找到bugreport-机型-时间点.txt文件 打开此文件&#xff0c;搜索“animator_duration_scale”关键字&#xff0c;找到图片…

9成省份“鸿蒙化”,它真起来了?

自去年9月华为宣布鸿蒙原生应用全面启动以来&#xff0c;鸿蒙正以不可阻挡之势&#xff0c;快速在全国千行百业的移动应用领域推进。不仅有支付宝、快手、淘宝、京东等超200家头部互联网企业加入鸿蒙生态&#xff1b;2024年以来&#xff0c;上海、浙江、广西等多省市政务民生、…

1.中医学习-总论

目录 1.为什么要学中医 2.什么是中医 介绍 中医例子1&#xff1a; 中医例子2: 中医最高境界“大道至简” 中医讲究的是本质 中医核心&#xff1a;阴阳、表里、寒热、虚实 ​编辑医不叩门 3.阴阳 1.一天中的阴阳 2.一年中的阴阳 3.阴阳之间的关系 4.阴阳四季的变化 …

【vue elementUI】修改el-dropdown样式

实现效果如下&#xff1a; 代码如下&#xff1a; <el-dropdown trigger"click" command"handleCommand" active-text-color"#606266"><span class"product-card">{{getCategoryName(categoryId)}}</span><el-dro…

基于SpringBoot和Vue的图书个性化推荐系统的设计与实现

今天要和大家聊的是一款基于SpringBoot和Vue的图书个性化推荐系统。 &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&#x1f495;个人简介&#xff1a;混迹在java圈十年有余&#xff0c;擅长Java、微信小程序、Python、Android等&#xff0c;大家有这一块的…

代码随想录算法训练营第day29|491.递增子序列、 46.全排列、 47.全排列 II

目录 491.递增子序列 46.全排列 47.全排列 II 491.递增子序列 力扣题目链接 给定一个整型数组, 你的任务是找到所有该数组的递增子序列&#xff0c;递增子序列的长度至少是2。 示例: 输入: [4, 6, 7, 7]输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7,…

Avalon总线学习

Avalon总线学习 avalon总线可以分为&#xff1a; Avalon clock interface Avalon reset interface Avalon Memory mapped interface Avalon iterrupt interface Avalon streaming interface Avalon tri-state conduit interface Avalon conduit interface 1、Avalon c…

【LeetCode热题100】206. 反转链表(链表)

一.题目要求 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 二.题目难度 简单 三.输入样例 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2…

【系统架构设计师】软件架构设计 02

系统架构设计师 - 系列文章目录 01 系统工程与信息系统基础 02 软件架构设计 文章目录 系统架构设计师 - 系列文章目录 文章目录 前言 一、软件架构的概念 ★★★ 二、基于架构的软件开发 ★★★★ 三、软件架构风格 ★★★★ 1.数据流风格 2.调用/返回风格 3.独立构件风格…

PlantUML Integration 编写短信服务类图

PlantUML Integration 写一个类图&#xff0c;主要功能为 1、编写一个serviceSms短信服务类&#xff1b; 2、需要用到短信的地方统一调用基建层的服务即可&#xff1b; 3、可以随意切换、增加短信厂商&#xff0c;不需要更改场景代码&#xff0c;只需要更改application.yml 里面…

搜索二叉树迭代和递归的两种*简单*实现方式

判断搜索二叉树 概念 一棵树所有结点的左节点小于父节点&#xff0c;右节点大于父节点&#xff0c;则为搜索二叉树。 迭代方法 中序遍历二叉树&#xff0c;如果总是升序则是搜索二叉树。如果存在降序&#xff0c;那肯定不是搜索二叉树。 Coding checkTreeOrder()方法 boo…

【Godot4自学手册】第二十六节用GPUParticles2D节点实现地宫入口的奇异光芒

我们的主人公与NPC对话完成后&#xff0c;夜晚来临&#xff0c;在城北有一处乱石岗&#xff0c;出现了奇异光芒&#xff0c;这是地宫的入口&#xff0c;但是有一妖怪把守&#xff0c;我们必须消灭妖怪后才可发现地宫入口。这一节&#xff0c;主要记录利用粒子系统GPUParticles2…

YOLOv9目标识别——环境配置与训练自己的数据集

前言 YOLOv9引入了一种全新的计算机视觉模型架构&#xff0c;相比目前流行的YOLO模型&#xff08;如YOLOv8、YOLOv7和YOLOv5&#xff09;&#xff0c;在MS COCO数据集上取得了更高的mAP&#xff08;平均精度均值&#xff09;。 YOLOv9是由Chien-Yao Wang、I-Hau Yeh和Hong-Yua…

Elasticsearch:调整近似 kNN 搜索

在我之前的文章 “Elasticsearch&#xff1a;调整搜索速度”&#xff0c;我详细地描述了如何调整正常的 BM25 的搜索速度。在今天的文章里&#xff0c;我们来进一步探讨如何提高近似 kNN 的搜索速度。希望对广大的向量搜索开发者有一些启示。 Elasticsearch 支持近似 k 最近邻…

做好外贸网站SEO优化,拓展海外市场

随着全球贸易的发展和互联网的普及&#xff0c;越来越多的外贸企业将目光投向了网络&#xff0c;希望通过建立网站来拓展海外市场。然而&#xff0c;在竞争激烈的外贸市场中&#xff0c;要让自己的网站脱颖而出&#xff0c;吸引更多的目标客户&#xff0c;就需要进行有效的SEO优…

【C语言进阶篇】C语言内存函数

目录 1.memcpy函数及其模拟实现 1.1 memcpy函数的使用 1.2 memcpy函数的模拟实现 2.memmove函数及其模拟实现 2.1 memmove函数的使用 2.2 memmove函数的模拟实现 3.memset函数 4.memcmp函数 1.memcpy函数及其模拟实现 1.1 memcpy函数的使用 memcpy函数是用来拷贝内存的函数&…

【力扣精选算法100道】——带你了解(数组模拟栈)算法

目录 &#x1f4bb;比较含退格的字符串 &#x1f388;了解题意 &#x1f388;分析题意 &#x1f6a9;栈 &#x1f6a9;数组模拟栈 &#x1f388;实现代码 844. 比较含退格的字符串 - 力扣&#xff08;LeetCode&#xff09; &#x1f4bb;比较含退格的字符串 &#x1f3…

代码学习记录22--回溯算法第三天

随想录日记part22 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.03.17 主要内容&#xff1a;今天主要是结合类型的题目加深对回溯算法的理解&#xff1a;1.组合总和;2.组合总和 ;3.分割回文串。 39. 组合总和 40.组合总和II131.分割回文串 Topic1组合总和 题…