Servlet技术入门(视频学习笔记)

一、Servlet技术

1、什么是Servlet

  1. Servlet是JaveEE规范之一,规范就是接口
  2. Servlet是JavaWeb三大组件之一。三大组件分别是:Servlet程序、Filter过滤器、Listener监听器
  3. Servlet是运行在服务器上的一个java小程序,它可以接收客户端发送来的请求,并响应数据给客户端

2、手动实现Servlet程序

  1. 编写一个类去实现Servlet接口
  2. 实现service方法,处理请求,并响应数据
  3. 到web.xml中去配置程序的访问地址
package com.first.cyh;

import jakarta.servlet.*;

import java.io.IOException;

public class HelloServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Hello Servlet!");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}
<?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_4_0.xsd"
         version="4.0">

    <!--给Tomcat配置Servlet程序-->
    <servlet>
        <!--给Servlet程序起一个别名,一般是类名-->
        <servlet-name>HelloServlet</servlet-name>
        <!--Servlet程序的全类名-->
        <servlet-class>com.first.cyh.HelloServlet</servlet-class>
    </servlet>

    <!--给Servlet程序配置访问地址-->
    <servlet-mapping>
        <!--告诉服务器我当前配置的地址给哪个Servlet程序用-->
        <servlet-name>HelloServlet</servlet-name>
        <!--
            配置访问地址
            /斜杠在服务器解析时表示地址为http://ip:port/工程路径
            /hello表示地址为http://ip:port/工程路径/hello
        -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

3、url地址到Servlet程序的访问

在这里插入图片描述

4、Servlet的生命周期

  1. 执行Servlet构造器方法
  2. 执行init初始化方法
    • 第一、二步是在第一次访问的时候创建Servlet程序时才调用
  3. 执行service方法
    • 每次访问都会调用
  4. 执行destroy销毁方法
    • 在web工程停止的时候调用

5、GET和POST请求的分发处理

package com.first.cyh;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;

public class HelloServlet implements Servlet {

    public HelloServlet() {
        System.out.println("你好鸭");
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("初始化成功咯!");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //ServletRequest和HttpServletRequest是两个接口,HttpServletRequest继承自ServletRequest
        //此处的形式参数其实是HttpServletRequest接口的实现类,因为接口不能实例化,所以传进来的是实现类
        //因为需要实现ServletRequest到HttpServletRequest的强制类型转换(父类到子类)
        //只有父类对象是用子类构造方法生成的才能实现强制转换,所以传进来的参数应该是HttpServletRequest接口的实现类

        //强制类型转换,因为HTTPServletRequest接口才有getMethod方法来判断请求的类型
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String method = httpServletRequest.getMethod();
        if(method.equals("GET")){
            doGet();
        }
        else if("POST".equals(method)){
            doPost();
        }
    }

    private void doGet() {
        System.out.println("我是GET请求");
    }

    private void doPost() {
        System.out.println("我是POST请求");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("拜拜QAQ");
    }
}

6、通过继承HttpServlet实现Servlet程序

一般在实际项目开发中,都是使用继承HttpServlet类的方式去实现Servlet程序

  1. 编写一个类去继承HttpServlet类(Servlet是接口,HttpServlet是类)
  2. 根据业务需要重写doGet()或doPost()方法
  3. 到web.xml中配置Servlet程序的访问地址
package com.first.cyh;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class HelloServlet2 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("hello doPost");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("hello doGet");
    }
}
<?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_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.first.cyh.HelloServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
    <!--新增部分-->
    <servlet>
        <servlet-name>HelloServlet2</servlet-name>
        <servlet-class>com.first.cyh.HelloServlet2</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>HelloServlet2</servlet-name>
        <url-pattern>/hello2</url-pattern>
    </servlet-mapping>
</web-app>

7、使用IDEA创建Servlet程序

  1. 项目结构->Facets->点开Web工程->在底部“根源”处把选项勾上
  2. 在需要创建Servlet程序的软件包处右键新建->Servlet->为程序命名->去掉创建注解类选项的勾
  3. 配置文件自动生成改Servlet程序的配置->手动配置该Servlet程序访问路径
  4. 重写Servlet程序的doGet()和doPost()方法

8、Servlet的继承体系

Servlet接口

在这里插入图片描述

Servlet继承体系

在这里插入图片描述

二、ServletConfig类

ServletConfig类是Servlet程序的配置信息类

二者都是由Tomcat负责创建,我们负责使用

Servlet程序默认是第一次访问时创建对象,而ServletConfig是每个Servlet程序创建时就创建一个对应的ServletConfig对象

