【JavaWeb】第八章 Servlet

1、初识Servlet

Servlet是JavaEE规范之一。规范就是接口。

Servlet是JavaWeb三大组件之一,三大组件分别是Servlet程序、Filter过滤器、Listener监听器

接口

Servlet是运行在服务器上的一个Java小程序,它可以接受客户端发送过来的请求,并响应数据给客户端。

2、Servlet的HelloWorld

🍁流程:

  • 编写一个类实现Servlet接口
  • 实现service方法,处理请求,并响应数据
  • 到web.xml中去配置servlet程序的访问地址

实现代码:
code

重写service方法后,编辑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_4_0.xsd"
         version="4.0">
    <!-- servlet标签是给Tomcat配置Servlet程序 -->
    <servlet>
        <!-- servlet-name标签是给Servlet程序起一个别名,一般用类名 -->
        <servlet-name>HelloServlet</servlet-name>
        <!-- servlet-class是Servlet程序的全类名 -->
        <servlet-class>com.code.servlet.HelloServlet</servlet-class>
    </servlet>
    <!-- servlet-mapping标签给Servlet程序配置访问地址 -->
    <servlet-mapping>
        <!-- servlet-name标签的作用是告诉服务器,我当前配置的地址是给哪个Servlet程序使用 -->
        <servlet-name>HelloServlet</servlet-name>
        <!-- url-pattern标签配置访问地址 -->
        <!-- /斜杠在服务器解析的时候,表示地址为 http://ip:port/工程路径 -->
        <!-- /hello 即http://ip:port/工程路径/hello -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

启动Tomcat实例后,在url后加/hello即可在控制台看到Sevlet程序的输出。

url地址如何定位到的Servlet程序并访问

流程原理

Tip
无法实现Servlet接口或者import javax.servlet.*报错时,在lib目录中导入jsp和servlet的jar包
error

3、Servlet的生命周期

  • 执行实现Servlet接口类的构造方法
  • 执行init初始化方法
  • 执行service方法
  • 执行destory方法

其中,前两步在第一次访问Servler程序的时候调用。第三步是每次访问都会调用。第四步是在web工程停止的时候会去调用

流程

4、Servlet-请求的分发处理

 /**
     * service方法是用来处理请求和响应的
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Hello Servlet被访问了!");
        //类型转换,因为getMethod是子类特有的方法
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        
        String method = httpServletRequest.getMethod();
        
        if("GET".equals(method)){
            //System.out.println("这里写收到GET请求的code");
            doGet();
        }else if("POST".equals(method)){
            System.out.println("这里写收到POST请求的code");
        }
    }

    /**
     * 把收到GET请求要做的事定义成一个方法
     * 从而避免service方法中代码臃肿
     */
    public void doGet(){

    }

写个HTML文档表单提交到servlet

<body>
    <form action="http://localhost:8080/web/hello" method="get">
        <input type="submit" value="提交"/>
    </form>

</body>

点击提交,在控制台就可看到不同请求被分发给不同的IF分支了。

5、通过集成HttpServlet类来实现Servlet程序

实际的开发中,很少通过实现Servlet接口去实现Servlet程序,而是直接继承Servlert接口的子类,如HttpServlet:

  • 编写一个类去继承HttpServlet类
  • 根据业务需要重写doGet和doPost方法
  • 到web.xml中的配置Servlet程序的访问地址
public class ServletHello extends HttpServlet{

...
Alt+Insert
}

LJcsdn
以上是原理,在IDEA中可以直接生成Servlet程序:
在这里插入图片描述

在这里插入图片描述

这样可以直接生成xx.java和修改好web.xml文件。

整理Servlet类的继承体系:
.
.

继承体系

6、ServletConfig类

顾名思义即Servlet程序的配置信息类。有大三作用:

  • 可以获取Servlet程序的别名,即servlet-name的值
  • 获取初始化参数init-param
  • 获取ServletContext对象

☀代码实现:

关于初始化参数:
web.xml

System.out.println("Servlet别名"+servletConfig.getServletName());

