JavaWeb Servlet快速入门【基础总结】

Servlet

Servlet简介

  • Servlet就是sun公司开发动态web的一门技术
  • Sun在这些API中提供一个接口叫做:Servlet,其方法有: init(),service(),destroy()等方法。
  1. init()方法是servlet生命的起点。一旦加载了某个servlet,服务器将立即调用它的init()方法。在init()方法中,servlet创建和初始化它在处理请求时需要用到的资源。
  2. service()方法处理客户机发出的所有请求。在init()方法执行之前,它无法开始对请求提供服务,通常,我们不能直接实现这个方法,除非对GenerieServlet抽象类进行扩展。
  3. destroy()方法标志servlet生命周期的结束。当服务需要关闭时,它调用servlet的destroy()方法。此时,在init()方法中创建的任何资源都应该被清除和释放。如果有打开的数据库连接,应当在此处保存任何在下一次加载时需要用到的永久性信息。
  • 如果我们想开发一个Servlet程序,需要完成两个步骤:
    • 编写一个类,实现Servlet接口
    • 把开发好的java类部署到web服务器中

把实现了Servlet接口的java程序叫做:Servlet.

Servlet的作用:

  1. 用来接收、处理客户端请求、响应给浏览器的动态资源。
  2. 在整个Web应用中,Servlet主要负责接收处理请求、协同调度功能以及响应数据。
  3. 我们可以把Servlet称为Web应用中的『控制器』。

Servlet接口的默认实现类

Servlet接口Sun公司有两个默认的内置实现类:

  • HttpSetvlet (常用)
  • GenericServlet

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ROgzjYA4-1629957983057)(C:\Users\张盛滨\AppData\Roaming\Typora\typora-user-images\image-20210826115203322.png)]

  1. Servlet接口有一个实现类是GenericServlet,而GenericServlet有一个子类是HttpServlet,我们创建Servlet的时候会选择继承HttpServlet,因为它里面相当于也实现了Servlet接口,并且对一些方法做了默认实现;而且子类的功能会比父类的更加强大
  2. 我们编写Servlet类继承HttpServlet的时候,只需要重写doGet()和doPost()方法就行了,因为HttpServlet重写了service()方法,在service()方法中判断请求方式,根据不同的请求方式执行doXXX()方法

在这里插入图片描述
同时,由上图可知,GenericServler类和HttpServlet等类也实现了ServletConfig接口, 故有必要了解ServletConfig接口中定义的方法:

ServletConfig接口方法介绍:

方法名作用
getServletName()获取HelloServlet定义的Servlet名称
getServletContext()获取ServletContext对象
getInitParameter()获取配置Servlet时设置的『初始化参数』,根据名字获取值
getInitParameterNames()获取所有初始化参数名组成的Enumeration对象

编写一个Servlet程序

  1. 编写一个普通类
  2. 实现Servlet接口,这里我们直接继承HttpServlet
package com.carson.servlet;

/*自动用了父模块的包*/
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入了doGet方法");
        PrintWriter out = resp.getWriter();
        out.print("Hello,Carson!I am Servlet!");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

  1. 编写Servlet映射(web.xml中注册servlet)

为什么需要映射: 我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务即web.xml配置文件中注册我们写的servlet,还需给它一个浏览器能够访问的路径!

<?xml version="1.0" encoding="UTF-8"?>

<!--    web.xml中是配置我们web应用的核心配置-->
<!--替换为webapp4.0版本和tomcat一致-->
<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_4_0.xsd"
         version="4.0"
         metadata-complete="true">

    <!--注册Servlet-->
    <servlet>
    	<!-- 相当于给该Servlet取个名,一般使用Servlet的类名/类名首字母改小写 -->
        <servlet-name>helloServlet</servlet-name>
        <!--你要配置的那个Servlet的全限定名-->
        <servlet-class>com.carson.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--配置Servlet的请求路径,将Servlet和请求路径关联起来-->
    <servlet-mapping>
    	<!--和servlet标签中的servlet-name保持一致-->
        <servlet-name>helloServlet</servlet-name>
        <!--这就是要给HelloServlet配置的映射路径,以/开头-->
        <url-pattern>/carson</url-pattern>
    </servlet-mapping>