1、ServletConfig类的三大作用

  1. 可以获取Servlet程序的别名servlet-name的值
  2. 获取初始化参数init-param
  3. 获取ServletContext对象
<!--给Tomcat配置Servlet程序-->
    <servlet>
        <!--给Servlet程序起一个别名,一般是类名-->
        <servlet-name>HelloServlet</servlet-name>
        <!--Servlet程序的全类名-->
        <servlet-class>com.first.cyh.HelloServlet</servlet-class>
        <!--init-param是初始化参数-->
        
        <!--新增部分-->
        
        <!--可以配置多个参数-->
        <init-param>
            <!--参数名-->
            <param-name>username</param-name>
            <!--参数值-->
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql//localhost/test</param-value>
        </init-param>
        
        <!--新增部分-->
        
    </servlet>
public class HelloServlet implements Servlet {
	@Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("初始化成功咯!");
        //获取配置文件中servlet-name的值
        System.out.println("servlet程序的别名是" + servletConfig.getServletName());
        //获取配置文件中的初始化参数init-param
        System.out.println("初始化参数username的值是" + servletConfig.getInitParameter("username"));
        System.out.println("初始化参数url的值是" + servletConfig.getInitParameter("url"));
        //获取ServletContext对象
        System.out.println(servletConfig.getServletContext());
    }
//其他方法省略
}
//输出结果
/*
	初始化成功咯!
    servlet程序的别名是HelloServlet
    初始化参数username的值是root
    初始化参数url的值是jdbc:mysql//localhost/test
    org.apache.catalina.core.ApplicationContextFacade@5cd687b4
*/

2、注意事项

如果Servlet程序继承HttpServlet类时重写了init(ServletConfig config) throws ServletException方法,需要加上super.init(config)

//这是GenericServlet类(HttpServlet类的父类)中的方法
public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

public void init() throws ServletException {
    }

public ServletConfig getServletConfig() {
        return this.config;
    }
public class HelloServlet2 extends HttpServlet {

    @Override
    //Tomcat生成ServletConfig对象并调用init程序
    public void init(ServletConfig config) throws ServletException {
        //super.init(config);
        //如果没有调用父类的init(config)方法,则Tomcat创建的ServletConfig值丢失
        //即config属性(定义在GenericServlet类中)没有保存值
        System.out.println("doSth");
    }

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

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("hello doGet");
        //由于GenericServlet类中的config没有保存值,所以getServletConfig()方法出现空指针异常
        ServletConfig servletConfig = getServletConfig();
        System.out.println("Servlet程序的别名为" + servletConfig.getServletName());
    }
}

三、ServletContext类

1、什么是ServletContext

  1. ServletContext是一个接口,它表示Servlet上下文对象
  2. 一个web工程,只有一个ServletContext对象实例
    • ServletConfig对象取决于有多少个Servlet程序(一一对应,只能访问到配置文件中属于自己的配置信息)
    • 但ServletContext对象还能访问配置文件中上下文参数
  3. ServletContext对象是一个域对象
  4. ServletContext对象是在web工程部署启动的时候创建,在web工程停止的时候销毁

什么是域对象

域对象指的是可以像Map一样存取数据的对象

这里的域指的是存取数据的操作范围——整个web工程

存数据取数据删除数据
Mapput()get()remove()
域对象setAttribute()getAttribute()removeAttribute()

2、ServletContext类的四大作用

  1. 获取web.xml中配置的上下文参数context-param
  2. 获取当前工程路径,格式:/工程路径
  3. 获取工程部署在服务器硬盘上的绝对路径
  4. 像Map一样存取数据
1)获取信息
<?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_4_0.xsd"
         version="4.0">

    <!--新增部分-->
    
    <!--context-param是上下文参数(它属于整个web工程),可以配置多组-->
    <context-param>
        <param-name>username</param-name>
        <param-value>context</param-value>
    </context-param>
    <context-param>
        <param-name>password</param-name>
        <param-value>root</param-value>
    </context-param>
    
    <!--新增部分-->

    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.first.cyh.HelloServlet</servlet-class>
        <init-param>
            <param-name>username</param-name>
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql//localhost/test</param-value>
        </init-param>
    </servlet>

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

</web-app>
public class ContextServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取配置文件中的上下文参数
        ServletContext context = getServletConfig().getServletContext();
        System.out.println(context.getInitParameter("username"));
        System.out.println(context.getInitParameter("password"));
        //获取当前的工程路径,格式:/工程路径
        System.out.println(context.getContextPath());
        //获取工程部署后在服务器硬盘的绝对路径
        /*
        	getRealPath()返回一个给定虚拟路径的真实路径
        	这个斜杆表示站点的根,也就是获取网站的根目录,即工程部署路径
            而/css就是表示该路径下的css文件夹
         */
        System.out.println(context.getRealPath("/"));
        System.out.println(context.getRealPath("/css"));
        System.out.println(context.getRealPath("/jpg"));
    }

}