System.out.println("Servlet程序初始化参数username的值是" + servletConfig.getInitParameter("username");

System.out.println(servletConfig.getServletContext());

run

另外:

Servlet程序和ServletConfig对象都是由Tomcat服务器负责创建,我们负责使用。Servlet程序默认第一次访问的时候创建,ServletConfig是每个Servlet程序创建时,就创建一个对应的Servletconfig对象。


因此,每个servletConfig对象只能获取它自己对应的那个servlet程序的信息,获取不了其他servlet程序

对象

在其他方法中,可以这样获取servletConfig对象:

//也可以这样获取servletConfig对象
ServlerConfig servletConfig = getServletConfig();


常见错误–servletConfig对象空指针异常

重写init方法后,出现servletConfig对象空指针异常,加这一句:

@Override
public void init(ServletConfig config) throws ServletException{
	super.init(config);
	...
}

调用父类的init方法:
父类

7、ServletContext类

☀介绍

  • ServletContext是一个接口,它表示Servlet上下文对象
  • 一个web工程,只有一个ServletContext对象
  • ServletContext是在Web工程部署启动的时候创建,在web工程停止的时候销毁
  • ServletContext是一个域对象

域对象是指可以像Map一样存取数据的对象,这里的域是指存取数据的操作范围,即整个Web工程

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

☀作用

  • 获取web.xml中配置的上下文参数
  • 获取当前的工程路径
  • 获取工程部署后在服务器硬盘上的绝对路径
  • 像Map一样存取数据

代码实现:
web.xml

public void doSome(){
        ServletContext context = getServletConfig().getServletContext();
        //获取context上下文参数
        String username = context.getInitParameter("username");
        //获取当前的工程路径
        String path = context.getContextPath();
        /**
         * 获取工程在服务器硬盘上的绝对路径
         * 斜杠/即会被服务器解析地址为http://ip:port/工程名
         */
        String realPath = context.getRealPath("/"); //工程部署路径
        String realImgPath = context.getRealPath("/img"); //img目录的绝对路径
        
        context.setAttribute("key1","value1");
        System.out.println("context中获取域数据key1的值是:"+context.getAttribute("key1"));
}

另外,获取ServletContext对象也可直接调用getServletContext()方法,看底层源码:
源码

8、HTTP协议

协议

协议,即双方或多方相互约定好的、大家都需要遵守的规则。

HTTP协议,指客户端和服务端之间通信时,发送的数据要遵守的规则,就是HTTP协议。HTTP协议中的数据又叫报文。

请求

客户端给服务端发送数据叫请求,服务端给客户端回传数据叫响应。请求有GET和POST两种:

  • GET请求

请求行:

  • 请求的方式 GET
  • 请求的资源路径 [+?+请求参数]
  • 请求的协议版本号 HTTP/1.1
如:

GET /web/submit.html HTTP/1.1

请求头:

一组组的key:value格式
请求头

  • POST请求

请求行:

  • 请求的方式 POST
  • 请求的资源路径 [+?+请求参数]
  • 请求的协议版本号 HTTP/1.1
如:

POST /web/submit HTTP:1.1

请求头:

一组组key:value。空行后接着是请求体
请求头
Cache-Control表示如何控制缓存,no-cache即不缓存

请求体:

即发送给服务器的数据
请求体

场景区分

GET请求常包括:

  • form标签中 method=get
  • a标签
  • link标签引入css
  • Script标签引入js文件
  • img标签引入图片
  • iframe引入HTML页面
  • 在浏览器的地址栏中输入地址后敲回车

请求常包括:

  • form标签中method=post

响应

响应行:

  • 响应的协议和版本号
  • 响应的状态码
  • 响应状态描述符
如:

HTTP/1.1 200 OK

常见的响应码有:

  • 200:请求成功
  • 302:表示请求重定向
  • 404:表示服务器已经收到了,但你要的数据不存在(请求地址错误)
  • 500:服务器已经收到请求,但服务器内部错误

响应头:

一组组的key:value形式,最后一个空行后就是响应体
在这里插入图片描述

响应体:

回传给客户端的数据,这里请求的是这个html页面:
响应

MIME类型

MIME即HTTP协议中的数据类型,起全称Multipurpose Internet Mail Extensions,多功能Internet邮件扩充给服务。MIME类型的格式是“大类型/小类型”,并与某一种文件的扩展名相对应。

MIME

9、HttpServletRequest类

每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析好封装到Request对象中,然后传递到service方法(doGet、doPost)中

HttpServletRequest类的常用方法

  • getRequestURI() 获取请求的资源路径
  • getRequestURL() 获取请求的统一资源定位符
  • getRemoteHost() 获取客户端的IP地址
  • getHeader() 获取请求头
  • getParameter() 获取请求的参数
  • getParameterValues() 获取请求的参数(多个值的时候)
  • getMethod() 获取请求的方式GET、POST
  • setAttribute(key,value) 设置域数据
  • getAttribute(key)获取域数据
  • getRequestDispatcher() 获取请求转发对象

示例代码:

package com.llg.web;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Arrays;

public class HttpServlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String[] hobby = request.getParameterValues("hobby");
        System.out.println("username:" + username);
        System.out.println("password:" + password);
        System.out.println("hobby:" + Arrays.asList(hobby));
        System.out.println("URI:" + request.getRequestURI());
        System.out.println("URL:" + request.getRequestURL());
        System.out.println("Client_IP:"+request.getRemoteHost());
        System.out.println("User-Agent:" + request.getHeader("User-Agent"));
        System.out.println("Request-Method:" + request.getMethod());

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /**
         * setCharacterEncoding方法设置请求的字符集为UTF-8,解决post请求的中文乱码问题
         * 注意要在获取请求参数之前调用
         */
        request.setCharacterEncoding("UTF-8");
        System.out.println("-----POST-----");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String[] hobby = request.getParameterValues("hobby");
        System.out.println("username:" + username);
        System.out.println("password:" + password);
        System.out.println("hobby:" + Arrays.asList(hobby));
    }
}