</web-app>

  1. 配置Tomcat

    先添加war包,然后再配置项目的发布路径就可以了。
    在这里插入图片描述

  2. 启动Tomcat进行测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XzhI2hy-1629957983060)(C:\Users\张盛滨\AppData\Roaming\Typora\typora-user-images\image-20210826120016890.png)]


Servlet运行原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Kmsygmb-1629957983063)(C:\Users\张盛滨\AppData\Roaming\Typora\typora-user-images\image-20210826123146365.png)]


Servlet的生命周期

什么是Servlet的生命周期

Servlet的生命周期就是servlet从创建到销毁的过程,我们所要去探讨的就是Servlet对象在什么时候创建出来以及在什么时候销毁。
当然创建和销毁Servlet对象的工作是不需要我们去做的。

Servlet对象什么时候创建?

默认情况下是在第一次有请求访问该Servlet实例的时候才会创建该Servlet对象

Servlet对象什么时候销毁?

在服务器关闭,或者当前项目从服务器中移除的时候会销毁当前项目中的所有Servlet对象


Servlet的生命周期方法

在Servlet的生命周期中必然会经历的方法我们称之为Servlet的生命周期方法,总共包含三个方法: init、service、destroy

init方法:

该方法会在Servlet实例对象被创建出来之后执行,我们可以在该方法中获取当前Servlet的初始化参数,以及进行一些读取配置文件之类的操作

service方法:

该方法会在Servlet实例对象每次接收到请求的时候均执行,我们可以在该方法中接收、处理请求,以及将客户端需要的数据响应给客户端

destroy方法:

该方法会在Servlet实例对象销毁之前执行,我们可以在该方法中做一些资源回收、释放、关闭等等操作


配置Servlet提前创建

为什么Servlet需要提前创建?

有时候我们需要在Servlet创建的时候做一些资源加载等等耗时操作,所以如果Servlet在第一次接收请求的时候才创建的话必然会影响用户的访问速度,所以此时我们需要让Servlet提前创建,将Servlet的创建提前到服务器启动的时候。

具体要如何进行配置Servlet提前创建?

通过修改web.xml中Servlet的配置可以实现:

<!-- 配置Servlet本身 -->
<servlet>
    <!-- 全类名太长,给Servlet设置一个简短名称 -->
    <servlet-name>HelloServlet</servlet-name>
    <!-- 配置Servlet的全类名 -->
    <servlet-class>com.drimwai.servlet.HelloServlet</servlet-class>
    <!-- 配置Servlet启动顺序 -->
    <load-on-startup>1</load-on-startup>
</servlet>

web.xml配置错误统一跳转页面

项目中可能会因为用户输错地址或其他原因导致404找不到页面的错误.
这个时候可以使用web.xml配置统一处理,当发生404错误的时候统一跳转到一个页面

示例配置:

<error-page>
    <!-- 路径不正确 -->
    <error-code>404</error-code>
    <location>/WEB-INF/view/error.html</location>
  </error-page>

Servlet的映射路径

为什么要配置Servlet的映射路径?

『映射路径』:Servlet并不是文件系统中实际存在目录或文件,所以为了方便浏览器访问,我们创建了映射路径来访问它。
映射路径的作用

Servlet的映射路径是提供一个让别人能够访问该Servlet的路径,例如Servlet的映射路径是"/hello",那么在浏览器上访问该Servlet的路径是http://localhost:8080/项目部署名/hello

注意: 一个Servlet可以配置多个映射路径,但是多个Servlet不能配置相同的映射路径

映射路径的分类

完全路径匹配

访问当前Servlet的路径需要和配置的映射路径完全一致,例如Servlet的配置是/demo01,那么访问该Servlet的时候的路径也必须是http://localhost:8080/项目部署名/demo01才可以访问到。

目录匹配

/ 开始需要以 * 结束,
: Servlet里面用的不多, 但是过滤器里面通常就使用目录匹配。

