Servlet

Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app.

Servlet 主要做的工作:
①允许程序猿注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类中的一些代码.
②帮助程序猿解析 HTTP 请求, 把 HTTP 请求从一个字符串解析成一个 HttpRequest 对象.
③帮助程序猿构造 HTTP 响应. 程序猿只要给指定的 HttpResponse 对象填写一些属性字段, Servlet就会自动的安装 HTTP 协议的方式构造出一个 HTTP 响应字符串, 并通过 Socket 写回给客户端.

Maven

maven,这是我们在 写 Servlet 代码 的时候,所需要用到的一个工具。也是Java 世界中的一个非常知名的 “工程管理工具 / 构建工具”。

使用

一直next,知道让你输入项目名称。

其中src/main/java用来存放我们的java文件

src/main/resource用来放一些我们依赖的资源

pom.xml是maven项目配置的核心文件

idea右侧:

 

 我们最常用的还是package,打包。

引入依赖

在maven的中央仓库:https://mvnrepository.com/中,以引入mysql为例

搜索mysql,下拉找到这个

 复制到pom.xml中,前提是得先加这个

如果引入的依赖很多,也只需要放在dependencies下即可,多个dependency是同级的。

初次引入依赖,是红色的,可以手动刷新一下让idea下载

Servlet

第一个servlet程序

①创建一个maven文件,②引入servlet依赖(前面讲了,省略)

 ③创建目录结构

新建如上目录,名字不能错,大小写也一样,这是tomcat默认的一种规定。

web.xml中文件是固定的,可以复制下来以后都能用:

<!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>

 ④编写servlet代码

其中的req代表一个请求,resp代表一个响应 。

一个服务器的工作流程可以分为三个部分:

①接收请求并解析

②根据请求计算响应

③根据响应数据返回客户端

而其中的①③tomcat已经为我们做好了,我们只需做好其中的第二步即可

 

getWrite返回一个字节流对象,后面的write将其写入到响应的body中。

但是一个tomcat会收到形形色色的请求,如何在你想要收到响应的页面中调用这个方法呢。

 我们需要加上如上一句,代表只有路径为/Hello的,tomcat才会调用这个方法。

⑤打包

在打包之前,我们需要处理一下,war包是tomcat是专门搞得,因此使用它

build是指定打包后的包名

双击运行.

 

 

此时,target目录下就出现了包

 ⑥部署

把war包拷贝到tomcat的webapps目录下

启动tomcat。

输入一下内容:ip地址 + war名 + 指定路径名

我的是http://127.0.0.1:8080/hello/Hello

 tomcat上是可以部署多个网站的,而每个网站(war包)的第一级路径不同,因此指定路径名是可以重复的,但在同一个网站(war包)下的指定路径名是不能重复的,否则会报错。

smart tomcat

如果我们要修改我们所写的代码,那么需要重新进行上面的⑤⑥操作,这样重复性的动作是最烦的,因此有了这个插件,在idea中下载好这个插件后,我们只需一点就能重新部署好。

下完后这里可以使用了

配置路径一直到这。

使用:

 

tomcat工作原理(伪代码):

class Tomcat {
    // 用来存储所有的 Servlet 对象
    private List<Servlet> instanceList = new ArrayList<>();
    public void start() {
// 根据约定,读取 WEB-INF/web.xml 配置文件;
// 并解析被 @WebServlet 注解修饰的类
// 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类.
        Class<Servlet>[] allServletClasses = ...;
// 这里要做的的是实例化出所有的 Servlet 对象出来;
        for (Class<Servlet> cls : allServletClasses) {
// 这里是利用 java 中的反射特性做的
// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的
// 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是
// 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。
            Servlet ins = cls.newInstance();
            instanceList.add(ins);
        }
// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;
        for (Servlet ins : instanceList) {
            ins.init();
        }
// 利用我们之前学过的知识,启动一个 HTTP 服务器
// 并用线程池的方式分别处理每一个 Request
        ServerSocket serverSocket = new ServerSocket(8080);
// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况
        ExecuteService pool = Executors.newFixedThreadPool(100);
        while (true) {
            Socket socket = ServerSocket.accept();
// 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
            pool.execute(new Runnable() {
                doHttpRequest(socket);
            });
        }
// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;
        for (Servlet ins : instanceList) {
            ins.destroy();
        }
    }
    public static void main(String[] args) {
        new Tomcat().start();
    }
}
小结:Tomcat 的代码中内置了 main 方法. 当我们启动 Tomcat 的时候, 就是从 Tomcat 的 main 方法开始执行的.被 @WebServlet 注解修饰的类会在 Tomcat 启动的时候就被获取到, 并集中管理.
Tomcat 通过 反射 这样的语法机制来创建被 @WebServlet 注解修饰的类的实例.
这些实例被创建完了之后, 会点调用其中的 init 方法进行初始化. (这个方法是 HttpServlet 自带的,我们自己写的类可以重写 init)这些实例被销毁之前, 会调用其中的 destory 方法进行收尾工作. (这个方法是 HttpServlet 自带的,我们自己写的类可以重写 destory)
Tomcat 内部也是通过 Socket API 进行网络通信.Tomcat 为了能同时相应多个 HTTP 请求, 采取了多线程的方式实现. 因此 Servlet 是运行在 多线程环境 下的.
 

 Servlet Api

