Tomcat和Servlet

目录

Tomcat服务器

Tomcat的安装目录

 静态文档和动态文档

部署静态文档

servlet

maven

servlet程序

Smart Tomcat

 Servlet的常见错误

Servlet的API

javax.servlet包

javax.servlet.http包

 Servlet的生命周期

Tomcat的运行过程

 Servlet的生命周期

HttpServlet 类

HttpServletRequest类

HttpServletResponse类


Tomcat服务器

Tomcat是HTTP的服务器,用于接收HTTP 请求,分析请求得出响应,然后将响应返回给浏览器

Tomcat的安装目录

 /webapps目录内,存放Tomcat服务器内的所有web程序,其内的Root目录是默认的web应用程序,默认访问应用程序使用的URL为http://127.0.0.1:8080

配置Tomcat的服务端口

默认端口是8080,可以进行修改

 启动Tomcat

双击bin目录下的startup

 

 出现这个表示成功启动,乱码原因:Tomcat使用UTF-8编码,而windows使用GBK

浏览器输入http://127.0.0.1:8080/,就可以看到Tomcat的欢迎界面

静态文档和动态文档

静态文档指的是在用户浏览的过程中,web呈现的页面不会有任何的改变,是固定死的;而动态文档是可以动态生成的

部署静态文档

在Root目录下,创建一个.html的文件

 浏览器输入http://127.0.0.1:8080/hello.html,就可以在浏览器查看这个文件

理论上其他用户就可以访问这个网址,看到这个界面;但是当前我们自己的IP是私有IP地址,不可以在网络上传播,需要借助外网IP

更多情况下,我们的网站内容是可变的,需要实现动态页面

这就要使用到我们Tomcat提供的API,也即是servlet

servlet

maven

Maven 是一个项目管理工具,可以对 Java 项目进行自动化的构建和依赖管理;我们的代码要依赖很多第三方库,Maven可以帮助我们自动管理依赖

使用idea创建maven项目

例如配置mysql的相关依赖,打开https://mvnrepository.com/,maven中央仓库,选择mysql的版本,复制maven内容

 拷贝maven的内容到pom.xml中

  如果没有自动从中央仓库下载依赖,可以手动刷新,启动下载

servlet程序

 Tomcat 作为服务器,负责接收客户请求,并做出响应

Tomcat把接收到的请求传送给Servlet,并将 Servlet 的响应传送回给客户;而Servlet是一种运行在支持Java语言的服务器上的组件

创建Servlet的流程

1、创建maven程序

2、引入servlet的依赖

Apache Tomcat® - Which Version Do I Want?

根据Tomcat的版本,从maven中央仓库选择对应servlet的版本,拷贝maven内容到代码中,idea从中央仓库下载依赖

3、 创建目录结构

我们需要在当前的目录结构上新增些结构

 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>

4、编写servlet代码

@WebServlet("/hi")//当url路径包含hi目录时,才会触发这个类
public class Hello extends HttpServlet {
    //继承HttpServlet类,这个类包含的方法之一就是doGet(),表示服务器在接收到Get请求之后,执行的操作
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //HttpServletRequest:请求类  HttpServletResponse:响应类
        //Tomcat服务器的控制台的内容
        System.out.println("服务器hello");
        //2、当服务器接收到请求之后,做出的响应
        resp.getWriter().write("hello");
    }
}

5、打包,将这个maven程序打包,部署到Tomcat服务器上

在pom.xml中做如下修改

<!--    将程序打包为war包-->
    <packaging>war</packaging>
<!--    程序打包之后的名称-->
    <build>
        <finalName>
            hellotest
        </finalName>
    </build>

打包支持war包和jar包,war包可以说是服务于Tomcat的,不仅包含.class文件,还包含css,js和其他配置文件;而jar包 包含.class文件

双击maven操作界面的packet,进行打包

 6、将包部署到Tomcat服务器上

将war包拷贝到Tomcat服务器上的webapps目录里

启动Tomcat服务器

7、检验是否成功

URL包含两级路径,输入http://127.0.0.1:8080/hellotest/hi

页面呈现