例如:  
配置/*,访问的路径可以是“/任意字符串”,比方: /aa, /aaa; 
配置 /aa/*,访问的路径可是“/aa/任意字符串”,比方: /aa/b, /aa/cc
扩展名匹配

*开头,以.扩展名结束,能够匹配所有以.相同扩展名结尾的请求路径

例如:  *.action; 
访问路径可以是: 任意字符串.action
比方: aa.action, bb.action, c.action;

关于web.xml的url映射中//*的区别:

  1. /会匹配到/login这样的路径型url,不会匹配到模式为*.jsp这样的后缀型url
  2. /*会匹配所有url:路径型的和后缀型的url(包括/login,.jsp,.js和*.html等)

以helloServlet为例

  • 当一个servlet指定单独一个映射路径时。

映射路径书写:

<!--注册Servlet-->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.carson.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--Servlet只指定一个单独的映射路径,即/carson-->
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/carson</url-pattern>
    </servlet-mapping>
  • 当一个servlet指定多个映射路径

映射路径书写:

    <!--注册Servlet-->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.carson.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--Servlet指定多个映射路径-->
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/carson</url-pattern>
    </servlet-mapping>
	<servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/carson1</url-pattern>
    </servlet-mapping>
	<servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/carson2</url-pattern>
    </servlet-mapping>
  • 当一个servlet指定一个通用的映射路径(*表示)
    <!--注册Servlet-->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.carson.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--Servlet指定一个通用的映射路径-->
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/carson/*</url-pattern>
    </servlet-mapping>
  • 令一个servlet为默认的请求路径(配置后会将默认的请求index.jsp变为默认请求设定的servlet) 【更改首页
    <!--注册Servlet-->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.carson.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--Servlet指定默认的映射路径-->
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

	<!-- 把首页指向helloServlet -->
    <welcome-file-list>
        <welcome-file>hello</welcome-file>
    </welcome-file-list>
  • 自定义后缀实现请求映射(以自定义以 .carson结尾为例)

注意: *号前面不能加项目映射的路径/符号】

错误写法:

 <url-pattern>/carson/*.carson</url-pattern> 
 <url-pattern>/*.carson</url-pattern> 

正确写法:

<url-pattern>*.carson</url-pattern> 
映射路径的优先级问题
  • 指定了 固有的映射路径 的优先级最高。
  • 如果找不到才会走默认的处理映射路径。

ServletContext 域对象

web容器在启动的时候,它会为每个web应用程序都创建一个对应的ServletContext对象,此对象代表了对应的web应用。

域对象就是在一定的作用域范围内进行数据共享的对象,ServletContext作为全局域对象可以在整个项目的所有动态资源(包含所有Servlet)中进行数据共享。

服务器为其部署的每一个应用(项目)都创建了一个ServletContext对象。ServletContext属于整个项目的,该项目中的所有Servlet都可以共享同一个ServletContext对象。

在这里插入图片描述
整个web项目部署之后,只会有一个应用域(ServletContext)对象,所有客户端都是共同访问同一个应用域对象,在该项目的所有动态资源中也是共用一个应用域对象。


获取ServletContext的API

调用Servlet自身的getServletContext方法获取

ServletContext ServletContext = getServletContext();

调用ServletConfig接口的getServletContext方法

ServletContext servletContext = servletConfig.getServletContext();
//HttpServletRequest对象也实现了ServletConfig接口,所以也有getServletContext()方法
ServletContext ServletContext = request.getServletContext();

ServletContext 域对象使用案例

目标

在ServletDemo01中往全局域对象中存入"username"为"aobama"的键值对,然后在ServletDemo02中从全局域对象中根据"username"获取对应的值

ServletDemo01中的代码

//1. 获取ServletContext对象
ServletContext servletContext = getServletContext();
//2. 存入数据
String str = "Jay Chow";
//将str设置到servletContext里面
servletContext.setAttribute("str",str);

ServletDemo02中的代码

//1. 获取ServletContext对象
ServletContext servletContext = getServletContext();
//2. 取出数据
String str = (String) servletContext.getAttribute("str");
System.out.println("在ServletDemo02中获取ServletContext域对象中的str = " + str);

ServletContext的具体应用

  • 共享数据: 在一个servlet中保存的数据,可以在另外一个setvlet拿到

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6kjrtg5a-1629957983066)(C:\Users\张盛滨\AppData\Roaming\Typora\typora-user-images\image-20210826130625690.png)]

  • 获取web.xml配置文件中配置的初始化参数

web.xml中;

<!--    配置一些web应用初始化参数-->
    <context-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
    </context-param>

servlet中:

 @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String url = context.getInitParameter("url");

        PrintWriter writer = resp.getWriter();
        writer.print(url);


    }
  • 请求转发

servlet中:

package com.carson.servlet;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ServletDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        System.out.println("进入了SetvletDemo02");
        //        转发的请求路径
        RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
        //     调用forward(req,resp)实现转发请求
        requestDispatcher.forward(req,resp);
        System.out.println("已成功转发");

    }
}

【注意请求转发时的路径书写: 不需要加上项目名后缀,这里的/代表当前的web应用项目的路径】

  • 读取资源文件(Properties文件是Java中常用的一种配置文件)

新建Propertoes文件的两种方式:

  1. 可以在java包目录下新建Properties文件(如:aa.properties)
  2. 可以在resouuces资源目录下新建Properties(如:db.properties)

在这里插入图片描述

当不在web.xml中进行资源导出配置相关配置时:

上面两种方式 文件都会被打包到同个路径下: 即类文件路径classes中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zw1LxlZs-1629957983069)(C:\Users\张盛滨\AppData\Roaming\Typora\typora-user-images\image-20210826133049805.png)]

而当对web.xml中进行资源导出配置相关配置时:

<!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>**/*.properties</include>
                    <exclude>**/*.xml</include>
                </excludes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

resources资源目录下新建Properties仍然是输出到类文件路径classes中(如db.properties),而java包目录下新建Properties的方式的输出目录是与.class文件同个目录下(如aa.properties)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hL4EdTS-1629957983070)(C:\Users\张盛滨\AppData\Roaming\Typora\typora-user-images\image-20210826132806742.png)]

配置文件db.properties中:

username=root
password=root

servlet中读取资源文件:

: 读取资源文件需要将其转为一个文件流

package com.carson.servlet;

import com.mysql.cj.callback.UsernameCallback;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Properties;

public class ServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
        Properties prop = new Properties();
        prop.load(is);
        String username = prop.getProperty("username");
        String password = prop.getProperty("password");

        PrintWriter out = resp.getWriter();
        out.print(username+","+password);

    }
}


HttpServletRequest和HttpServletResponse

Web服务器收到客户端的http请求,针对这个请求,其将会分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse对象。

  • 如果要获取客户端请求过来的参数: 使用 HttpServletRequest对象
  • 如果要给客户端响应一些信息: 使用HttpServletResponse对象

HttpServletResponse对象

Response的概念

在Servlet API中,定义了一个HttpServletResponse接口(doGet,doPost方法的参数),它继承自ServletResponse接口,专门用来封装HTTP响应消息
由于HTTP响应消息分为响应行、响应头、响应体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应头、响应体的方法。

用我们自己的话说: Response就是服务器端一个对象,它里面可以封装要响应给客户端的响应行、头、体的信息。

Response的组成部分

  1. 响应行: 包含响应状态码、状态码描述信息、HTTP协议的版本
  2. 响应头: 一个个的键值对,每一个键值对都包含了具有各自含义的发送给客户端的信息
  3. 响应体: 用于展示给客户端的超文本、图片,或者供客户端下载或播放的内容

Response的作用:

  1. 设置并返回响应信息(包括:响应行、响应头、响应体)
  2. 进行重定向

Response主要应用示例:

  1. Response向浏览器响应超文本

向浏览器输出超文本: response.getWriter().write(“文本”),
此时如果输出的是中文就会出现中文乱码问题

  •    解决中文乱码问题: response.setContentType("text/html;charset=UTF-8")
    

响应超文本示例Servlet:

/**
 * 使用response:
 * 1. 设置响应状态码: setStatus(状态码)
 *    常见的状态码:200请求成功、302重定向、403权限拒绝、404资源未找到、500服务器异常
 * 2. 设置响应体的内容:
 *    2.1 向浏览器输出超文本: response.getWriter().write("文本"),此时如果输出的是中文就会出现中文乱码问题
 *        解决中文乱码问题: response.setContentType("text/html;charset=UTF-8")
 *    2.2 向浏览器输出文件: response.getOutputStream();获取字节输出流,就可以向客户端输出文件
 */
