Servlet_JSP

news2024/11/20 4:38:27

1.一些回顾

对于Tomcat部署中 我们有一些补充的点需要在此说明一下
1.如果我们想要查询MINEType的话 可以到TOMCAT_HOME/conf/web.xml中进行查询 里面记录了不同类型对应的MINEType
2.我们客户端发送请求数据给服务器之后 服务器会调用父类中的service方法 然后在内部决定调用doGet还是doPost方法 我们也可以重写service方法自定义service的内部逻辑

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/hello3/test" method="get">
    <div>账号 <input type="text" name="username"></div>
    <div>密码 <input type="text" name="password"></div>
    <div><button type="submit">登录</button></div>
</form>
</body>
</html>
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("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(this + "_service");
    }
}

在这里插入图片描述
3.通过response获取的字符输出流PrintWriter不需要手动关闭 当service调用完毕以后就会自动关闭该输出流
4.alt+d启动tomcat之后的默认操作中 常用的有两个:update classes and resourses、redeploy 前者主要为针对只修改的servlet进行重新加载到项目的操作 而后者则为针对添加成员的servlet进行重新加载到项目的操作

2.Servlet

所谓Servlet 其实就是server(服务) applet(小程序)的简称 即小服务程序的意思
当一个请求数据发送到服务器之后 交由一个servlet处理 他会调用其中的service方法 然后在决定要调用doGet还是doPost方法

默认情况下 一个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("/test")
public class TestServlet extends HttpServlet {
    public TestServlet(){
        System.out.println(this + "_构造方法");
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(this + "_service");
    }
}

以下是第一次发送数据以后的结果
在这里插入图片描述
接着我们修改了service的代码 然后重新加载到项目中 查看哈希值是否改变

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("/test")
public class TestServlet extends HttpServlet {
    public TestServlet(){
        System.out.println(this + "_构造方法");
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(this + "_service123");
    }
}

在这里插入图片描述
从结果中就可以看出 哈希值并没有改变 说明两次发送数据用的对象是同一个对象 而且对象创建的时机是在第一次接收客户端请求数据之时(我可以通过内存层面解释一下这个问题:修改代码修改的是代码区中的内容 而对象存放的位置是堆空间 修改代码区并不会影响堆空间的东西 既然对象没有发生改变 那么就一直沿用即可)
但是如果我们是为servlet类增删成员而通过redeploy的方式重新加载servlet类的话 那么每一次的redeploy中服务器都会销毁旧的servlet对象 同时创建出新的servlet对象 所以如果是每一次的redeploy的话 那么就会根据同一个servlet类创建出不同的对象

1.注意

有些人可能会这么想 就是既然服务器只会根据一个servlet类创建一个实例的话 那么该servlet类采用的就是单例模式 这种想法是错误的
我们先来看一下单例模式的实现

public class Person {
    // 构造方法私有化
    private Person(){}
    // 提供一个私有静态的对象 静态是为了确保程序运行过程中只有一份内存 私有则是为了不被外界访问
    private static Person instance = new Person();
    // 提供一个共有静态的获取实例方法 静态是为了保证通过类访问
    public static Person getInstance(){
        return instance;
    }
}
public class Main {
    public static void main(String[] args) {
        Person p1 = Person.getInstance();
        Person p2 = Person.getInstance();
        System.out.println(p1);// Person@1b6d3586
        System.out.println(p2);// Person@1b6d3586
    }
}

从结果证明 上述代码确实实现了一个单例模式 所谓单例模式就是根据一个类只能创建一个实例 我们就可以查看一下servlet类是否可以创建多个实例即可证明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("/test")
public class TestServlet extends HttpServlet {
    public TestServlet(){
        System.out.println(this + "_构造方法");// TestServlet@39ac9b6d_构造方法
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        TestServlet ts1 = new TestServlet();// TestServlet@2c4c84b_构造方法
        TestServlet ts2 = new TestServlet();// TestServlet@3282dd6a_构造方法
        System.out.println(ts1);// TestServlet@2c4c84b
        System.out.println(ts2);// TestServlet@3282dd6a
    }
}

从ts1和ts2打印的结果不同来看 就可以证明servlet并非采取单例模式 而是可以根据一个该类创建出多个实例

