Servlet API

在学Servlet API 之前,一定要先学好 HTTP 协议 和 Servlet 的基本使用
HTTP 协议
Servlet 基本使用

  • 1. HttpServlet
    • 处理乱码
    • 核新方法
    • Servlet"生命周期"
  • 2. HttpServletRequest
    • 核心方法
      • 打印请求信息
      • 获取 GET 请求中的参数
      • 获取 POST 请求中的参数
        • form格式
        • Json格式
  • 2.1 - Postman安装
    • 进入Postman
    • 使用教程
      • POST请求格式
      • POST 请求,Json 格式
  • 3. HttpServletResponse
    • 核心方法
      • 设置状态码
      • 自动刷新
      • 重定向
  • 实现一个 web 表白墙
    • 1. 准备工作
    • 2. 约定前后端交互接口
    • 3. 数据存入数据库
    • 4.创建 DBUtil 类
    • 5. 服务器端代码
    • 6. 调整前端页面
    • 7. 实现效果
  • 上传文件
    • 代码示例

1. HttpServlet

我们在写 Servlet 代码的时候,首先第一步就是先创建类,继承自 HttpServlet,并重写其中的某些方法

我们想要构造GET请求还是容易的,直接在地址栏输入就行,可是我们POST请求就不行了,他是隐藏的,我们需要用到 ajax或form表单
我这里就用 ajax 举个例子:

Servlet代码

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;
@WebServlet("/method")
public class MethodServlet extends HttpServlet {
 @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("POST 响应");
    }
 }   

ajax代码(记得导入jqurey),且这个html文件要放在webapp下

<body> 
    <script src="jquery.js">script>
    <script>
       $.ajax({
           type: 'post',
           url: 'method',
           success: function(body){
               console.log(body);
           }
       });
    script>
body>

页面响应:
Servlet API_第1张图片
解析:
Servlet API_第2张图片

处理乱码

还有需要注意一点的是:
Servlet API_第3张图片写上这串代码就是为了防止页面出现乱码,因为我们浏览器是 GBK,而这里都是utf8,用这串代码告诉浏览器用utf8来读,统一编码格式,防止出现乱码
Servlet API_第4张图片如果我们没有这串代码:
Servlet API_第5张图片

我们发现构造POST请求还挺麻烦的,有没有更快更方便的方式,当然是有的,有Postman这样一个工具:

核新方法

方法名称 调用时机
init 在 HttpServlet 实例化之后被调用一次
destory 在 HttpServlet 实例不再使用的时候调用一次
service 收到 HTTP 请求的时候调用
doGet 收到 GET 请求的时候调用(由 service 方法调用)
doPost 收到 POST 请求的时候调用(由 service 方法调用)
doPut/doDelete/doOptions/… 收到其他请求的时候调用(由 service 方法调用)

我们很少会用到 init / destory / service,这里就需要说说Servlet的"生命周期"了:

Servlet"生命周期"

Servlet"生命周期"主要分为三个阶段:
1,初始化阶段 //调用init()方法
2,响应客户请求阶段 //调用service()方法
3,终止阶段 //调用destroy()方法

1:init:初始化阶段,对象创建好之后,就会执行到,用户可以重写这个方法,来执行一些初始化逻辑
2:service:在处理请求阶段来调用,每一次来个请求都要调用一次service
3:destroy:退出主循环,tomcat结束之前会调用,用来释放资源

2. HttpServletRequest

HttpServletRequest 对应到一个 HTTP 请求,他请求里面有啥,这里就有啥,所以要想学好这个,学好 HTTP请求就轻松了

核心方法

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

通过这些方法可以获取到一个请求中的各个方面的信息.

注意: 请求对象是服务器收到的内容, 不应该修改. 因此上面的方法也都只是 “读” 方法, 而不是 “写” 方法

打印请求信息

我这里就打印 GET 请求了:

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.Enumeration;

@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("

首行部分

"
); stringBuilder.append(req.getProtocol()); stringBuilder.append("
"
); //换行 stringBuilder.append(req.getMethod()); stringBuilder.append("
"
); stringBuilder.append(req.getRequestURI()); stringBuilder.append("
"
); stringBuilder.append(req.getContextPath()); stringBuilder.append("
"
); stringBuilder.append(req.getQueryString()); stringBuilder.append("
"
); stringBuilder.append("

header 部分

"
); //枚举类 Enumeration<String> headerNames = req.getHeaderNames(); while(headerNames.hasMoreElements()){ String headerName = headerNames.nextElement(); String headerValue = req.getHeader(headerName); stringBuilder.append(headerName+": "+headerValue+"
"
); } //注意统一格式 resp.setContentType("text/html; charset=utf8"); resp.getWriter().write(stringBuilder.toString()); } }