public class ResponseDemo01 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置响应状态码
        response.setStatus(403);
        //使用response向浏览器输出一个超文本(字符串)
        //解决输出中文文本时候的乱码问题
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("<h1>你好世界</h1>");
    }
}
  1. Response向浏览器响应文件

向浏览器输出文件所使用的API :response.getOutputStream();

获取字节输出流,就可以向客户端输出文件

响应图片文件示例Servlet:

public class ResponseDemo02 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //当用户访问ResponseDemo02的时候,我们要向客户端输出一张图片:imgs/logo.png
        //1. 使用字节输入流读取img里面的那张logo.png
        //1.2 使用ServletContext对象的getRealPath()方法动态获取logo.png的部署目录下的路径
        String realPath = getServletContext().getRealPath("imgs/logo.png");
        FileInputStream is = new FileInputStream(realPath);
        //2. 使用字节输出流将读取到的字节输出到浏览器
        //2.1 定义一个缓冲区
        byte[] buffer = new byte[1024];
        //2.2 定义一个读取到的字节的长度
        int len = 0;
        //2.3 获取输出流
        ServletOutputStream os = response.getOutputStream();
        //2.4 使用while循环进行边读编写
        while ((len = is.read(buffer)) != -1){
            //写
            os.write(buffer,0,len);
        }
        //关流
        os.close();
        is.close();
    }
}
  1. Resonse实现下载文件

