【JavaEE】如何开始基础的Servlet编程(基于Tomcat服务器)

news2024/11/26 10:20:44

如何开始最简单的Servlet编程?(基于Tomcat服务器)

知道了如何借助Tomcat开始进行最简单的Servlet编程后,我们就可以进一步完善功能制作一个基础的网站了。

在此之前我们先了解一下Servlet的生命周期。

Servlet的生命周期

初始化init -> 处理请求service -> 销毁destroy

对生命周期而言这三个方法是最重要的,标志着开头过程和结束。

Servlet的方法:

init()

HttpServlet实例化后被调用一次

service()

收到HTTP请求就会调用

destroy()

HttpServlet实例不再被使用时调用一次

doGet()

收到GET请求后,由service方法调用

doPost()

收到POST请求后,由service方法调用

doDelete() \ doPut()…

收到其他请求后,由service方法调用

在了解Servlet工作过程之前先查看一个概念:Servlet容器。Servlet代码的运行环境就是Servlet容器,说白了就是管理许多Servlet的创建、使用、销毁的东西,如Tomcat、Jetty、Jboss,一般而言是Web服务器。

Servlet工作过程:

  1. 在Web服务器接收到第一个HTTP请求的时候,会调用一次init()加载一个对应的Servlet。

  1. 随后Web服务器用多线程的方式,每个请求对应一个线程去执行service(),响应结果再返回Web服务器。

  1. 而destroy()是不一定被调用到的,因为大部分情况程序会被强制关闭,而非老老实实被销毁然后让JVM垃圾回收。

p.s. 开发中通常都是只需要重写doXXX方法,service会根据方法调用不同的doXXX。

知道了Servlet的生命周期后,我们就需要知道,我们不仅得了解如何处理请求,我们可能还要了解请求如何生成。

Servlet编程

前后端编程

前端通过ajax构建请求

后端设置响应

基本交互

逻辑:

  1. 客户端发送ajax请求后,触发服务器的doXXX

  1. 然后getWriter.write会触发客户端的ajax中success回调函数

如下:

有表白墙代码供大家练习:

<script>
        let container = document.querySelector('.container');
        let button = document.querySelector('button');
        button.onclick = function() {
            // 1. 获取到输入框的内容
            let inputs = document.querySelectorAll('input');
            let from = inputs[0].value;
            let to = inputs[1].value;
            let message = inputs[2].value;
            if (from == '' || to == '' || message == '') {
                alert('当前输入框内容为空!');
                 return;
             }
            console.log(from + ", " + to + ", " + message);
            // 2. 能够构造出新的 div, 用来保存用户提交的内容
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row';
            rowDiv.innerHTML = from + " 对  " + to + " 说:  " + message;
            container.appendChild(rowDiv);
            // 3. 提交完之后, 清空输入框的内容
            for (let i = 0; i < inputs.length; i++) {
                 inputs[i].value = '';
             }
            // 将输入框里取到的数据构造成JS对象(类似于JSON结构,都是{}+:的字符串)
            // 完整形式如:
            // let messageJson = {
            //     "from": from,
            //     "to": to,
            //     "message": message
            // };
            // 当然可以省略key对象的引号,写成下面的形式:
            let messageJson = {
                 from: from,
                 to: to,
                 message: message
             };
            $.ajax ({
                 type:'post',
                 
                // 相对路径写法:
                // 只需要写Context Path后的内容 
                 url:'mWall',
                // 绝对路径写法:
                // 写localhost:8080后的内容
                 // url:'/newCat/mWall'
                 
                 contentType: 'application/json; charset:utf8',
                // JS自带的JSON转译 转成JSON对象
                 data:JSON.stringify(messageJson),
                // 服务器是2系列的时候执行
                 success:  function(){
                     alert("提交成功!");
                 },
                // 服务器不是2系列的时候执行
                 error: function() {
                     alert("提交失败");
                 }
             });
         }
</script>  

这中间的ajax代码表示,当点击了提交按钮后,会自动构造一个POST请求提交数据,内容是将input的内容打包成json格式发送给服务器。

后端代码处理这个POST请求:

@WebServlet("/mWall")
public class MessageWall  extends HttpServlet {
//    ObjectMapper是Jackson Datamind提供用于IO Json的类
    private  ObjectMapper objectMapper = new ObjectMapper();
    List<Message> messageList = new ArrayList<>();
    @Override
    protected  void doPost(HttpServletRequest req,
                           HttpServletResponse  resp) throws ServletException, IOException {
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        messageList.add(message);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write("{ \"ok\": 1}");
     }
}
class Message {
    public  String from;
    public  String to;
    public  String message;
}

对POST的处理方式是将内容存储在List当中,并且通过getWriter().write()返回响应,响应会触发回调函数

success里的内容是一个提示,所以有

前端再整个从服务器中获取页面的操作,逻辑是构造Get请求希望服务器发送List的内容,然后通过success回调去解析:

   <script>
        // 从服务器获取页面操作:
        // 函数在页面加载时候调用 (每次加载页面  / 刷新页面时候触发)
        // 通过该函数实现从服务器获取消息列表,并显示到页面上
     
        function load() {
            $.ajax( {
                 type: 'get',
                 url: 'mWall',
                 success:  function(body) {
                     // 获取的body已经是js对象的数组
                     // ajax会自动 根据Content-Type 进行类型转换
                     // 将服务器返回的Json格式字符串时 Content-Type为application/json
                     // 则将 将json转为js对象
                     // 对响应的body进行解析
                     
                     // 遍历数组并将内容构造到页面上
                     for (let message of body) {
                         let newDiv = document.createElement('div');
                         newDiv.className = 'row';
                         newDiv.innerHTML = message.from + "  对" + message.to + " 说:" + message.message;
                         container.appendChild(newDiv);
                     }
                 }
             })
         }
        // 函数调用写在这里,页面加载的时候执行
        load();
    </script> 

后端响应如下:

@Override
protected void doGet(HttpServletRequest  req,
                      HttpServletResponse resp) throws ServletException, IOException {
    resp.setContentType("application/json;  charset=utf8");
//         这个方法可以将对象转为Json格式的字符串 这里是List的话就转成Json数组
    String  respString  = objectMapper.writeValueAsString(messageList);
    resp.getWriter().write(respString);
}

提交后抓包可以看到:

每次刷新页面就会前端就会调用load代码然后构造了请求,获取到了json键值对

使用数据库改进数据存储方式

  1. 在pom.xml中引入Maven依赖

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.31</version>
</dependency>
 
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

好像主要是使用mysql-connector-java

  1. 创建数据库表

  1. 把操作保存好

  1. 修改后端代码

  1. 和数据库建立连接:建立一个DBUtil类,封装connection、close等功能

public class DBUtil {
//    单例对象
    private  static DataSource dataSource = null;
 
    private  static DataSource getDataSource () {
        if (dataSource == null) {
            dataSource = new MysqlDataSource();
//            localhost:3306后跟的应该是数据库的名字
            ((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/MessageWall?characterEncoding=utf8&useSSL=false");
            ((MysqlDataSource)dataSource).setUser("root");
            ((MysqlDataSource)dataSource).setPassword("1234");
         }
        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();
             }
         }
     }
}
  1. 通过JDBC完成对数据库的操作

private void save (Message message) {
    Connection  connection = null;
    PreparedStatement  statement = null;
    try {
//            和数据库构建连接
        connection = DBUtil.getConnection();
//            构造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);
//            执行SQL语句:返回值表示操作影响了几行(针对table而言)
        int ret = statement.executeUpdate();
//            这里插入了一行数据,就是影响了一行
        if (ret != 1) {
            System.out.println("插入失败!");
        } else {
            System.out.println("插入成功!");
         }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
//            关闭连接
        DBUtil.close(connection, statement, null);
     }
    System.out.println("Method save  has been executed!");
}
 
