Servlet学习

Posted 爱敲代码的三毛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet学习相关的知识,希望对你有一定的参考价值。

文章目录


Servlet

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

静态页面和动态页面

静态页面也就是内容始终固定的页面,即使用户不同/时间不同/输入的参数不同,页面内容也不会发生变化(除非网站的开发人员修改源代码,否则页面内容始终不变)

对应的,动态页面指的就是 用户不同/时间不同/输入的参数不同,页面内容会发送变化

第一个Servlet程序

xml配置路由映射

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         <!--所有路由文件已经在本xml,没办法使用注解的范式配置路由 -->
         metadata-complete="true">

    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

Servlet 注解的 web.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         <!-- 设置成false就可以了-->
         metadata-complete="false">

</web-app>
  1. 首先要创建一个 Maven项目

  2. 在Maven项目里的pom.xml里引入依赖,从中央仓库的Servlet api 的jar包

    选择Tomcat8对应的3.1版本的Servlet依赖

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <!-- scope这个标签表示当前jar包只是在开发阶段使用,而不需要打包到最终的发布包中 -->
    <!-- Tomcat里面内置了Servlet,把程序部署到Tomcat上的时候,已经有了Servlet了 -->
    <!-- 就不需要把自己代码里的Servlet也打包一份放到Tomcat上了 -->
    <scope>provided</scope>
</dependency>

把依赖放到 pom.xml中的 dependencies标签中

  1. 开始写代码

    1.先创建好一个类继承HttpServlet

    2.重写doGet方法

    @WebServlet("/test")
    public class TestServlet extends HttpServlet 
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
            //这个调用父类的方法一定要干掉
            //super.doGet(req, resp);
            resp.getWriter().write("hello Servlet");
        
    
    //doGet是HttpServlet这个父类的方法
    //这个方法的参数分别是
    // HttpServletRequest:HTTP请求
    //HttpServletResponse:HTTP响应
    //doGet方法里要做的事情,就是根据请求,生成响应
    

    注意:重写的 doGet 方法之后,并不需要手动调用 doGet,而是由Tomcat自动来调用,也不需要咱们手动的创建 TestServlet 实例,也是由Tomcat自动创建实例

    resp.getWriter.write("hello Servlet");
    

    这个操作,就是往HTTP响应的body中,写了一个 "hello Servlet"字符串

@WebServlet

@WebServlet("/test")

@WebServlet 也是Servlet中提供的注解,功能就是把类和HTTP 特定请求进行关联,根据HTTP请求URL的路径来进行关联的

如果咱们的Tomcat收到了一个路径为 /test 的请求,就会调用到 TestServlet 的代码

如果这个请求是GET请求,就会调用到HelloServlet.doGet 方法

如果这个请求是POST请求,就会调用到HelloServlet.doPost方法

注意

super.doGet(req, resp)

重写方法后,一定要把这个调用父类代码的操作,给干掉

这个方法里面直接构造了一个错误的响应(状态码为 405 的响应)

web.xml

在main目录中,创建一个webapp目录,里面再创建一个 WEB-INF目录,在WEB-INF里再创建 web.xml文件

把下面的代码复制到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>

webapp 目录就是未来部署到 Tomcat 中的一个重要的目录. 当前我们可以往 webapp 中放一些静态资源, 比如 html , css 等.
在这个目录中还有一个重要的文件 web.xml. Tomcat 找到这个文件才能正确处理 webapp 中的动态资源

打包操作

双击package就能进行打包,或者右击运行

由于Maven默认打成的是一个 jar格式的包,而Tomcat不能识别 jar格式的包。Tomcat识别的是war格式的包,所以要修改 maven 中的 pom.xml 的配置,修改打包类型为 war

在 pom,xml中添加下面这行代码,就能让maven打成的包为 war格式

<packaging>war</packaging>

打包的包名默认是 artifact id + version,名字太复杂

在pom.xml中添加下面的代码就能修改打包的包名

<build>
    <finalName>java</finalName>
</build>

部署程序

把war包拷贝到 Tomcat的webapps目录中即可,启动Tomcat之后,就会解压缩出来一个同名的目录