下载文件示例Servlet:

package com.carson.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;

public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*1:要获取下载文件的路径*/
        String reaLPath = "D:\\360downloads\\wpcache\\卡森.jpg";
        /*2:下载的文件名是*/
        String fileName = reaLPath.substring(reaLPath.lastIndexOf("\\") + 1);
        /*3:通过设置让浏览器支持将要下载的东西(如果包含中文,需要使用URLEncoder.encode()编码,否则乱码)*/
        resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
        /*4:获取下载文件的输入流*/
        FileInputStream in = new FileInputStream(reaLPath);
        /*5:创建缓冲区*/
        int len = 0;
        byte[] buffer = new byte[1024];
        /*6:获取OutputStream对象*/
        ServletOutputStream out = resp.getOutputStream();
        /*7:将FileOutputStream流写入到buffer缓冲区中,使用OutputStream流将缓冲区中的数据输出到客户端*/
        while((len=in.read(buffer))>0){
            out.write(buffer,0,len);
        }
        /*涉及IO操作,需要关闭资源*/
        in.close();
        out.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

  1. Response实现验证码功能

验证码功能示例servlet:

package com.carson.servlet;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.FormatFlagsConversionMismatchException;
import java.util.Random;

public class ImageServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*让浏览器隔几秒刷新一次*/
        resp.setHeader("refresh","3");
        /*在内存中创建一个图片*/
        BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
        /*得到图片的画笔*/
        Graphics2D g = (Graphics2D) image.getGraphics();
        /*设置图片的背景颜色*/
        g.setColor(Color.white);
        g.fillRect(0,0,80,20);
        /*给图片写数据*/
        g.setColor(Color.blue);//重置画笔颜色
        g.setFont(new Font(null,Font.BOLD,20));//设置字体
        g.drawString(makeNum(),5,20);//写入随机生成的数字字符串
        /*告诉浏览器,这个请求用图片的方式打开*/
        resp.setContentType("image/jpeg");
        /*网站存在缓存,不让浏览器缓存,-1表示不缓存*/
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");
        /*把图片写给浏览器*/
        ImageIO.write(image, "jpg", resp.getOutputStream());


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

    /*生成随机数的函数*/
    private String makeNum(){
        Random random = new Random();
        String num = random.nextInt(999999)+" ";//输入的数字的位数代表生成的数字位数
        StringBuffer sb = new StringBuffer();//StringBuffer是可变长和改变的字符串
        //保证输出的是6位数
        for (int i = 0; i < 6-num.length() ; i++) {
            sb.append(0);
        }
        num = sb.toString()+num;
        //返回num(生成的随机数)
        return num;
    }
}

  1. Response实现重定向