Servlet API_第6张图片

获取 GET 请求中的参数

上述方法都不常用,真正常用的还是getParameter 这个方法(是获取到query string 中的详细内容)

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;

@WebServlet("/getParameter")
public class GetParameterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //预期这样一个请求: /getParameter?userId=123&classId=456
        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");
        resp.getWriter().write("userId=" + userId + ", classId=" + classId);
    }
}

Servlet API_第7张图片

我们发现,没有 query string的时候,getParameter 获取的值是null

我们通过浏览器加上query string,在刷新
Servlet API_第8张图片
此时就说明了服务器已经获取到了客户端传过来的参数了

获取 POST 请求中的参数

POST 请求的参数一般都是通过 body 传递给服务器的,但是我们知道前面学 HTTP请求的时候,POST中 body中的数据格式有很多种,我这里举几个例子:

form格式

Servlet代码:

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;

@WebServlet("/postGetParameter")
public class PostGetParameterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");
        resp.getWriter().write("userId="+userId+",classId="+classId);
    }
}

前端代码:

<form action="postGetParameter" method="POST">
    <input type="text" name="userId">
    <input type="text" name="classId">
    <input type="submit" value="提交">
form>

注意路径啥的一定要写对

当点击提交时我们用抓包工具观察一下:
Servlet API_第9张图片

Json格式

如果是 json 格式,如果我们手动来解析,是非常困难的,因为我们知道 json 是可以嵌套的,由于他们是以键值对的形式出现,就是说键对应的值也可以是一个 json 格式的

Jackson导入

所以,这里我们需要导入第三方库来解决,有很多,我这里使用的是 Jackson(因为他是 Spring 官方推荐的,后续我们学Spring 也会用到),还是一样去 中央仓库上下
官网:https://mvnrepository.com/
Servlet API_第10张图片Servlet API_第11张图片
Servlet API_第12张图片

 <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.0</version>
        </dependency>

Servlet代码:

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;

class User{
    //当前这俩属性都设置成 public, 如果设为了private,就要提供 getter and setter 效果也一样
    public int userId;
    public int classId;
}

@WebServlet("/postJson")
public class PostJsonServlet extends HttpServlet {
    //1.创建一个 jackson 核心对象
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //2.读取 body 中的请求,然后使用 ObjectMapper 来解析成需要的对象.
        //readValue 就是把 JSON 格式的字符串,转成 java 的对象
        //第一个参数,表示那个字符串进行转换,这个参数可以填写成一个 String, 也可以填一个 InputStream 对象,还可以填一个 File
        // 第二个参数,表示要把这个 JSON 格式的字符串,转成哪个 Java 对象
        User user = objectMapper.readValue(req.getInputStream(),User.class);
        resp.getWriter().write("userId: "+user.userId+", classId: "+user.classId);
    }
}

前端代码:

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>Documenttitle>
head>
<body>
<input type="text" id="userId">
<input type="text" id="classId">
<input type="button" value="提交" id="submit">



<script src="jquery.js">script>
<script>
    let userIdInput = document.querySelector('#userId');
    let classIdInput = document.querySelector('#classId');
    let button = document.querySelector('#submit');
    button.onclick = function() {
        $.ajax({
            type: 'post',
            url: 'postJson',
            contentType: 'application/json',
            data: JSON.stringify({
                userId: userIdInput.value,
                classId: classIdInput.value
            }),
            success: function(body) {
                console.log(body);
            }
        });
    }
script>
body>
html>

Servlet API_第13张图片

解析代码:

Servlet API_第14张图片

2.1 - Postman安装

Postman构造 POST 请求,JSON格式什么的是比上面更加方便的

官网下载链接:https://www.postman.com/

Servlet API_第15张图片

进入Postman

可能出现的问题:

有些人可能进这个软件之后会发现一片空白,什么反应都没有,但是任务管理器上又成功启动了的
Servlet API_第16张图片

解决办法:
Servlet API_第17张图片

变量: POSTMAN_DISABLE_GPU
值: true

最后重启Postman即可

使用教程

Servlet API_第18张图片

POST请求格式

