• 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中拷贝以下内容

    1. web-app PUBLIC
    2. "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    3. "http://java.sun.com/dtd/web-app_2_3.dtd">
    4. <web-app>
    5. <display-name> Archetype Created Web Applicationdisplay-name>
    6. web-app>

    4、编写servlet代码

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

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

    在pom.xml中做如下修改

    1. <packaging>warpackaging>
    2. <build>
    3. <finalName>
    4. hellotest
    5. finalName>
    6. 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方法
    1. @WebServlet("/hi")//当url路径包含hi目录时,才会触发这个类
    2. public class Hello extends HttpServlet {
    3. @Override
    4. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    5. //HttpServletRequest:请求类 HttpServletResponse:响应类
    6. //Tomcat服务器的控制台的内容
    7. System.out.println("服务器hello");
    8. //2、当服务器接收到请求之后,做出的响应
    9. resp.getWriter().write("hello");
    10. }
    11. }

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

    3、 500:服务器出现异常

    1. String ret=null;
    2. 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将请求转发给其他资源
    ServletConfigServlet类使用的过滤器配置对象

    常用的类和接口:

    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方法

    1. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    2. String method = req.getMethod();
    3. long lastModified;
    4. if (method.equals("GET")) {
    5. lastModified = this.getLastModified(req);
    6. if (lastModified == -1L) {
    7. this.doGet(req, resp);
    8. } else {
    9. long ifModifiedSince = req.getDateHeader("If-Modified-Since");
    10. if (ifModifiedSince < lastModified) {
    11. this.maybeSetLastModified(resp, lastModified);
    12. this.doGet(req, resp);
    13. } else {
    14. resp.setStatus(304);
    15. }
    16. }
    17. } else if (method.equals("HEAD")) {
    18. lastModified = this.getLastModified(req);
    19. this.maybeSetLastModified(resp, lastModified);
    20. this.doHead(req, resp);
    21. } else if (method.equals("POST")) {
    22. this.doPost(req, resp);
    23. } else if (method.equals("PUT")) {
    24. this.doPut(req, resp);
    25. } else if (method.equals("DELETE")) {
    26. this.doDelete(req, resp);
    27. } else if (method.equals("OPTIONS")) {
    28. this.doOptions(req, resp);
    29. } else if (method.equals("TRACE")) {
    30. this.doTrace(req, resp);
    31. } else {
    32. String errMsg = lStrings.getString("http.method_not_implemented");
    33. Object[] errArgs = new Object[]{method};
    34. errMsg = MessageFormat.format(errMsg, errArgs);
    35. resp.sendError(501, errMsg);
    36. }
    37. }

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

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

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

     这里看似是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属性

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

     HttpServletRequest类

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

    1. @WebServlet("/request")
    2. public class HttpServletRequestApi extends HttpServlet {
    3. @Override
    4. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    5. StringBuilder builder = new StringBuilder();
    6. builder.append(req.getProtocol());//返回请求所使用的协议名称和版本
    7. builder.append('\n');
    8. builder.append(req.getMethod());//返回请求使用的方法
    9. builder.append('\n');
    10. builder.append(req.getRequestURI());//获取请求的资源路径
    11. builder.append('\n');
    12. builder.append(req.getRequestURL().toString());//获取请求的统一资源定位符(绝对路径)
    13. builder.append('\n');
    14. builder.append(req.getContextPath());//获取指示请求上下文的路径
    15. builder.append('\n');
    16. builder.append(req.getQueryString());//获取QueryString字段内容
    17. builder.append('\n');
    18. builder.append(req.getCharacterEncoding());//获取body的字符编码
    19. builder.append("\n");
    20. Enumeration<String> headerNames = req.getHeaderNames();//返回所有请求头的头名 也就是key
    21. while (headerNames.hasMoreElements()) {
    22. String des = headerNames.nextElement();//key
    23. builder.append(des).append(" ");
    24. builder.append(req.getHeader(des));//根据key值得到对应的value
    25. builder.append('\n');
    26. }
    27. //读取body内容,返回一个字节流
    28. ServletInputStream in = req.getInputStream();
    29. int len = 0;
    30. byte[] elem = new byte[1024];
    31. while ((len = in.read(elem)) != -1) {
    32. builder.append(new String(elem, 0, len, StandardCharsets.UTF_8));
    33. }
    34. resp.getWriter().write(builder.toString());
    35. }
    36. }

    创建请求

     运行结果

    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来获取数据内容

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

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

    1. class type{
    2. public int name;
    3. public int password;
    4. //属性名要匹配
    5. }
    6. @WebServlet("/jsontext")
    7. public class json extends HttpServlet {
    8. //1、创建jackson的对象
    9. // ObjectMapper类是Jackson库的主要类。它提供一些功能将转换成Java对象匹配JSON结构
    10. ObjectMapper objectMapper = new ObjectMapper();
    11. @Override
    12. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    13. //2、读取body内容,解析成为对应对象
    14. type ret = objectMapper.readValue(req.getInputStream(), type.class);//转化为了Type对象
    15. //根据type.class,也就是反射,json的name 和password,匹配到type的两个属性
    16. resp.getWriter().write("name :" + ret.name + "password :" + ret.password);
    17. }
    18. }

    获取get请求中jqueryString的内容

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

    HttpServletResponse类

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

  • 相关阅读:
    MyBatis打印不带问号SQL
    正版Adobe软件来了!Adobe全球唯一正版全家桶订阅只需0元/年
    Cesium.js实现无人机按轨迹飞行,并扫描地面
    pb:自动生成编号
    Node.js+vue校内二手物品交易系统tdv06-vscode前后端分离
    学习记录609@python实现数据样本的过采样与欠采样
    个性化精准推送服务:Mobpush引领用户深度参与度的新纪元
    题解:ABC320E - Somen Nagashi
    隆云通CO2、PM2.5、PM10三参数传感器
    【JVM技术专题】精心准备了一套JVM分析工具的锦囊「JConsole补充篇」
  • 原文地址:https://blog.csdn.net/m0_58342797/article/details/126452901