什么是重定向?

重定向是由项目中的一个资源跳转到另一个资源,在这个过程中客户端会发起新的请求。

重定向的API:

response.sendRedirect("路径");

重定向示例servlet:

package com.carson.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*原理:
           resp.setHeader("Location","/response/img");
           resp.setStatus(302);临时重定向
           */
        //实现重定向(注意路径需要加项目名,这里的/表示的是站点的根目录(不包括项目名))
        resp.sendRedirect("/response/img");
    }
}


HttpServletRequest对象

Request的概念

在Servlet API中,定义了一个HttpServletRequest接口,它继承自ServletRequest接口,专门用来封装HTTP请求消息。由于HTTP请求消息分为请求行、请求头和请求体三部分,因此,在HttpServletRequest接口中定义了获取请求行、请求头和请求消息体的相关方法。

用我们自己的话来理解: Request就是服务器中的一个对象,该对象中封装了HTTP请求的请求行、请求头和请求体的内容。

Request的组成

  1. 请求行: 包含请求方式、请求的url地址、所使用的HTTP协议的版本
  2. 请求头: 一个个的键值对,每一个键值对都表示一种含义,用于客户端传递相关的信息给服务器
  3. 请求体: POST请求有请求体,里面携带的是POST请求的参数,GET请求没有请求体

Request的作用

  1. 获取HTTP请求三部分内容(行,头,体)
  2. 进行请求转发跳转
  3. 作为请求域对象进行存取数据

HttpServletRequest代表客户端的连接,用户通过http协议访问服务器,Http请求中所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获取客户端的信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hSF0BEtp-1629957983073)(C:\Users\张盛滨\AppData\Roaming\Typora\typora-user-images\image-20210826140225870.png)]

Request对象的常见应用示例:

  1. Request获取HTTP请求行的内容:

获取请求行的API:

  • getMethod(); 获取请求方式
  • getContextPath(); 获得当前应用上下文路径
  • getRequestURI(); 获得请求地址,不带主机名
  • getRequestURL(); 获得请求地址,带主机名
public class RequestDemo01 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //使用request对象获取请求行的信息:
        //1. 获取请求的方式(可能会用)
        String method = request.getMethod();
        //System.out.println("请求方式为:" + method);;

        //2. 获取请求的url: 统一资源定位符 http://localhost:8080/requestDemo/demo01
        String url = request.getRequestURL().toString();
        //System.out.println("此次请求的url是:" + url);

        //3. 获取请求的uri: 统一资源标识符,在url的基础上省略了服务器路径"http://loaclhost:8080"
        String uri = request.getRequestURI();
        System.out.println(uri);
    }
}
  1. Request获取HTTP请求头的内容:

获取请求头的API:

  • getHeader(String name), 根据请求头的name获取请求头的值
public class RequestDemo02 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //根据请求头的name获取value
        //目标:获取name为user-agent的请求头的信息
        //user-agent请求头中包含的是客户端浏览器信息
        String header = request.getHeader("user-agent");
        System.out.println("获取的请求头agent为:" + header);
    }
}
  1. Request获取HTTP请求参数:

请求参数的概念:

请求参数是客户端携带给服务器的由键值对组成的数据,例如"username=aobama&password=123456"这种类型的数据

客户端携带请求参数的形式

  • URL地址后面附着的请求参数,例如http://localhost:8080/app/hellServlet?username=汤姆

  • 表单携带请求参数

  • Ajax请求携带请求参数

获取请求参数的API:

方法名返回值类型方法描述
request.getParameterMap()Map<String, String[]>获取当前请求的所有参数,以键值对的方式存储到Map中
request.getParameter(“请求参数的名字”)String根据一个参数名获取一个参数值
request.getParameterValues(“请求参数的名字”)String []根据一个参数名获取多个参数值
request.getParameterNames()Enumeration获取当前请求的所有参数的参数名