我先在 IDEA 编写好 Servlet 代码,使用 POST 请求

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;

@WebServlet("/method")
public class MethodServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("POST 响应");
    }
}

启动 服务器:
选择想要的格式,我这里用的 POST 请求,就选POST方式,输入你要访问的地址,最后点击send,
Servlet API_第19张图片

这样就不用再去编写代码来实现 POST 请求了,非常方便
例如我还可以编写 JSON 格式

POST 请求,Json 格式

首先写好 Servlet 代码

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;

class User{
    //当前这俩属性都设置成 public, 如果设为了private,就要提供 getter and setter 效果也一样
    public int userId;
    public int classId;
}

@WebServlet("/postJson")
public class PostJsonServlet extends HttpServlet {
    //1.创建一个 jackson 核心对象
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //2.读取 body 中的请求,然后使用 ObjectMapper 来解析成需要的对象.
        //readValue 就是把 JSON 格式的字符串,转成 java 的对象
        //第一个参数,表示那个字符串进行转换,这个参数可以填写成一个 String, 也可以填一个 InputStream 对象,还可以填一个 File
        // 第二个参数,表示要把这个 JSON 格式的字符串,转成哪个 Java 对象
        User user = objectMapper.readValue(req.getInputStream(),User.class);
        resp.getWriter().write("userId: "+user.userId+", classId: "+user.classId);
    }
}

启动服务器:
Servlet API_第20张图片
Servlet API_第21张图片

非常方便!!!

Servlet API_第22张图片

3. HttpServletResponse

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

核心方法

方法 描述
void setStatus(int sc) 为该响应设置状态码。
void setHeader(String name,String value) 设置一个带有给定的名称和值的 header. 如果 name 已经存在,则覆盖旧的值.
void addHeader(Stringname, 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 中写入二进制格式数据.

注意:
1: 响应对象是服务器要返回给浏览器的内容, 这里的重要信息都是程序猿设置的. 因此上面的方法都是 “写” 方法.
2: 对于状态码/响应头的设置要放到 getWriter / getOutputStream 之前. 否则可能设置失效.

设置状态码

实现一个程序, 用户在浏览器通过参数指定要返回响应的状态码.

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;

@WebServlet("/status")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(404);
        resp.getWriter().write("hello");
    }
}

Servlet API_第23张图片
通过抓包工具观察一下:

Servlet API_第24张图片

可能有的人就会疑问了,这个404页面好像不是这样的?
那是因为服务器返回的状态码,只是在告诉浏览器,当前是个啥状态,他是不会影响显示在 body 中的内容的,就像我以 哔哩哔哩为例子:
Servlet API_第25张图片
他是可以自己设置的

自动刷新

实现一个程序,让浏览器每秒钟自动刷新,并显示时间戳

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;

@WebServlet("/autoRefresh")
public class AutoRefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("Refresh","1");
        resp.getWriter().write("timeStamp: "+System.currentTimeMillis());
    }
}

他是一秒刷新的时间戳
Servlet API_第26张图片

重定向

实现一个程序, 返回一个重定向 HTTP 响应, 自动跳转到另外一个页面.

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;

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //我们在这里返回一个 302 重定向响应,就会自动跳转到火狐
        
        resp.setStatus(302);
        resp.setHeader("Location","https://home.firefoxchina.cn/");
    }
}

Servlet API_第27张图片

简写方式:
Servlet API_第28张图片

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;

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendRedirect("https://home.firefoxchina.cn/");
    }
}

实现一个 web 表白墙