上述程序执行后会返回

context
root
/myfirst
E:\eclipse-workspace\Javaweb-servlet\out\artifacts\Javaweb_servlet_war_exploded
E:\eclipse-workspace\Javaweb-servlet\out\artifacts\Javaweb_servlet_war_exploded\css
E:\eclipse-workspace\Javaweb-servlet\out\artifacts\Javaweb_servlet_war_exploded\jpg

而为什么获得的路径为什么和我们项目创建的路径不一样,在我的另一篇博文web工程部署路径与getServletConfig().getRealPath()中详细讲解了

为什么需要获取工程存放路径呢?

因为项目在本地开发后,项目中的文件路径写的是本地的路径。然而当项目部署到其他设备后,物理地址发生改变,就会导致文件找不到而报错。

2)存取数据
public class ContextServlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //GenericServlet获取获取ServletContext对象的方式,等同于getServletConfig().getServletContext()
        ServletContext servletContext = getServletContext();
        System.out.println("保存之前Context1中获取域数据key1的值是:" + servletContext.getAttribute("key1"));
        //存数据
        servletContext.setAttribute("key1","value1");
        //取数据
        System.out.println("Context1中获取域数据key1的值是:" + servletContext.getAttribute("key1"));
    }
}
public class ContextServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = getServletContext();
        System.out.println("Context2中获取域数据key1的值是:" + servletContext.getAttribute("key1"));
    }
}

以下为两次访问ContextServlet1输出的结果(没有重新部署)

保存之前Context1中获取域数据key1的值是:null
Context1中获取域数据key1的值是:value1

保存之前Context1中获取域数据key1的值是:value1
Context1中获取域数据key1的值是:value1

重新部署后再次访问ContextServlet1输出的结果

保存之前Context1中获取域数据key1的值是:null
Context1中获取域数据key1的值是:value1

这是因为ServletContext对象是在web工程部署启动的时候创建,在web工程停止的时候销毁,所以没有重新部署的话key1在set之后就一直存在,所以第二次及以后访问时都会有保存之前Context1中获取域数据key1的值是:value1,而重新部署之后第一次访问就又是null

而前文中的getInitParameter()获取的数据是保存在xml文件中的,故一直存在

总结

setAttribute方式存参数相当于把数据存在内存中,工程停止时消失,而xml方式存参数则是持久化到硬盘中

getInitParameter获得的是xml方式存储的参数

getAttribute获得的是setAttribute方式存储的参数

而当我们已经访问完ContextServlet1后再访问ContextServlet2,会输出

Context2中获取域数据key1的值是:value1

这是因为一个web工程,只有一个ServletContext对象实例,两个Servlet程序中的servletContext其实是相同的,如果我们在两个程序中都输出servletContext这个对象,值是相同的

所以当第一个Servlet程序设置完key1参数后,第二个自然也可以使用它

四、HttpServletRequest类

1、作用

每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的Http协议信息解析封装好到Request对象中,然后传递到service方法(doGet和doPost)中给我们使用。我们可以通过HttpServletRequest对象,获得到所有请求的信息

2、常用方法

  • getRequestURI():获取请求的资源路径
  • getRequestURL():获取请求的统一资源定位符(绝对路径)
  • getRemoteHost():获取客户端的IP地址
  • getHeader():获取请求头
  • getParameter():获取请求的参数
  • getParameterValues():获取请求的参数(多个值得时候使用)
  • getMethod():获取请求的方式(GET或POST)
  • setAttribute(key, value):设置域数据
  • getAttribute(key):获取域数据
  • getRequestDispatcher():获取请求转发对象
public class RequestAPIServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("URI-->" + req.getRequestURI());//URI-->/myfirst/req
        System.out.println("URL-->" + req.getRequestURL());//URL-->http://localhost:8080/myfirst/req
        //如果以localhost访问,则返回0:0:0:0:0:0:0:10:0:0:0:0:0:0:1
        //以127.0.0.1访问,则返回127.0.0.1
        //以真实ip访问,则返回真实ip
        System.out.println("HOST-->" + req.getRemoteHost());
        //HEADER-->Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.30
        System.out.println("METHOD-->" + req.getMethod());//GET
        System.out.println("HEADER-->" + req.getHeader("User-Agent"));
    }
}
public class RequestGetParameter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("用户名" + req.getParameter("username"));
        System.out.println("密码" + req.getParameter("password"));
        String[] hobbies = req.getParameterValues("hobby");
        try{
            System.out.println("兴趣爱好" + Arrays.asList(hobbies));
        }catch (Exception e){
            System.out.println(e.getStackTrace());
        }
    }
}