解决获取请求参数乱码:

我们当前使用的Tomcat的版本是Tomcat8以上,所以我们不需要考虑GET方式乱码的问题,因为Tomcat8及以上版本已经在配置中解决了GET请求乱码的问题。
我们只需要解决POST请求乱码问题。

解决POST请求的参数乱码只需要在获取请求参数前调用request.setCharacterEncoding("UTF-8")就行了。

  1. Request实现请求转发

什么是请求转发:

请求转发是从一个资源跳转到另一个资源,在这个过程中客户端不会发起新的请求。

请求转发的API:

request.getRequestDispatcher("路径").forward(request,response);

  • req.getRequestDispatcher("/success.jsp").forward(req,resp);
    
  1. Request请求域对象实现资源共享

请求域范围:

请求域的范围只是在一次请求中的动态资源能够共享的一个范围。
在这里插入图片描述
每一次请求都有一个请求域对象,当请求结束的时候对应的请求域对象也就销毁了。

请求域对象的API:

  • 往请求域中存入数据:request.setAttribute(key,value)
  • 从请求域中取出数据:request.getAttribute(key)

使用请求域的前提:

请求域对象一定要和请求转发一起使用,因为请求域的范围是一次请求范围内,所以要在两个动态资源中使用请求域必须要进行请求转发跳转。

请求域应用示例代码:

在RequestDemo06中往请求域中存入"username"作为key,"aobama"作为值的键值对;
然后在RequestDemo07中从请求域中根据"username"取出对应的值。
RequestDemo06

public class RequestDemo06 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("RequestDemo06执行了...")
        String username = "aobama";

        //将username存储到request域对象中
        request.setAttribute("username",username);
        //请求转发跳转到RequestDemo07
        request.getRequestDispatcher("/RequestDemo07").forward(request, response);
    }
}

RequestDemo07

public class RequestDemo07 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = (String)request.getAttribute("username");
        System.out.println("在RequestDemo06中获取username:" + username);
    }
}

重定向和请求转发对比

相同点:

页面内容都会变化

不同点:

  • 请求转发

    • url不会发生变化
    • 请求转发的跳转是由服务器发起的【服务器级别】,在这个过程中浏览器只会发起一次请求
    • 请求转发只能跳转到本项目的资源,并且由于WEB-INF下面的内容都是只能由服务器级别才能访问,而请求转发就是属于服务器级别,所以请求转发也可以跳转到WEB-INF中的资源。
    • 只能将请求转发给同一个web应用程序的组件,故在这里/代表当前web应用的根目录(即路径携带项目名,如: localhost:8080/项目名)
    • 书写请求转发的路径 不需要写项目名
  • 重定向

    • url会发生变化
    • 重定向的跳转是由浏览器发起的【客户端级别】,在这个过程中浏览器会发起两次请求
    • 重定向跳转可以跳转到任意服务器的资源,由于WEB-INF下面的内容都是只能由服务器级别才能访问,但由于重定向属于客户端级别, 所以重定向无法访问WEB-INF中的资源
    • 不仅可以将请求重定向到当前web应用程序的组件,还可以重定向到同一个服务器上的其它应用程序的资源,故在这里/代表整个web站点的根目录(即路径 不携带项目名,如: localhost:8080)
    • 书写 重定向的路径 需要写项目名

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wBOlYQU5-1629957983071)(C:\Users\张盛滨\AppData\Roaming\Typora\typora-user-images\image-20210826135645135.png)]


关键名词区分

URL

URL的概念

url是uniform Resource Locater的简写,中文翻译为统一资源定位符,它是某个互联网资源的唯一访问地址,客户端可以通过url访问到具体的互联网资源。

URL的组成

在这里插入图片描述

URL的使用场景

客户端访问服务器的资源,或一台服务器中要访问另外一台服务器的资源都是通过url访问。


URI(Uniform Resource Identifier)

URI的概念

URI是Uniform Resource Identifier的缩写,中文翻译为统一资源标识符, 它是服务器中某个资源的唯一标识,通过uri可以实现同一项目中的某个资源中访问另一个资源