当然 你也可以为同一个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({"/test1", "/test2"})
public class TestServlet extends HttpServlet {
    public TestServlet(){
        System.out.println(this + "_构造方法");// TestServlet@39ac9b6d_构造方法
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(this + "_service");
    }
}

这样 你可以通过不同的请求路径和服务器进行交互 并且如果只是修改代码重新加载的程度 服务器也只会创建一次servlet实例 而且时机为第一次接收请求数据的时候

还有一个需要注意的地方就是:不要在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({"/test1", "/test2"})
public class TestServlet extends HttpServlet {
    private int age = 10;
    public TestServlet(){
        System.out.println(this + "_构造方法");// TestServlet@39ac9b6d_构造方法
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        age++;
    }
}

如果同时有多个请求数据发送给服务器的话 并且同时执行到age++;语句 假设同时发送两次 那么实际的结果11可能和预期结果12是不一致的 这就是所谓的线程安全问题 解决的方案就是将成员变量从可写改成可读 即声明为常量即可

还有一个值得注意的地方就是:http请求的默认端口号为80 如果你的tomcat服务器端口号设置为80的话 那么客户端发送请求过程中就可以省略端口号(会自动添加) 我们重新加载的方式选择restart server 这样才能保留端口号的修改
在这里插入图片描述

3.crm项目

所谓crm 就是customer relationship management的简称 即用户关系管理
我们想要实现的一个项目的效果其实就是当我们点击登录 如果登录成功 就会跳转到展示用户信息的页面 如果登录失败 就会提醒我们重新登陆 并且点击可以实现跳转到登录界面的效果
而且在servlet代码中 我们需要单独封装登录成功、登录失败以及获取储存用户信息的功能
并且我们可以直接创建一个servlet类 而并非class类 该类已经有了基本的框架 包含doGet、doPost以及WebServlet等信息
我们接下来就来实现一下该项目
LoginServlet

package com.mj.servlet;

import com.mj.bean.Customer;

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.PrintWriter;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置请求数据的解码方式为UTF-8
        request.setCharacterEncoding("UTF-8");
        // 接收请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // 然后设置响应数据的内容格式 由于展示的为html页面 因此MINEType为text/html 而编码方式设置为UTF-8
        response.setContentType("text/html;charset=utf-8");
        // 然后定义字符输出流 用于输出响应数据
        PrintWriter out = response.getWriter();
        // 判断 如果登录成功 执行登录成功的逻辑 登陆失败则执行登录失败的逻辑
        if(username.equals("123") && password.equals("123")){
            success(out);
        }else{
            fail(out);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
    // 封装获取储存用户信息的逻辑
    private List<Customer> getCustomers(){
        // 定义一个list集合 用于存放用户信息
        List<Customer> customers = new ArrayList<>();
        // 假设我们拥有的用户信息数量为10个
        for(int i = 0; i < 10; ++i){
            // 性别奇数为男 偶数为女
            customers.add(new Customer("张三", "10086", ((i & 1) == 1 ? "男" : "女")));
        }
        return customers;
    }
    // 封装登录成功的逻辑
    private void success(PrintWriter out){
        // 先展示登录成功信息
        out.write("<h1>登录成功</h1>");
        out.write("<table>");
        out.write("<thead>");
        out.write("<tr>");
        out.write("<th>姓名</th>");
        out.write("<th>电话</th>");
        out.write("<th>年龄</th>");
        out.write("</tr>");
        out.write("</thead>");
        out.write("<tbody>");
        // 获取存放用户信息的列表
        List<Customer> customers = getCustomers();
        for(Customer customer: customers){
            out.write("<tr>");
            out.write("<td>" + customer.getName() + "</td>");
            out.write("<td>" + customer.getPhone() + "</td>");
            out.write("<td>" + customer.getGender() + "</td>");
            out.write("</tr>");
        }
        out.write("</tbody>");
        out.write("</table>");
    }
    // 封装登录失败的逻辑
    private void fail(PrintWriter out){
        // 先展示登录失败信息
        out.write("<h1>登录失败</h1>");
        // 在展示重新登录 并且跳转到登录界面
        out.write("<a href=\"http://localhost:8080/crm/login.html\">重新登录</a>");
    }
}

Customer

package com.mj.bean;

