Servlet---1

Tomcat 就是基于 Java 实现的一个开源免费,也是被广泛使用的 HTTP 服务器.

目录结构

针对 tomcat 目录解压缩之后, 可以看到如下结构

apache-tomcat-8.5.47\
    bin\ 存放各种启动、停止脚本的。*.sh 是以后在 linux 上用的,*.bat 是在 windows
上用的
startup.bat 启动服务,双击即可使用
conf\ 相关的配置文件,目前我们不用关心
lib\ 运行 tomcat 需要的类库,我们不关心
logs\ 运行时的日志文件,我们有时需要查看日志,来发现定位一些问题
temp\ 临时文件夹,不关心
webapps\ 存放我们要运行的 web application 的文件夹,对于我们最常用的一个文件夹
work\ Tomcat 内部进行预编译的文件夹,我们不关心
下面都是一些文档,兴趣的同学可以自行阅读
BUIDING.txt
    CONTRIBUTING.md
    LICENSE
    NOTICE
    README.md
    RELEASE-NOTES
    RUNNING.txt

其中我们最关注的目录就是 webapps 目录. web applications 的简称, 意思是用来存放 web 应用的文件夹.

"web 应用" ---> 一个具有独立完整功能的 "网站", 我们就可以称为一个 "web 应用".

例如 搜狗搜索 实现了独立完整的 "搜索引擎功能", 淘宝网 实现了独立完整的 "电商功能" .

一个 Tomcat 服务器上是可以同时部署多个这样的 web 应用的. 这些 web 应用以目录的形式被放到 webapps 目录中.

webapps 目录

webapps\
    docs\     
    examples\     
    host-manager\     
    manager\     
    ROOT\

每个文件夹都对应着一个 web 应用, 可以在浏览器中分别访问每个 web 应用.

启动服务器

在 bin 目录中, 双击 startup.bat 即可启动 Tomcat 服务器看到形如以下内容的日志, 说明启动成功.

注意: 在 Windows 上通过 cmd 方式启动 Tomcat 会出现乱码. 但是不影响 Tomcat 的使用.

乱码的原因是 Tomcat 默认按照 UTF-8 的编码方式处理中文. 而 windows 的 cmd 默认是 GBK 编码.

如果使用 Linux 或者 IDEA 中的终端来启动 Tomcat, 则没有乱码问题. 因此此处的乱码我们暂时不处理.

在浏览器中输入 127.0.0.1:8080 即可看到 Tomcat 的默认欢迎页面.

如果启动失败怎么办?

最常见的启动失败原因是端口号被占用.

Tomcat 启动的时候默认会绑定 8080 和 8005 端口.

如果有其他进程已经绑定了这两个端口中的任意一个, 都会导致 Tomcat 不能启动.

在命令行中使用 netstat -ano | findstr 8080 确定看 8080 是否被其他进程绑定, 把对方进程干掉, 再重新启动 Tomcat 一般就可以解决问题.

形如这样的结果说明 8080 端口已经被占用. 占用的进程是 13348 这个进程. 然后就可以在任务管理器中找到这个进程, 并干掉这个进程.

Servlet

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

Servlet是一个比较古老的编写网站的方式了,以前java写网站主要采用Servlet的方式。后来用的是spring(一套框架,对servlet进行了进一步的封装)来编写网站。

servlet主要做的工作就是让程序猿自己写一些类然后把这些类给加载到tomcat中后续tomcat收到http请求(来自于浏览器),就会执行到咱们上面写的代码。从而通过这些代码,完成一定的业务逻辑。

ervlet 是一组 Tomcat 提供的 API, 让程序猿自己写的代码能很好的和 Tomcat 配合起来, 从而更简单的实现一个 web app. 而不必关注 Socket, HTTP协议格式, 多线程并发等技术细节, 降低了 web app 的开发门槛, 提高了开发效率.

创建项目

此处创建的是一种新的项目形式,称为Maven项目,它是java中的一个知名的构建工具。用于编译/打包代码的工具。

Maven项目首次创建的时候,会自动的从Maven的网站上下载一些依赖的组件。(这个过程如果网络不稳定,可能出错,会对后续使用maven的使用造成影响)。

创建之后目录结构为

main中放业务代码。

test中放测试代码

pom.xml是maven项目的入口配置文件。

创建完项目之后idea会自动的把目录结构生成好。还会有一个HelloServlet文件,运行项目,可以访问index.jsp文件

运行项目

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>JSP - Hello World</title>
</head>
<body>
<h1>
    <%= "Hello World!" %> // 把内容显示在页面上。
</h1>
<br/>
<a href="hello-servlet">Hello Servlet</a> 
// 运行项目之后的url为http://localhost:8080/test1_war_exploded/
// 点击超链接之后浏览器会访问http://localhost:8080/test1_war_exploded/hello-servlet
</body>
</html>

@WebServlet中的value属性是将这个类和一个HTTP请求路径关联起来。

value中路径务必以/开头,而且确保一个项目中,多个servlet这里指定的路径不能重复

Servlet API详解

我们写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自 HttpServlet, 并重写其中的某些方法.

核心方法