完整的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>TestServlet</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <!-- 按照jdk1.8这样的版本来理解源码,并按照这个版本来生成字节码 -->
        <maven.compiler.soutce>8</maven.compiler.soutce>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <!-- 改成war包 -->
    <packaging>war</packaging>
    <build>
        <finalName>java</finalName>
    </build>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <!-- scope这个标签表示当前jar包只是在开发阶段使用,而不需要打包到最终的发布包中 -->
            <!-- Tomcat里面内置了Servlet,把程序部署到Tomcat上的时候,已经有了Servlet了 -->
            <!-- 就不需要把自己代码里的Servlet也打包一份放到Tomcat上了 -->
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

Smart Tomcat

IDEA专业版自带 Tomcat的部署功能,不需要使用 Smart Tomcat插件

IDEA社区版没有自带Tomcat的部署功能,需要使用 Smart Tomcat插件

Servlet常见问题

1.404

出现404最大的原因就是URL的路径写错了

或者是@WebServlet注解的内容写错了,比如少了一个 /,或者是URL的路径和注解对不上号

如果 web.xml写错了,也可能导致404

2.405

405最重要的原因,请求的方法和代码中重写的方法对不上号

往浏览器里输入URL应该是GET请求,而实现的却是POST方法

还有一个原因就是调用父类的 doGet方法,没有删掉。

父类的doGet方法是直接返回405的

3.500

出现500最主要的原因就是代码中抛异常了,页面上/Tomcat日志里面会明确提示出异常的调用栈等详细信息

在实际开发中,异常调用栈直接展示在页面上,是一件非常危险的事情

一个商业产品,如果异常调用栈被用户看到了,就可能带来安全隐患

从异常调用栈里可以看到出错误的代码的具体位置,也知道了是用什么代码写的

同时也知道了 Tomcat的版本号

黑客手里一般都有漏洞库,就可以去找对应的版本的漏洞库,从而对你的服务器进行攻击(很可能会入侵成功,窃取破坏服务器上的信息)

4.空白页面

@WebServlet("/test")
public class TestServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        //resp.getWriter().write("hello Servlet");
    

如果没有调用resp.getWriter().write,没往body里写就会出现空白页面。有时候可能里面的代码比较复杂,没有注意是否调用resp.getWriter().write就可能出现这种情况

5.无法访问此网站

一般就是Tomcat启动失败了

Servlet API

学习Servlet的具体使用,核心掌握三个类就够了

  • HttpServlet
  • HttpServletRequest
  • HttpServletResponse

HttpServlet