注意点

如果使用的是post请求,则需要设置字符集,否则在post请求参数是中文会出现乱码

req.setCharacterEncoding(“UTF-8”);

且此设置要在所有获取参数语句(不管会不会获取中文)的前面设置

只要有获取参数的语句在设置语句前面,不管这条语句会不会获取中文,都会导致设置失效,从而使那些在设置语句后的获取参数语句同样会乱码

3、请求转发

在这里插入图片描述

public class Servlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //查看请求的带来的数据
        String username = req.getParameter("username");
        System.out.println("servlet1中请求带来的数据:" + username);
        //处理业务
        //doSth
        username += "ahahha";
        System.out.println("业务1完成,username=" + username);
        //处理完业务,在request域中存储数据
        req.setAttribute("username",username);
        //询问servlet2的路径
        //请求转发必须以斜杆开始,表示http://ip:8080/工程名/
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
        //带着请求走向servlet
        requestDispatcher.forward(req,resp);
    }
}
public class Servlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //查看请求带来的数据
        String username = request.getParameter("username");
        System.out.println("servlet2中请求带来的数据:" + username);
        //获取servlet1传入的数据
        Object obj = request.getAttribute("username");
        System.out.println("servlet1传入的数据:"+obj);
        //处理自己的业务
        System.out.println("业务处理完成");
    }
}

请求转发的特点

  1. 浏览器地址栏没有发生变化
  2. 它们是一次请求
  3. 它们共享Request域中的数据
  4. 可以转发到WEB-INF目录下
  5. 不可以访问工程以外的资源

4、base标签的作用

设置页面相对路径工作时参照的地址,href属性就是参数的地址值

应用场景

如果页面采用请求转发,跳转页面时地址不会发生改变,这时候在跳转到的页面中如果访问了设置成相对路径的地址,则会发生请求错误,因为如果没有设置base标签,默认相对地址会参照于当前浏览器的地址

5、web中’/‘的不同意义

在web中,’/'是一种绝对路径

如果被浏览器解析,得到的地址是:http://ip:port/

  • <a href="/"></a>

如果被服务器解析,得到的地址是:http://ip:port/工程路径

  • <url-pattern>/servlet1</url-pattern>
  • servletContext.getRealPath("/")
  • request.getRequestDispatcher("/")

特殊情况:response.sendRediect("/")是把斜杠发给浏览器解析,故得到的是http://ip:port/

五、HttpServletResponse类

1、作用

HttpServletResponse类和HttpServletRequest类一样,每次请求进来,Tomcat服务器都会创建一个Response对象传递给Servlet程序去使用。HttpServletRequest表示请求过来的信息,HttpServletResponse表示所有响应的信息。我们如果需要设置返回给客户端的信息,都可以通过它来设置。

2、两个输出流的说明

  • 字节流:getOutputStream()——常用于下载(传递二进制数据)
  • 字符流:getWrite()——常用于回传字符串(常用)

两个流同时只能使用一个,否则会报错

3、如何往客户端回传数据

回传字符串

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置服务器字符集
        response.setCharacterEncoding("UTF-8");
        //设置浏览器字符集
        response.setHeader("Content-Type","text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        writer.write("哈哈");
    }

设置字符集也可以使用以下方法

response.setContent-Type("text/html;charset=UTF-8")

此法会同时设置服务器和浏览器的字符集,并设置响应头

以上方法都应该在获取输出流之前调用

4、请求重定向

在这里插入图片描述

public class Response1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //第一种方法
        /*
            System.out.println("我曾来过");
            //设置状态码302,表示重定向(已搬迁)
            response.setStatus(302);
            //设置响应头,说明新的地址在哪
            response.setHeader("Location","http://localhost:8080/myfirst/response2");
        */
       	//第二种方法(推荐使用)
        response.sendRedirect("https://www.baidu.com");
    }
}
public class Response2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setHeader("Content-Type","text/html;charset=UTF-8");
        response.getWriter().write("欢迎您的到来");
    }
}

请求重定向的特点

  1. 浏览器地址栏会发生变化

  2. 两次请求

  3. 不共享Request域中的数据

  4. 不能访问WEB-INF下的资源

  5. 可以访问工程外的数据

  6. 可以访问工程外的数据

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

得过且过的勇者y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值