我们要实现的这个案例,他不同于`我们之前写前端的时候数据不能保存,这个表白墙就需要实现数据做到"持久化"存储,我这里用数据库来存储信息

1. 准备工作

1: 创建Maven项目
2: 创建必要的 目录,WEB-INF,web.xml

Servlet API_第29张图片

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
</web-app>

3:调整 pom.xml
引入依赖:

<dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.6.1</version>
        </dependency>
        <!-- 引入  mysql  驱动包       -->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
    </dependencies>

4: 将之前表白墙拷贝到 webapp目录中,同时也要引入 jquery

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>Documenttitle>
head>
<body>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 100%;
        }

        h3 {
            text-align: center;
            padding: 30px 0;
            font-size: 24px;
        }

        p {
            text-align: center;
            color: rgb(62, 54, 54);
            padding: 10px 0;
        }

        .row {
            width: 400px;
            height: 50px;
            margin: 0 auto;

            display: flex;
            justify-content: center;
            align-items: center;
        }

        .row span {
            width: 60px;
            font-size: 20px;
        }

        .row input {
            width: 300px;
            height: 40px;
            line-height: 40px;
            font-size: 20px;
            text-indent: 0.5em;
            /* 去掉输入框的轮廓线 */
            outline: none;
        }

        .row #submit {
            width: 300px;
            height: 40px;
            font-size: 20px;
            line-height: 40px;
            margin: 0 auto;

            color: white;
            background-color: rgb(203, 66, 157);
            /* 去掉边框 */
            border: none;

            border-radius: 10px;
        }

        .row #submit:active {
            background-color: gray;
        }
    style>
    <div class="container">
        <h3>表白墙h3>
        <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>

    <script>
        // 当用户点击 submit, 就会获取到 input 中的内容, 从而把内容构造成一个 div, 插入到页面末尾. 
        let submitBtn = document.querySelector('#submit');
        submitBtn.onclick = function() {
            // 1. 获取到 3 个 input 中的内容. 
            let inputs = document.querySelectorAll('input');
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                // 用户还没填写完, 暂时先不提交数据. 
                return;
            }
            // 2. 生成一个新的 div, 内容就是 input 里的内容. 把这个新的 div 加到页面中. 
            let div = document.createElement('div');
            div.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            div.className = 'row';
            let container = document.querySelector('.container');
            container.appendChild(div);
            // 3. 清空之前输入框的内容. 
            for (let i = 0; i < inputs.length; i++) {
                inputs[i].value = '';
            }
        }
    script>
body>
html>

Servlet API_第30张图片

2. 约定前后端交互接口

所谓 “前后端交互接口” 是进行 Web 开发中的关键环节.
具体来说, 就是允许页面给服务器发送哪些 HTTP 请求, 并且每种请求预期获取什么样的 HTTP 响 应.

1. 获取全部留言
Servlet API_第31张图片

我们期望浏览器给服务器发送一个 GET /message 这样的请求, 就能返回当前一共有哪些留言记
录. 结果以 json 的格式返回过来.

2. 发表新留言
Servlet API_第32张图片

3. 数据存入数据库

我们用数据库来存放信息

创建 messages 表;

drop table if exists messages;
create table messages (`from` varchar(1000), `to` varchar(1000), `message` varchar(3000));

4.创建 DBUtil 类

用来和数据库交互的,我用的是之前讲过的 JDBC
Mysql 的JDBC 编程

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DBUtil {
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/java102?characterEncoding=utf8&useSSL=false";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "";//自己的密码

    private volatile static DataSource dataSource = null;

    private static DataSource getDataSource() {
        if (dataSource == null) {
            synchronized (DBUtil.class) {
                if (dataSource == null) {
                    dataSource = new MysqlDataSource();
                    ((MysqlDataSource)dataSource).setUrl(URL);
                    ((MysqlDataSource)dataSource).setUser(USERNAME);
                    ((MysqlDataSource)dataSource).setPassword(PASSWORD);
                }
            }
        }
        return dataSource;
    }

    public static Connection getConnection() throws SQLException {
        return getDataSource().getConnection();
    }

    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}


5. 服务器端代码

创建 Message 类

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

创建 MessageServlet 类

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

@WebServlet("/message")
public class MessageServlet extends HttpServlet {

    //用于转换 JSON 字符串
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 处理提交消息请求
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        // 通过 ContentType 来告知页面, 返回的数据是 json 格式.
        // 有了这样的声明, 此时 jquery ajax 就会自动的帮我们把字符串转成 js 对象.
        // 如果没有, jquery ajax 就只是当成字符串来处理的~~
        save(message);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write("{ \"ok\": true }");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取到消息列表. 只要把消息列表中的内容整个的都返回给客户端即可
        // 此处需要使用 ObjectMapper 把 Java 对象, 转成 JSON 格式字符串~
        List<Message> messages = load();
        String jsonString = objectMapper.writeValueAsString(messages);
        System.out.println("jsonString: " + jsonString);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(jsonString);
    }

    private void save(Message message) {
        // 把一条消息保存到数据库中
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            // 1. 和数据库建立连接
            connection = DBUtil.getConnection();
            // 2. 构造 SQL
            String sql = "insert into messages values(?, ?, ?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1, message.from);
            statement.setString(2, message.to);
            statement.setString(3, message.message);
            // 3. 执行 SQL
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, null);
        }
    }

    private List<Message> load() {
        // 从数据库中获取到所有的消息
        List<Message> messages = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select * from messages";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                Message message = new Message();
                message.from = resultSet.getString("from");
                message.to = resultSet.getString("to");
                message.message =  resultSet.getString("message");
                messages.add(message);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, resultSet);
        }
        return messages;
    }
}


6. 调整前端页面

    <script src="jquery.js">script>

    <script>
        //加入 ajax 的代码,此处要加入的逻辑有两个部分
        //点击提交按钮的时候,ajax 要构造数据发送给服务器
        //页面加载的时候,从服务器获取消息列表,并在界面上显示

        function getMessages(){
            $.ajax({
                type: "get",
                url: "message",
                success: function (body){
                    //当前 body 已经是一个 js 对象数组了,ajax 会根据响应的 content type 来自动进行解析.
                    //如果服务器返回的 content type 已经是 application/json 了,ajax 就会把 body 自动转成 js 的对象
                    //如果客户端没有自动转,也可以通过 JSON.parse() 这个函数来手动转换

                    //依次来取数组中的每个元素
                    let container = document.querySelector('.container');
                    for(let message of body){
                        let div = document.createElement('div');
                        div.innerHTML = message.from + ' 对 ' + message.to + ' 说: ' + message.message;
                        div.className = 'row';
                        container.appendChild(div);
                    }
                }
            });
        }

        //加上函数调用
        getMessages();

        // 当用户点击 submit, 就会获取到 input 中的内容, 从而把内容构造成一个 div, 插入到页面末尾. 
        let submitBtn = document.querySelector('#submit');
        submitBtn.onclick = function() {
            // 1. 获取到 3 个 input 中的内容. 
            let inputs = document.querySelectorAll('input');
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                // 用户还没填写完, 暂时先不提交数据. 
                return;
            }
            // 2. 生成一个新的 div, 内容就是 input 里的内容. 把这个新的 div 加到页面中. 
            let div = document.createElement('div');
            div.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            div.className = 'row';
            let container = document.querySelector('.container');
            container.appendChild(div);
            // 3. 清空之前输入框的内容. 
            for (let i = 0; i < inputs.length; i++) {
                inputs[i].value = '';
            }

            //4. 把当前获取到的输入框的内容,构造成一个 HTTP POST 请求,通过 ajax 发给服务器body

            let body = {
                from: from,
                to: to,
                message: msg
            };

            $.ajax({
                type: "post",
                url: "message",
                contentType: "application/json;charset=utf8",
                data: JSON.stringify(body),
                success: function (body){
                    alert("消息发送成功!");
                },
                error: function (){
                    alert("消息发送失败!");
                }
            })
        }
    script>
body>
html>

7. 实现效果

Servlet API_第33张图片

这样就算我们刷新,他的数据就不会消失了,做到了"持久保存"

上传文件

核心方法:
HttpServletRequest 类方法

方法 描述
Part getPart(String name) 获取请求中给定 name 的文件
Collection getParts() 获取所有的文件

Part类方法

方法 描述
String getSubmittedFileName() 获取提交的文件名
String getContentType() 获取提交的文件类型
long getSize() 获取文件的大小
void write(String path) 把提交的文件数据写入磁盘文件

代码示例

实现程序,通过网页提交一个图片到服务器上

1. 创建 upload.html, 放到 webapp 目录中.

<form action="upload" enctype="multipart/form-data" method="POST">
    <input type="file" name="MyImage">
    <input type="submit" value="提交图片">
form>

上传文件一般通过 POST 请求的表单实现.
在 form 中要加上 multipart/form-data 字段.

2. 创建 UploadServlet 类

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;

@MultipartConfig
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Part part = req.getPart("MyImage");
        System.out.println(part.getSubmittedFileName());
        System.out.println(part.getContentType());
        System.out.println(part.getSize());
        part.write("d:/hahaha.jpg");
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("上传成功!");
    }
}

注意:
1:需要给 UploadServlet 加上 @MultipartConfig 注解. 否则服务器代码无法使用 getPart 方法
2: getPart 的 参数 需要和 form 中 input 标签的 name 属性对应.
3: 客户端一次可以提交多个文件. (使用多个 input 标签). 此时服务器可以通过 getParts 获取所有的Part 对象.

3. 部署程序:
Servlet API_第34张图片

你可能感兴趣的:(JavaWeb,ajax,java,服务器)