方法名调用时机
init在 HttpServlet 实例化之后被调用一次
destory在 HttpServlet 实例不再使用的时候调用一次
service收到 HTTP 请求的时候调用
doGet收到 GET 请求的时候调用(由 service 方法调用)
doPost收到 POST 请求的时候调用(由 service 方法调用
doPut/doDelete/doOptions/…收到其他请求的时候调用(由 service 方法调用)

实际开发中主要重写 doxxx方法,很少会重写init/destory/service。

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

Serlet的生命周期

一个常见的面试题:说一下Servlet的生命周期

  1. Servlet在实例化之后调用一次 init
  2. Servlet每次收到请求,调用一次service
  3. Servlet在销毁之前,调用一次destroy

代码实例:处理GET、POST、PUT请求

创建一个 MethodServlet类

@WebServlet("/method")
public class MethodServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        //设置响应的内容类型和字符编码
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("get 请求");
    

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("post 请求");
    

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().write("PUT 请求");
    


html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>MethodServlet</title>
</head>
<body>
    <button onclick="sendGet()">发送GET请求</button>
    <button onclick="sendPost()">发送POST请求</button>
    <button onclick="sendPut()">发送PUT请求</button>
    <script src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js"></script>

    <script>
        function sendGet() 
            $.ajax(
                type: 'get',
                url: 'method',
                success: function (data,status) 
                    console.log(data);
                
            )
        
        function sendPost() 
            $.ajax(
                type: 'post',
                url: 'method',
                success: function (data,status) 
                    console.log(data);
                
            )
        
        function sendPut() 
            //构造一个 XMLHttpRequest 对象
            let httpRequest = new XMLHttpRequest;
            //这里注册的 function 不是立马执行的,响应啥时候到,function啥时候执行
            httpRequest.onreadystatechange = function () 
                if (httpRequest.readyState == 4) 
                    console.log(httpRequest.responseText+" "+httpRequest.status);
                
            
            //构造一个HTTP请求,设置要向哪个URL发送请求
            httpRequest.open('PUT','method');
            httpRequest.send();
        
    </script>
</body>
</html>

乱码处理

setContentType

这 个方法设置发送到客户端的响应的内容类型,此时响应还没有提交。给出的内容类型可以包括字符编码说明,此处注明响应编码是 utf-8

该方法如果在getWriter()方法被调用之后或者在被提交之后调用,将不会设置响应的字符编码

HttpServletRequest

由Tomcat把字符串结构的请求解析成了一个结构化的数据

从字符串到结构化数据的过程,称为"反序列化"

URL和URI

URL和URI含义是类似的,都是表示网络上的一个资源

L Lication,资源的位置

I Id, 资源的标识符

所以这俩东西往往使用的场景都差不多,也就不做过多区分了

方法描述
String getProtocol()返回请求协议的名称和版本
String getMethod()返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT
String getRequestURI()从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分
String getContextPath()返回指示请求上下文的请求 URI 部分
String getQueryString()返回包含在路径后的请求 URL 中的查询字符串,得到的是完整的QueryString
Enumeration
getParameterNames()
返回一个 String 对象的枚举,包含在该请求中包含的参数的名称,也就是header中所有键值对的key
String getParameter(String name)以字符串形式返回请求参数的值,或者如果参数不存在则返回null,输入key返回jQuery中的value值
String[]
getParameterValues(String name)
返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null
Enumeration
getHeaderNames()
返回一个枚举,包含在该请求中包含的所有的头名
String
getHeader(String name)
以字符串形式返回指定的请求头的值,header中的键值对的value
String
getCharacterEncoding()
返回请求主体中使用的字符编码的名称
String getContentType()返回请求主体的 MIME 类型,如果不知道类型则返回 null
int getContentLength()以字节为单位返回请求主体的长度,该方法用于获取请求的 Body 的长度,如果不确定长度,则返回 -1
InputStream
getInputStream()
用于读取请求的 body 内容. 返回一个 InputStream 对象

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

注意:请求对象是服务器收到的内容, 不应该修改. 因此上面的方法也都只是 “读” 方法, 而不是 “写”
方法

代码示例:打印request中的信息

@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        resp.setContentType("text/html; charset=utf-8");
        StringBuilder requestBody = new StringBuilder();

        //获取协议的名称和版本
        requestBody.append(req.getProtocol());
        requestBody.append("<br/>");
        //返回HTTP方法名称
        requestBody.append(req.getMethod());
        requestBody.append("<br/>");
        //返回资源的标识和URL类似
        requestBody.append(req.getRequestURI());
        requestBody.append("<br/>");
        //返回ContextPath,就是那个用来区分webapp的根路径
        requestBody.append(req.getContextPath());
        requestBody.append("<br/>");
        //返回完整的querystring
        requestBody.append(req.getQueryString());

        requestBody.append("<h3>headers:</h3>");
        //返回一个枚举类型返回请求头 header中所有键值对的 key
        Enumeration<String> headerNames = req.getHeaderNames();
        //类似于迭代器的遍历
        while (headerNames.hasMoreElements()) 
            String headerName = headerNames.nextElement();
            requestBody.append(headerName+": ");
            //getHeader是根据key获取value
            requestBody.append(req.getHeader(headerName));
            requestBody.append("<br/>");
        
        resp.getWriter().write(requestBody.toString());
    

代码示例:获取GET请求中的参数

GET请求中的参数一般都是通过query string 传递给服务器的列如:

http://127.0.0.1:8080/TestServlet/showRequest?userId=111&classId=222

此时浏览器通过 query string 给服务器传递了两个参数, userId 和 classId,值分别是 111和222

在服务端就可以通过 getParameter来获取到参数的值

@WebServlet("/getParameter")
public class GetParameter extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        //设置响应的内容类型和字符编码
        resp.setContentType("text/html; charset=utf-8");
        
        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");

        resp.java web学习: 三大组件之servlet基础

Java Servlet学习笔记

学习学习servlet

java web 学习笔记 - servlet01

Java Web 学习与总结Servlet核心接口+Servlet3.0配置

java web学习笔记first servlet