写个form用来提交信息:

<body>
    <form action="http://localhost:8080/web1/s1" method="post">
        用户名:<input type="text" name="username"></br>
        密码:<input type="text" name="password"></br>
        兴趣爱好:<input type="checkbox" name="hobby" value="c">C
        <input type="checkbox" name="hobby" value="java">Java
        <input type="checkbox" name="hobby" value="js">JavaScript
        <input type="submit">
    </form>

</body>

运行效果:
在这里插入图片描述
在这里插入图片描述

10、请求的转发

图解转发的过程:
转发
代码实现:

Servlet1的代码:

package com.llg.web;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet(name = "Servlet1", value = "/Servlet1")
public class Servlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /**
         * 获取请求参数(办事的材料)
         */
        String username = request.getParameter("username");
        System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);
        /**
         * 给材料盖一个章,并传递到Servlet2(柜台2)取查看
         */
        request.setAttribute("key1","柜台1的章");
        /**
         * 问路Servlet2(柜台2)怎么走
         * 请求的转发必须要以斜杠打头,斜杠表示地址为:http://ip:port/工程名,映射到IDEA的web目录
         */
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/servlet2");
        /**
         * 走向Servlet2(柜台2)
         */
        requestDispatcher.forward(request,response);

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

Servlet2的代码:

package com.llg.web;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet(name = "Servlet2", value = "/Servlet2")
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(柜台2)中查看参数(材料):" + username);
        /**
         * 查看柜台1是否有盖章
         */
        Object key1 = request.getAttribute("key1");
        System.out.println("柜台1是否有章:" + key1);
        /**
         * 处理自己的业务
         */
        System.out.println("Servlet2在这里干它的业务");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

配置web.xml

在这里插入图片描述

访问:
在这里插入图片描述
控制台输出:
在这里插入图片描述

请求转发的特点:

  • 浏览器地址栏没有变化
  • 他们是一次请求,因此共享Request域中的数据
  • 可以将请求转发到WEB-INF目录下,但不能访问工程以外的资源

11、base标签

实验前期准备:

先在web项目下建立这样一个目录结构,以便后续实验:
在这里插入图片描述
c.html和index.html的内容:
在这里插入图片描述
在这里插入图片描述
启动项目,就先单单利用a标签实现了跳转的效果:
在这里插入图片描述
在这里插入图片描述

使用Servlet转发:

编写ForWardC类,重写doGet方法:

在这里插入图片描述
在这里插入图片描述
到此,访问:

http://localhost:8080/web1/forwardC

即会转发到a/b/c.html。在首页html中加入上面的链接,和之前做比较:

在这里插入图片描述
重新部署后:
在这里插入图片描述
跳转成功:
在这里插入图片描述
再点击跳回首页,报错404,注意这时的路径:
在这里插入图片描述

原因分析:

在这里插入图片描述
跳回首页时a标签的路径是…/…/index.html,是一个相对路径。而 所有相对路径在工作的时候都会参照当前浏览器地址栏中的地址来跳转