关于servlet Api我们只需了解三个类即可

HttpServlet核心方法

红色框中代表的是常用方法。

doGet演示

/**
 * 处理Get方法请求
 */
@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响应");
    }
}

如果直接在浏览器上输入网址,那么得到的请求将会是一个Get请求,会产生错误405,因此我们需要构造一个post请求。而setContentType让浏览器通过这个格式来读取数据,因为浏览器默认是GBK编码,而idea是utf8。不使用这个会导致乱码。

我们通过ajax来构造,新建一个html文件,而如果要使用ajax,我们需要引入jquery模块。

<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>

 HTML文件通过url将method服务器代码绑定在了一起,因此当我们访问/hello/MethodServlet.html文件时,其实就是在访问/hello/method,因此会触发服务器代码。

但直接访问/hello/method又不行,因为输入url是Get请求,我们构造的是POST请求。

HttpServletRequest核心方法

@WebServlet("/ShowServlet")
public class ShowRequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        StringBuilder sb = new StringBuilder();
        //获取协议的名称和版本
        sb.append(req.getProtocol());
        sb.append("<br>");
        //获取HTTP方法名称
        sb.append(req.getMethod());
        sb.append("<br>");
        //获取网络路径的一部分
        sb.append(req.getRequestURI());
        sb.append("<br>");
        //返回第一级路径
        sb.append(req.getContextPath());
        sb.append("<br>");
        //返回查询字符串的信息
        sb.append(req.getQueryString());
        sb.append("<br>");
        //获取请求报头,请求报头也是一个key/value结构
        //此方法是获取请求报头中所有的key值,返回一个枚举类型
        Enumeration<String> e = req.getHeaderNames();
        while(e.hasMoreElements()){
            String k = e.nextElement();
            //通过报头key值获取value值
            String v = req.getHeader(k);
            sb.append(k + ": " + v + "<br>");
        }
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write(sb.toString());
    }
}

 

获取Get请求中的查询字符串

 

获取Post中的查询字符串

POST 请求 的body 格式

1、x-www-form-urlencoded
2、json

①如何在前端构造一个x-www-form-urlencoded请求

1、form表单

创建一个html文件,写入以下内容

 

 

点击提交后我们也可以通过fiddler抓包,能看的更清楚

 

 

2、postman

②json格式构造

这里不向前面那么简单了,既要处理前端还要处理后端

对于前端,我们还是使用ajax来构造post的body部分:

对于POST这样的请求ajax允许我们使用data 这个属性来构造body部分,stringify试讲一个js对象转换成一个字符串,而这个字符串就是我们所需的json格式

ps:在ajax中,key值已经默认是字符串类型了,因此加不加引号都是一样的。

后端我们则需要使用一个依赖,jackson

<!-- 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>

 继续通过fiddler抓包:

格式已经对了~

而如果我们使用了postman,那就更加简单了,我们不用写这个html文件了

重新写一个测试类(和上文代码一样):

每次修改增删都需要重新启动一下tomcat,否则一直找不到错误可是很烦的~

 HttpServletResponse核心方法

状态码(setStatus)

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

 

打开fiddler抓包

 如果改成404试试:

 一样能返回状态码~

服务器返回的状态码只是在告诉浏览器,当前的响应是一个什么样子的状态,并不影响浏览器照常显示body内容

自动刷新(setHeader)