private List<Message> load (){
    Connection  connection = null;
    PreparedStatement  statement = null;
    ResultSet  resultSet = null;
    List<Message> messageList = new ArrayList<>();
    try {
//            和数据库创建连接
        connection = DBUtil.getConnection();
//            构造SQL语句
        String sql = "select *  from message;";
        statement = connection.prepareStatement(sql);
//            执行SQL:executeQuery执行查询 返回结果是一个resultSet结果集
        resultSet = statement.executeQuery();
//            遍历结果集
        while (resultSet.next()) {
            Message message = new Message();
//            读取from这一列
            message.from = resultSet.getString("from");
            message.to = resultSet.getString("to");
            message.message = resultSet.getString("message");
            messageList.add(message);
         }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
//            释放资源
        DBUtil.close(connection, statement, resultSet);
     }
    System.out.println("Method load  has been executed!");
    System.out.println(messageList.toString());
    return  messageList;
}

主要是添加这两个方法,将原来的通过类中的容器进行存储改成通过数据库进行存储。

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

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

相关文章

C++ list

目录 一. 初步了解 1.构造、析构、赋值 2.容量 3.元素访问 4.增删 二. 模拟实现 框架 push_back 迭代器 带参构造、析构、赋值 增删 反向迭代器 所有代码 说白了&#xff0c;就是一个双向循环带头链表&#xff0c;由于我们在数据结构中已经学习过链表的知识&a…

macOS Big Sur 11.7.3 (20G1116) Boot ISO 原版可引导镜像

本站下载的 macOS Big Sur 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。 请访问原文链接&#xff1a;https://sysin.org/blog/macOS-Big-Sur-boot-iso/&a…

Nginx学习整理|入门记录

目录 1. Nginx概述 1.1 Nginx介绍 1.2 Nginx下载和安装 1.3 Nginx目录结构 2. Nginx命令 3. Nginx配置文件结构 4. Nginx具体应用 4.1 部署静态资源 4.2 反向代理 4.3 负载均衡 1. Nginx概述 1.1 Nginx介绍 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件…

积分电路的并联电阻、反向放大电路的并联电容的区别?

运放反相比例放大电路中反馈电阻两端经常并联一个电容&#xff0c;而运放积分电路的反馈电容上常常并联一个电阻&#xff0c;两者电路结构相似&#xff0c;如下所示&#xff08;隐去阻容值&#xff09;&#xff0c;二者有何区别呢&#xff1f;电阻、电容分别又起到什么作用&…

Catboost

CatBoost简介 CatBoost是俄罗斯搜索巨头Yandex在2017年开源的机器学习库&#xff0c;是Boosting算法的一种&#xff0c;CatBoost和XGBoost&#xff0c;Lightgbm并称为GBDT三大主流神器&#xff0c;都是在GBDT算法框架下的一种改进实现&#xff0c;XGBoost是被广泛应用于工业界…

使用jstack解决线程爆满问题

问题发现生产应用现存在问题&#xff0c;影响到系统的使用&#xff0c;前端页面只配置了35个派生指标&#xff0c;后台任务生成20000多线程任务&#xff0c;占用了全部资源&#xff0c;导致其他系统也没资源可用&#xff0c;指标工厂也无法进一步使用&#xff0c;今天上午发的死…

Email Signature Manager 9.3 Crack

概述 Email Signature Manager为所有用户创建和部署电子邮件签名 包括合并的联系方式、公司徽标、社交媒体图标 和链接&#xff0c;甚至个性化内容&#xff0c;如用户照片 创建和附加电子邮件活动&#xff0c;向所有人介绍奖项&#xff0c; 活动或促销&#xff0c;或设置运行的…

基于STM32的FreeRTOS开发(1)----FreeRTOS简介

为什么使用freertos FreeRTOS 是一个免费和开源的实时操作系统&#xff0c;它主要用于嵌入式系统。它非常轻量级&#xff0c;可以在很小的硬件资源上运行&#xff0c;因此非常适合在限制硬件资源的嵌入式系统中使用。 FreeRTOS提供了一组简单的任务管理功能&#xff0c;可以让…

基于Springboot vue前后端分离在线培训考试系统源码