用a标签的时候成功原因的分析:

在这里插入图片描述
而用转发来跳转的时候:
在这里插入图片描述

base标签

base标签可以设置当前页面中所有相对路径工作时,参照哪个路径来进行跳转

在这里插入图片描述

加入base后,每次相对路径跳转时参考的就不再是浏览器中的地址栏url,而是base的href,进而转发后的页面也能跳成功

在这里插入图片描述

12、相对路径、绝对路径和斜杠\

在JavaWeb中,关于相对路径和绝对路径,相对路径有:

  • . 表示当前目录
  • .. 表示上一级目录
  • 只有一个资源名,即当前目录/资源名

绝对路径是:

http://ip:port/工程路径/资源路径

而在web中,斜杠/是一种绝对路径:

  • 斜杠 / 如果被浏览器解析,则得到的是http://ip:port/
    在这里插入图片描述
    在这里插入图片描述
  • 斜杠/如果被服务器解析,得到的地址是:http://ip:port/工程路径
常见的地方有:

- xml中的<url-pattern>/servlet1<url-pattern>
- servletContext.getRealPath("/");
- request.getRequestDispatcher("/");
  • 特殊的是:response.sendRediect("/");是把斜杠发送给浏览器解析,得到http://ip:port/

13、HttpServletResponse类

输出流

HttpServletResponse和HttpServletRequest类一样,每次请求进来,Tomcat服务器都会创建一个Response对象传给Servlet程序使用。

HttpServletRequest表示请求过来的信息,HttpServletResponse表示所有响应的信息,如果需要设置返回给客户端的信息,则可通过HttpServletResponse对象来设置。

关于两个输出流:

  • 字节流 getOutputStream()方法,常用于下载(传递二进制数据)
  • 字符流 getWriter()方法,常用于回传字符串(用的多)

两个流不能同时使用。

在这里插入图片描述
运行Tomcat实例:

在这里插入图片描述

给客户端回传字符串

  • 代码:
    在这里插入图片描述
  • 运行:
    在这里插入图片描述
  • 当我们往客户端回传一个中文字符串的时候,会发现有乱码出现:
    在这里插入图片描述
  • 查看默认的字符集,并设置服务器的字符集为UTF-8,以解决乱码:
    在这里插入图片描述
    在这里插入图片描述
  • 通过响应头,设置浏览器也使用UTF-8
    在这里插入图片描述
    在这里插入图片描述
response.setHeader("Content-Type","text/html; charset=UTF-8");
  • 最高效的是同时设置服务器和客户端都使用UTF-8字符集,还设置了响应头
    在这里插入图片描述
    注意setContentType()方法虽然高效,但必须在获取流对象之前使用

14、请求重定向

请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说:我给你一个新地址,你去新地址访问。这就是请求的重定向(因为之前的地址可能已经废弃)。这就像生活中某旺铺搬迁,会在门口贴告示:新店已搬迁至南京路300弄

在这里插入图片描述
注意和之前的转发做区分!

代码实现:

Response1:

package com.llg.web;

import ...

public class Response1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("曾到Response1一游");
        /**
         * 设置响应状态码302,表示重定向(已搬迁)
         */
        resp.setStatus(302);
        /**
         * 设置响应头,说明新的地址在哪儿
         */
        resp.setHeader("Location","http://localhost:8080/web1/response2");
        //resp.sendRedirect("http://localhost:8080/web1/response2");
    }
}

Response2:

package com.llg.web;

import ...

public class Response2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("this is Response2's result!!");
    }
}

配置web.xml文件:
在这里插入图片描述

访问已废弃的localhost:8080/web1/response1:
在这里插入图片描述
请求重定向的特点:(注意和之前的转发做区分)

  • 浏览器的地址栏会发生变化
  • 实际是两次请求,且不共享Request域中的数据
  • 不能访问WEB-INF下的资源,但可以转向访问工程外的资源,如www.baidu.com。
请求重定向,除了以上的:
resp.setStatus(302);
resp.setHeader("Location","http//localhost:8080/web1/response2");


还可直接调用方法sendRedirect()
resp.sendRedirect("http//localhost:8080/web1/response2");

两种重定向方式的效果都一样:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

-代号9527

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

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

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

打赏作者

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

抵扣说明:

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

余额充值