方法名称调用时机

方法名

调用时机

init

在 HttpServlet 实例化之后被调用一次

destory

在 HttpServlet 实例不再使用的时候调用一次

service

收到 HTTP 请求的时候调用

doGet

收到 GET 请求的时候调用(由 service 方法调用)

doPost

收到 POST 请求的时候调用(由 service 方法调用)

doPut/doDelete/DoOptions/...

收到其他请求的时候调用(由 service 方法调用)

实际写的时候经常会重写doXXX之类的方法,很少会重写init/desotry等。

这些方法的调用时机, 就称为 "Servlet 生命周期". (也就是描述了一个 Servlet 实例从生到死的过程).

HttpServlet 的实例只是在程序启动时创建一次. 而不是每次收到 HTTP 请求都重新创建实例.

Post请求和Get请求

package com.example.web_sxk;

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("/method")
public class test extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPost");
        resp.getWriter().println("doPost");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet");
        resp.getWriter().println("doGet");

    }

}

运行项目,在postman等之类的软件中访问/method方法。

HttpServletRequest

HttpServletRequest表示了一个HTTP请求。

当 Tomcat 通过 Socket API 读取 HTTP 请求(字符串), 并且按照 HTTP 协议的格式把字符串解析成HttpServletRequest 对象.

通过这些方法可以获取到一个请求中的各个方面的信息.

注意: 上面这些方法都是get开头,因为request方法是一个请求,是服务器收到的内容,不应该被修改。

案例:打印请求信息

package com.example.web_sxk;

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.util.Enumeration;

@WebServlet("/showRequest")
public class showRequest extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 调用上述API把得到的结果构造成一个字符串,统一返回给客户端
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(req.getProtocol());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getMethod());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getRequestURI());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getContextPath());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getQueryString());
        stringBuilder.append("<br>");

        // 获取所有header
        Enumeration<String> headerNames = req.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = req.getHeader(key);
            stringBuilder.append(key + ": " + value + "<br>");
        }

        // 告诉浏览器数据是啥类型,任何一次服务器返回都应该做的事情
        resp.setContentType("text/html; charset=utf8");

        resp.getWriter().write(stringBuilder.toString());
    }
}

案例:获取GET请求中的参数

GET请求中的参数一般都是通过query string传递给服务器的,例如https://v.bitedu.vip/personInf/student?userId=1111&classId=100此时浏览器通过 query string 给服务器传递了两个参数, userId 和 classId, 值分别是1111和100在服务器端就可以通过 getParameter 来获取到参数的值.

package com.example.web_sxk;


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("/getParameter")
public class GetParameterServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 次数约定,请求中给定的query string是形如:username=zhangsan&password=123
        // 上述query string,就会被tomcat给自动解析成一个Map这样的结构
        // getParameter就是在查询Map里的内容
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        // 拿到这两个内容之后就可以做一些其他的处理
        System.out.println(username);
        System.out.println(password);

        resp.getWriter().println(username + " " + password);

    }
}

当没有 query string的时候, getParameter 获取的值为 null.getParameter 的返回值类型为 String. 必要的时候需要手动把 String 转成 int.

案例:获取POST请求中的参数(1)

POST 请求的参数一般通过 body 传递给服务器. body 中的数据格式有很多种. 如果是采用 form 表单的形式, 仍然可以通过 getParameter 获取参数的值.

package com.example.web_sxk;


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("/postParameter")
public class PostParameter extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        resp.getWriter().write("username: " + username + ", " + "password: " + password);
    }
}

Content-Type: application/x-www-form-urlencoded, 对应的 body 数据格式就形如

userId=123&classId=456

案例:获取POST请求中的参数(2)

如果 POST 请求中的 body 是按照 JSON 的格式来传递, 那么获取参数的代码就要发生调整.

Servlet自身不支持,需要引入额外的第三方库。日常写程序,经常会引入大量的第三方库,此处为了针对json格式的数据进行解析和构造,就需要引入json的库 --- jackson

package com.example.web_sxk;
import com.fasterxml.jackson.databind.ObjectMapper;
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.InputStream;
class Request {
    public String username;
    public String password;
}
class Response {
    public boolean ok;
}
@WebServlet("/PostParameterJson")
public class PostParameterJson extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
        * 此处约定格式如下
        * POST /json
        * Content-Type: application/json
        *
        * {
        *   username: "zhangsan",
        *   password: "123"
        * }
        *
        * 此处也约定响应的格式(也按照json来组织)
        * {
        *   ok: true
        * }
        *
        * */
        resp.setContentType("application/json;charset=utf-8");

        ObjectMapper objectMapper = new ObjectMapper();

        Request request = objectMapper.readValue(req.getInputStream(), Request.class);

        System.out.println("username=" + request.username);
        System.out.println("password=" + request.password);

        Response response = new Response();
        response.ok = true;
        // 把响应对象转换为json
        String respJson = objectMapper.writeValueAsString(response);
        resp.getWriter().write(respJson);
    }
}

上一篇:安装 JDK


下一篇:网页web无插件播放器EasyPlayer.js点播播放器遇到视频地址播放不了的现象及措施