# 云帆培训考试系统 管理账号&#xff1a;admin/admin 学员账号&#xff1a;person/person # 介绍 一款多角色在线培训考试系统&#xff0c;系统集成了用户管理、角色管理、部门管理、题库管理、试题管理、试题导入导出、考试管理、在线考试、错题训练等功能&#xff0c;考…

C++:运算符重载与类的赋值运算符重载函数

目录 章节知识架构 一.运算符重载 1. 运算符重载的基本概念 代码段1 2.关于运算符重载的重要语法细则 二.运算符重载在类中的使用 三.类的默认成员函数&#xff1a;重载函数(赋值运算符重载) 1.自定义重载函数 代码段2 2.编译器默认生成的重载函数 四.前置(--)和后置…

Facebook小组与主页:哪个更适合SEO?

在 SEO中&#xff0c;对于优化人员来说有两种策略&#xff1a;一种是在 Facebook组上投放广告&#xff1b;另一种则是在主页上投放广告。那么&#xff0c;这两种策略哪种更好呢&#xff1f;对于 SEO来说又有什么影响呢&#xff1f;如果你已经在 Facebook上进行了一些优化工作&a…

Python---文件操作

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 文件操作思维导图前言文件是什么文件路径文件操…

SpringBoot基础回顾:场景启动器

上一章我们回顾了 SpringBoot 的自动装配&#xff0c;以及承载自动装配的核心——自动配置类。自动配置类的定义位置通常在每个场景的 jar 包中&#xff0c;配置 spring.factories 文件中 EnableAutoConfiguration 的位置通常在相应的 autoconfigure jar 包下。本章会着重回顾和…

SpringMVC简介

SpringMVC简介什么是MVC?MVC的工作流程什么是SpringMVC&#xff1f;HelloWorld创建maven工程配置web.xml创建请求控制器配置springMVC.xml配置文件什么是MVC? MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分 M:Model,模型层,指工程中的javaBean,作用是是处理数…

恶意代码分析实战 9 隐蔽的恶意代码启动

9.1 Lab12-1 分析 查看程序的导入函数。 通过这几个函数&#xff0c;可以推断出是远程线程注入。 使用ProMon检测&#xff0c;并没有看到什么有用的信息。 使用Proexproer检查。 也没有什么有用的信息。 拖入IDA中分析一下。 将这几个字符串重命名&#xff0c;便于识别。 …

【MyBatis】| MyBatis使用⼩技巧

目录 一&#xff1a;MyBatis使用⼩技巧 1. #{}和${} 2. typeAliases 3. mappers 4. IDEA配置⽂件模板 5. 插⼊数据时获取⾃动⽣成的主键 一&#xff1a;MyBatis使用⼩技巧 1. #{}和${} #{}&#xff1a;先编译sql语句&#xff0c;再给占位符传值&#xff0c;底层是Prepar…

【C语言进阶】一文带你学会C语言文件操作

前言 我们前面学习结构体时&#xff0c;写了通讯录的程序&#xff0c;当通讯录运行起来的时候&#xff0c;可以给通讯录中增加、删除数据&#xff0c;此时数据是存放在内存中&#xff0c;当程序退出的时候&#xff0c;通讯录中的数据自然就不存在了&#xff0c;等下次运行通讯录…

Python---自动生成二维码

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 自动生成二维码 二维码的本质上&#xff0c;就…

人工智能学习06--pytorch05--torchvision中的数据集使用DataLoader的使用

torchvision中的数据集使用 test_set的class属性 把数据集每一部分都变成tensor类型 现在输出的就是tensor数据类型了 DataLoader的使用 batch_size 一摞牌中&#xff0c;每次抓几张shuffle 打乱&#xff0c;第二次打牌前&#xff0c;牌的顺序要跟第一次不一样&#xff0…

【JavaSE】一文看懂构造器/构造方法(Cunstructor)

&#x1f331;博主简介&#xff1a;大一计科生&#xff0c;努力学习Java中!热爱写博客~预备程序媛 &#x1f4dc;所属专栏&#xff1a;Java冒险记【从小白到大佬之路】 ✈往期博文回顾: 【JavaSE】保姆级教程|1万字10张图学会类与对象–建议收藏 &#x1f575;️‍♂️近期目标…