这里的第一级路径(context path)是"/hellotest",就是打包时,pom.xml中的名称

第二级目录(Servlet path)是"/hi",就是java中的@WebServlet("/hi")语句指定的路径,这个语句将URL中的/hi目录和java类关联,只要发送的请求包含/hi目录,才会被这个类接收请求,从而发送响应

Smart Tomcat

idea的一个插件,实现将maven程序自动打包并配置到Tomcat服务器中。(不是真的会打一个war

包,拷贝到webapps目录内,smart Tomcat简化这个过程,直接让Tomcat服务器从指定路径访问了程序,实现了逻辑上的配置)

 Servlet的常见错误

1、404:表示资源不存在

  • 缺少第一级路径(context path)

  •  缺少第二级路径(servlet path)
  • 第一级路径错误或者第二级路径错误
  •  程序要加载web.xml中的资源,可能是web.xml为空

 2、405:表示方法不支持

我们以上重写的是doGet方法,也就是Get请求

一般执行Get请求的操作:

  • 浏览器输入url访问
  • 页面跳转
  • form表单或者ajex构造get方法

一般执行Post请求的操作:

  • form表单或者ajex构造Post方法
@WebServlet("/hi")//当url路径包含hi目录时,才会触发这个类
public class Hello extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //HttpServletRequest:请求类  HttpServletResponse:响应类
        //Tomcat服务器的控制台的内容
        System.out.println("服务器hello");
        //2、当服务器接收到请求之后,做出的响应
        resp.getWriter().write("hello");
    }
}

当前是Post方法,我们直接输入http://localhost:8080/hello/hi,执行的是Get方法,方法不匹配

3、 500:服务器出现异常

    String ret=null;
        resp.getWriter().write(ret.length());

比如以上这种,空指针异常

 4、无法访问此网站

Tomcat服务器启动问题

Servlet的API

servlet 3.0 API由以下4个包组成:

1、javax.servlet包:定义了开发独立于协议的服务器的小程序的接口和类

2、javax.servlet.http包:定义了开发采用HTTP通信的服务器的小程序的接口和类

3、javax.servlet.annotation包:定义了9个注解类型和两个枚举类型

4、javax.servlet.descriptor包:定义了以编程方式访问Web英语程序配置信息的类型

javax.servlet包

这个包定义了独立于任何协议的一般Servlet接口和类

狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者

 接口 说明
Servlet

所有Servlet的根接口

ServletRequest 客户请求的接口
ServletRespons 服务器响应的接口
ServletContextListener 监听web应用程序的监听器接口
ServletContextAttributedListener 监听web应用程序属性的监听器接口
RequestDispatcher 将请求转发给其他资源
ServletConfig Servlet类使用的过滤器配置对象

常用的类和接口:

1、Servlet 接口:Servlet API中的核心接口,每一个Servlet 程序必须直接或者间接的使用这个接口 

我们介绍它的3个方法:

  • init(ServletConfig config):这个方法由Tomcat调用,完成Servlet 初始化并且提供服务
  • service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException ):对每一个用户请求,Tomcat调用这个方法,Tomcat允许Servlet 程序为请求提供响应,而计算响应的逻辑就是service()方法实现的
  • destory():这个方法由Tomcat调用,指示Servlet 程序清除本身,释放的资源并准备结束服务

2、ServletConfig 接口:为用户提供了Servlet 的配置信息

3、GenericServlet 类: 这个抽象类实现了ServletConfig 接口和Servlet接口,提供了Servlet接口中处理 service()外的所有方法,同时也增加了几个日志操作的方法,可以提供扩展这个类并实现 service()方法来创建任何类型的 servlet

javax.servlet.http包

介绍几个重要的类和接口

1、HttpServlet 类

这个抽象类用于实现针对 HTTP协议 的servlet,扩展了GenericServlet类,实现了service ()方法,而service()是用来处理客户请求的

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    super.service(req, resp);
}

查看service()方法的源码,这个方法可以处理GET,POST,PUT等请求,根据方法名执行对应的doXXX方法

   protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }

 HttpServlet类是抽象类,可以通过继承这个类,重写service()方法,它会自动判断请求类型,从而调用对应方法,但是根据请求计算响应,逻辑还是doGet()方法等实现