public class Customer {
    // 定义三个私有成员变量 分别为:姓名、电话以及性别
    private String name;
    private String phone;
    private String gender;
    // 定义无参和有参的构造方法
    public Customer(String name, String phone, String gender) {
        this.name = name;
        this.phone = phone;
        this.gender = gender;
    }

    public Customer() {
    }
    // 定义每一个成员变量的getter和setter方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

login

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/crm/login" method="post">
    <div>账号 <input type="text" name="username"></div>
    <div>密码 <input type="text" name="password"></div>
    <div><button type="submit">登录</button></div>
</form>
</body>
</html>

文件架构
在这里插入图片描述
测试之后 可以发现 确实能够满足我们的需求 但是如果要将网页作为响应数据返回给客户端的话 那么就会出现大量可读性极差、难以维护的字符串拼接代码 而且我们的用户信息都是我们自己写死的(一般的解决思路就是将他们放置在数据库中)

4.Servlet处理请求的常见过程

在这里插入图片描述

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

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

相关文章

用Jenkins Gerrit-Trigger插件实现提交gerrit后自动启动编译验证-解决编译依赖问题

用Jenkins Gerrit-Trigger插件实现提交gerrit后自动启动编译验证-CSDN博客讨论了如何利用插件在提交gerrit的时候自动出发一个jenkins job编译固件,但是没有解决编译依赖问题。本文提出一种解决方案 首先在git commit -m ""的时候在commit message中设置Depend-On:…

ControlNet官方资源链接【ControlNet论文原文】【持续更新中~】

ControlNet官方资源链接 ControlNet论文原文&#xff1a;https://arxiv.org/abs/2302.05543ControlNet官方GitHub&#xff1a;https://github.com/lllyasviel/ControlNetControlNet 1.1官方GitHub&#xff1a;https://github.com/lllyasviel/ControlNet-v1-1-nightlyControlNe…

深度学习之基于Vgg16卷积神经网络印度交警手势识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 随着智能交通系统的不断发展&#xff0c;手势识别技术在其中扮演着越来越重要的角色。特别是在印度等…

CVE-2017-11882分析和白象样本分析

CVE-2017-11882分析和白象样本分析 CVE-2017-11882是微软公布的一个远程代码执行漏洞&#xff0c;漏洞是由模块EQNEDT32.EXE公式编辑器引起&#xff0c;该模块在Office的安装过程中被默认安装&#xff0c;该模块以OLE技术&#xff08;Object Linking and Embedding&#xff0c…

《网络安全---frida应用实践---某付费视频应用一举拿下》

文章目录 目标应用环境:步骤1、查壳2、定位付费界面布局3、找到可疑方法4、那就看下请求信息吧,看下有没有思路5、其他请求(列表,视频信息,获取播放url)6、请求参数加密算法7、图片信息解密8、数据请求关键点9、以上都是废话10、直接找关键hook点总结相关源码1、文章仅供…

2.初探MPI——点对点通信(阻塞)

系列文章目录 初探MPI——MPI简介初探MPI——&#xff08;阻塞&#xff09;点对点通信初探MPI——&#xff08;非阻塞&#xff09;点对点通信初探MPI——集体通信 文章目录 系列文章目录前言一、Sending & Receiving message1.1 简介1.2 发送消息1.3 接收消息1.4 MPI 发送…

AI智能名片商城小程序构建企业级私域的IMC模型:IP、MarTech与Content的深度融合

在数字化营销的新时代&#xff0c;为企业定制开发的AI智能名片B2B2C商城小程序&#xff0c;结合我们丰富的私域运营实践&#xff0c;我们深刻领悟到构建企业级私域的三大核心要素&#xff1a;IP&#xff08;企业人设&#xff09;、MarTech&#xff08;营销技术&#xff09;和Co…

【自动化测试】使用MeterSphere进行接口测试

一、接口介绍二、接口测试的过程三、接口自动化测试执行自动化流程 四、接口之间的协议HTTP协议 五、 接口测试用例设计接口文档 六、使用MeterSphere创建接口测试创建接口定义设计接口测试用例 一、接口介绍 自动化测试按对象分为&#xff1a;单元测试、接口测试、UI测试等。…

C语言/数据结构——每日一题(移除链表元素)

一.前言 今天在leetcode刷到了一道关于单链表的题。想着和大家分享一下。废话不多说&#xff0c;让我们开始今天的知识分享吧。 二.正文 1.1题目要求 1.2思路剖析 我们可以创建一个新的单链表&#xff0c;然后通过对原单链表的遍历&#xff0c;将数据不等于val的节点移到新…

【Java从入门到精通】Java 流(Stream)、文件(File)和IO

Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。 Java.io 包中的流支持很多种格式&#xff0c;比如&#xff1a;基本类型、对象、本地化字符集等等。 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据&#xff0c;输出流…

获取淘宝商品销量数据接口

淘宝爬虫商品销量数据采集通常涉及以下几个步骤&#xff1a; 1、确定采集目标&#xff1a;需要明确要采集的商品类别、筛选条件&#xff08;如天猫、价格区间&#xff09;、销量和金额等数据。例如&#xff0c;如果您想了解“小鱼零食”的销量和金额&#xff0c;您需要设定好价…

扫雷实现详解【递归展开+首次必展开+标记雷+取消标记雷】

扫雷 一.扫雷设计思路二.扫雷代码逐步实现1.创建游戏菜单2.初始化棋盘3.打印棋盘4.随机布置雷5.统计周围雷的个数6.递归展开棋盘7.标记雷8.删除雷的标记9.保证第一次排雷的安全性棋盘必定展开10.排查雷11.判断输赢 三.扫雷总代码四.截图 一.扫雷设计思路 1.创建游戏菜单。  2.…

Javascript基础(三)

Javascript基础&#xff08;一&#xff09; Javascript基础&#xff08;二&#xff09; 引用数据类型 在之前的文章中&#xff0c;我们提及了与基本数据类型并列的引用数据类型&#xff0c;当时提到引用数据类型大致分为三类&#xff1a;数组Array&#xff0c;函数Function&a…

13.1 QQ邮箱

1. 邮箱发送 2. 准备工作 3. 整合SpringBoot 3.1 配置 依赖引入 <!-- 邮件服务--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>application.…

浅析扩散模型与图像生成【应用篇】(二十)——TiNO-Edit

20. TiNO-Edit: Timestep and Noise Optimization for Robust Diffusion-Based Image Editing 该文通过对扩散模型中添加噪声的时刻 t k t_k tk​和噪声 N N N进行优化&#xff0c;提升SD等文生图模型的图像编辑效果。作者指出现有的方法为了提升文生图模型的图像编辑质量&…

VISO流程图之子流程的使用

子流程的作用 整个流程图的框图多而且大&#xff0c;进行分块&#xff1b;让流程图简洁对于重复使用的流程&#xff0c;可以归结为一个子流程图&#xff0c;方便使用&#xff0c;避免大量的重复性工作&#xff1b; 新建子流程 方法1&#xff1a; 随便布局 框选3 和4 &#…

基于YOLOv8的水稻虫害识别系统,加入BiLevelRoutingAttention注意力进行创新优化

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文摘要&#xff1a;基于YOLOv8的水稻虫害识别&#xff0c;阐述了整个数据制作和训练可视化过程&#xff0c;并加入BiLevelRoutingAttention注意力进行优化&#xff0c;最终mAP从原始的 0.697提升至0.732 博主简介 AI小怪兽&#xff…

MATLAB - 自定义惯性矩阵

系列文章目录 前言 一、关键惯性约定 Simscape 多体软件在惯性定义中采用了一系列约定。请注意这些约定&#xff0c;因为如果手动进行惯性计算&#xff0c;这些约定可能会影响计算结果。如果您的惯性数据来自 CAD 应用程序或其他第三方软件&#xff0c;这些约定还可能影响到您需…

【计算机毕设】在线商城系统设计与开发 - 免费源码(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 本项目旨在设计并实现一个在线商城系统&#xff0c;提供商品展示、购物车管理、订单管理等功能&#xff0c;为用户提供便捷的购物体验&#xff0c;…

并发-启动线程的正确姿势

目录 启动线程的正确姿势 Start方法原理解读 Run方法原理解读 常见问题 启动线程的正确姿势 start()与run()方法的比较测试结果可以看出&#xff0c;runnable.run()方法是由main线程执行的&#xff0c;而要子线程执行就一定要先调用start()启动新线程去执行run方法并不能成…