@WebServlet("/Header")
public class SetHeader extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("Refresh","3");
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("时间: " + System.currentTimeMillis());
    }
}

 

 

打开fiddler转包,可以看到自动刷新了许多次

重定向(setHeader)

@WebServlet("/123")
public class SetHeader2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(302);
        resp.setHeader("Location","https://www.sogou.com");
    }
}

更简便做法

@WebServlet("/123")
public class SetHeader2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*resp.setStatus(302);
        resp.setHeader("Location","https://www.sogou.com");*/
        resp.sendRedirect("https://www.sogou.com");
    }
}

Servlet实战:留言墙

懒得解释了,看看代码把

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

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

    private ObjectMapper objectMapper = new ObjectMapper();

    // 这个是重启服务器后留言就无了
    //改成数据库, 就不需要这个变量了,已经能持久化保存了
    // private List<Message> messages = new ArrayList<>();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 处理提交消息请求
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        // 最简单的保存方法就是保存到内存中.
        // messages.add(message);
        // 通过 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 message 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 message";
            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;
    }
}
public class DBUtil {
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8&useSSL=false";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "123456";

    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();
            }
        }
    }
}
<!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>
</head>
<body>
    <div class="container">
        <h1>留言墙</h1>
        <p>输入后点击提交 会将信息显示在表格中</p>
        <div class="row">
            <span>谁 :</span>
            <input type="text" class="edit" >
        </div>
        <div class="row">
            <span>对谁 :</span>
            <input type="text" class="edit">
        </div>
        <div class="row">
            <span>说什么 :</span>
            <input type="text" class="edit">
        </div>
        <div class="row">
            <button class="submit">提交</button>
        </div>
    </div>

    
    <style>
        h1{
            text-align: center;
            padding: 20px 0;
        }

        p{
            text-align: center;
            color: rgb(128,128,128)
        }

        *{
            margin: 0;
            padding: 0;
        }

        span{
            width: 100px;
            line-height: 40;
        }
        .row{
            align-items: center;
            display: flex;
            justify-content: center;
            height: 40px;
        }

        .container {
            width: 400px;
            margin: 0 auto;
        }

        .row .submit {
            width: 304px;
            height: 40px;
            background-color: orange;
        }
        .row .edit{
            width: 200px;
            height: 30px;
        }
        .submit:active{
            background-color: darkgray;
        }
    </style>
    <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
    <script>

        function getMessage(){
            $.ajax({
                type: 'get',
                url: 'message',
                success:function(body){
                    let container = document.querySelector('.container');
                    for(let message of body){
                        /* 创建一个新的div来存放信息 */
                        let newDiv = document.createElement('div');
                        newDiv.innerHTML = message.from + "对 " + message.to + "说 :" + message.message;
                        newDiv.className = 'row';
                        container.appendChild(newDiv);

                    }
                }
            })
        }
        getMessage();

        let submitB = document.querySelector('.submit');
        submitB.onclick = function(){
            /* 获取输入框中的数据 */
            let inputs = document.querySelectorAll('input')
            let from = inputs[0].value;
            let to = inputs[1].value;
            let message = inputs[2].value;
            if(from == '' || to == '' || message == ''){
                return;
            }
            /* 这是一个测试 */
            /* console.log(from + "" + to + "" +message) */
            /* 生成一个新的div 内容就是输入框中的内容 */
            let newDiv = document.createElement('div');
            newDiv.innerHTML = from + "对 :" + to + "说 :" + message;
            newDiv.className = 'row';
            // 将新建节点,挂在 container 这个节点下面
            let container = document.querySelector('.container');
            container.appendChild(newDiv);
            /* 清空输入框 */
            for(let i = 0 ; i < inputs.length ; i++){
                inputs[i].value = "";
            }
            /* 把当前的获取到的输入框内容构造成一个http请求 */
            let body = {
                "from":from,
                "to":to,
                "message":message,
            }
            $.ajax({
                type: 'post',
                url: 'message',
                contentType: "application/json; charset=utf8",
                data: JSON.stringify(body),
                success:function(body){
                    alert("消息提交成功");
                },
                error:function(body){
                    alert("消息提交失败");
                }
            });

        }
    </script>
</body>
</html>

emm,记得mysql建个库和表。

效果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

海绵宝宝养的的小窝

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

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

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

打赏作者

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

抵扣说明:

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

余额充值