所以我们直接重写doGet()方法处理GET请求,重写doPost()方法,处理Post请求

@WebServlet("/ok")//当url中第二级路径是/ok,才会触发到这个类,才可以处理请求
public class ServletAPI extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //重写doGet方法,用于处理HTTP协议的GET请求
        //1、接收请求
        //2、根据请求计算响应
        int a =10;
        //3、将响应返回给服务器
        resp.getWriter().write(a+"ok");
    }
}

 这里看似是ServletAPI类扩展了HttpServlet类,实际上是 ServletAPI类 实现了Servlet接口,(因为HttpServlet这个抽象类继承自GenericServlet 类,GenericServlet类实现了ServletConfig 接口和Servlet接口)

所以是:ServletAPI类实现了Servlet接口,重写了doGet()方法

Tomcat将请求传递给ServletAPI类,Tomcat调用Servlet接口的service()方法,service()方法内部调用doGet()方法,调用的是ServletAPI 类重写的doGet()方法,就涉及到了多态

2、HttpServeltRequest接口

扩展了ServletRequest接口,并提供了一些方法获取到HTTP请求的内容

3、HttpServeltResponse接口

扩展了ServletResponse接口,并提供了一些方法针对HTTP协议返回响应

 Servlet的生命周期

Servlet作为一个在容器中运行的组件,有一个创建到销毁的过程,这个过程被称作Servlet的生命周期

Tomcat的运行过程

接收请求

1、Tomcat初始化

Tomcat会在指定的目录中找到要加载资源的 servlet

2、Tomcat部署的是war包,存入的是.class文件和其他的css,js等文件,那么对于.class文件,就需要根据反射机制为需要使用的类创建实例化对象,这个对象本质上实现了Servlet 这个根接口

3、对于servlet 接口的实例,Tomcat要调用 servlet 接口的init()方法,完成类的初始化并准备提供服务

4、如果是启动HTTP的服务器,那么Tomcat创建TCP 的socket,监听8080端口,等待客户端连接;这个等待接收的过程,是基于多线程实现的

5、如果以上监听8080端口的环节正常结束,那么会给每一个servlet实例调用destory()方法,关闭相关资源;这里的正常结束,比如被别的线程结束,但是如果直接关闭了Tomcat,就可能来不及调用destory()

处理请求

1、根据socket对象的信息(字符串),构造基于HTTP协议的请求,并创建一个空的HTTP响应

2、根据HTTP请求中的信息,找到URL,也即是所请求访问资源的地址

如果这个地址在我们电脑存在,那么表示请求访问的资源是静态资源,直接将对应内容输出即可;

否则,对应的就是动态页面;Tomcat调用Servlet 接口 的service()方法,service()方法执行被重写的doXXX()方法,产生响应。涉及到了多态

 Servlet的生命周期

我们将一个Servlet 类的实例,经历init(),service(),destory()三个状态的时间,称之为 Servlet的生命周期;对于一个类,init()仅且执行多次,service()可能执行多次,destory()最多执行一次(可能被异常关闭)

HttpServlet 类的使用

一个servlet 程序也可以部署静态文件,放在webapp目录即可

浏览器使用Tomcat加载demo.html这个静态页面,然后这个静态页面发送Post请求,被服务器接收,做出响应

  出现上述的乱码,是因为编码方式的问题

idea默认使用UTF-8编码,windows默认使用GBK编码,需要统一编码

就是设置header的content-type属性

@WebServlet("/method")
public class ServletText  extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     resp.setContentType("text/html;charset=utf-8");//指定响应的格式
        resp.getWriter().write("响应 ok");
    }
}

 HttpServletRequest类

支持很多方法,获取到请求的信息