URI的组成

uri的写法是/项目部署名/资源路径
: URI = 应用名+资源名

在这里插入图片描述

URI的使用场景

在同一个项目的某个资源中访问该项目中的另一个资源。

URI和URL的区别:

URI可以表示一个域,也可以表示一个资源。
URL只能表示一个资源。


URI路径的使用

相对路径的使用 (不建议使用)

相对路径的概念

相对路径是不以/开头的路径写法,编写相对路径的原则是以目标资源的uri路径相对当前资源的uri路径

相对路径实例

目标: 在A资源中访问B资源

A资源的uri路径: /app/pages/a.html

B资源的uri路径:/app/static/vue.js

那么要实现在A资源中访问B资源的相对路径写法是../static/vue.js,其中../static表示找到当前资源的上一级目录下的static目录

绝对路径的使用(建议使用)

绝对路径的概念

绝对路径是以/开头的路径写法,编写绝对路径的原则是通过目标资源的uri访问目标资源。但是特殊情况是请求转发,如果是请求转发访问目标资源的话,那么绝对路径是在uri的基础之上省略/项目部署名

绝对路径实例

目标: 在A资源中访问B资源

A资源的uri路径: /app/pages/a.html

B资源的uri路径:/app/static/vue.js

那么要实现在A资源中访问B资源的绝对路径写法是/app/static/vue.js

在请求转发的时候使用绝对路径

请求转发的时候绝对路径的写法是/资源名,其实就是在uri的基础上省略/项目部署名

工作目录和部署目录

工作目录

是我们自己写代码创建工程时的目录,如下:
在这里插入图片描述


部署目录

是打包后输出的部署web项目的根目录,如下:

在这里插入图片描述

编写资源映射路径的基准:

用户通过浏览器访问服务器,而服务器上运行的是部署目录,
所以写路径的时候参考部署目录而不是工程目录

工程目录和部署目录的对应关系:

工程目录下的web目录对应部署目录的根目录,同时部署目录的根目录也是路径中的Web应用的根目录。
在这里插入图片描述


两个类似路径概念区分

Web站点目录(路径):

其相对于上面所说的工作目录的根目录,浏览器路径体现为:

http://localhost:8080

Web应用目录(路径):

其相对于上面所说的部署目录的根目录,浏览器路径体现为:

这里的response_war是web应用名(项目发布路径)
http://localhost/response_war/


应用名和资源名

应用名

tomcat中配置的项目应用名,如下:
在这里插入图片描述


资源名

具体访问的资源的名字,如下:
在这里插入图片描述


Web项目上下文路径

动态获取上下文路径(Web应用名)

上下文路径的概念

上下文路径(context path)=/Web应用名称

为什么要动态获取上下文路径

因为我们使用绝对路径的时候需要用到资源的uri路径,而uri路径中是包含上下文路径的,所以如果采用静态方式写绝对路径,那么就会将上下文路径写死在绝对路径中;而我们在部署项目的时候,上下文路径是可变的,所以这样就会因为部署时设置的上下文路径不同从而导致绝对路径出错的问题。

动态获取上下文路径的API
request.getContextPath()

使用上述API可以动态获取项目的上下文路径,每一次获取的都是当前环境下实际的上下文路径的值!!


获取资源的真实路径

为什么需要用代码获取资源的真实路径

例如我们的目标是需要获取项目中某个静态资源的路径,不是工程目录中的路径,而是部署目录中的路径;我们如果直接拷贝其在我们电脑中的完整路径的话其实是有问题的,因为如果该项目以后部署到公司服务器上的话,路径肯定是会发生改变的,所以我们需要使用代码动态获取资源的真实路径。

获取资源真实路径的API

String realPath = servletContext.getRealPath("资源在web目录中的路径");

动态获取真实路径的优势

只要使用了servletContext动态获取资源的真实路径,那么无论项目的部署路径发生什么变化,都会动态获取项目运行时候的实际路径,所以就不会发生由于写死真实路径而导致项目部署位置改变引发的路径错误问题。


The End!!创作不易,欢迎点赞/评论!!欢迎关注个人公众号

在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值