@WebServlet("/request")
public class HttpServletRequestApi extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        StringBuilder builder = new StringBuilder();
        builder.append(req.getProtocol());//返回请求所使用的协议名称和版本
        builder.append('\n');
        builder.append(req.getMethod());//返回请求使用的方法
        builder.append('\n');
        builder.append(req.getRequestURI());//获取请求的资源路径
        builder.append('\n');
        builder.append(req.getRequestURL().toString());//获取请求的统一资源定位符(绝对路径)
        builder.append('\n');
        builder.append(req.getContextPath());//获取指示请求上下文的路径
        builder.append('\n');
        builder.append(req.getQueryString());//获取QueryString字段内容
        builder.append('\n');
        builder.append(req.getCharacterEncoding());//获取body的字符编码
        builder.append("\n");
        Enumeration<String> headerNames = req.getHeaderNames();//返回所有请求头的头名 也就是key
        while (headerNames.hasMoreElements()) {
            String des = headerNames.nextElement();//key
            builder.append(des).append(" ");
            builder.append(req.getHeader(des));//根据key值得到对应的value
            builder.append('\n');
        }

        //读取body内容,返回一个字节流
        ServletInputStream in = req.getInputStream();
        int len = 0;
        byte[] elem = new byte[1024];
        while ((len = in.read(elem)) != -1) {
            builder.append(new String(elem, 0, len, StandardCharsets.UTF_8));
        }
        resp.getWriter().write(builder.toString());
    }
}

创建请求

 运行结果

URI: 统一资源标识符。用来唯一标识资源,是一种语义上的抽象概念。

URL: 统一资源定位符。用来定位唯一的资源, 必须提供足够的定位信息。

URN:  统一资源名称。定义了资源的身份(命名)。

简单比喻 - URI唯一标识一个人(例如身份证), URL定义了如何访问到这个人(例如家庭地址),URN用名字标识一个人(假设所有人名字都不一样的情况下)

 URL和URN都是URI的子集

  post请求的数据格式:

body的数据组织形式Content-Type有三种:

1、application/x-www-form-urlencoded; 就是键值对的形式,和url的queryString格式相同;也是默认的格式userid=123&pass=908
2、application/json {userid:123,pass:908}

3、multipart/from-data:文件上传,下载的格式

对于json格式的body,可以使用第三方库:jackson来获取数据内容

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script >
    //创建post请求 body部分设置为json格式
    $.ajax({
    type: 'post',
    url:"jsontext",
    contentType:'application/json',
data:JSON.stringify({ //将js数据的对象 转化为一个json格式的字符串
    name:'123',password:'456'//这个是一个js对象
   
}),
success: function(body){
  console.log(body);
}
});

抓包观察,body部分的数据格式

class  type{
    public int name;
    public int password;
    //属性名要匹配
}
@WebServlet("/jsontext")
public class json extends HttpServlet {
    //1、创建jackson的对象
    //  ObjectMapper类是Jackson库的主要类。它提供一些功能将转换成Java对象匹配JSON结构
    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //2、读取body内容,解析成为对应对象
        type ret = objectMapper.readValue(req.getInputStream(), type.class);//转化为了Type对象
        //根据type.class,也就是反射,json的name 和password,匹配到type的两个属性

        resp.getWriter().write("name :" + ret.name + "password :" + ret.password);
    }
}

获取get请求中jqueryString的内容

@WebServlet("/QueryString")
public class QueryString extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("text/html;charset=utf-8");
        String  name=  req.getParameter("name");
        //获取参数的值,如果不存在返回null,也就是jqueryString中name=123,这里就返回123
      String password= req.getParameter("password");
      resp.getWriter().write("name: "+name+ " password: "+password);
      //结果:name=123 password=456
    }
}

HttpServletResponse类

@WebServlet("/demo")
public class read extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("Refresh", "1");//设置一个Header内的键值对,如果name已经存在,进行覆盖
        //结果:每隔一秒,刷新页面
         resp.getWriter().write("time"+ System.currentTimeMillis());//按照文本格式,将数据写回客户端
        
        resp.setStatus(302);//设置状态码
        resp.addHeader("location","https://www.sogou.com");//设置一个Header内的键值对,如果name已经存在,不进行覆盖
        //结果:重定位
        //重定位,可以简化
        resp.sendRedirect("https://www.sogou.com");
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340