文章目录
8.1 官方文档
8.1.1 地址: https://tomcat.apache.org/tomcat-8.0-doc/servletapi/index.html
8.1.2 离线文档: JAVA_EE_api_中英文对照版.chm
8.1.3 Servlet 和 Tomcat 的关系: 一句话, Tomcat 支持 Servlet
8.2 为什么会出现 Servlet
- 提出需求: 请用你现有的html css javascript,开发网站,比如可以让用户留言/购物/支付, 你能搞定吗?
- 引入我们动态网页(能和用户交互)技术 => Servlet
- 对Java Web 技术体系的流程图改造说明(细化)
8.3 什么是 Servlet
-
什么是Servlet ?
① Servlet 在开发动态 WEB 工程中,得到广泛的应用 [非常重要]
② Servlet(基石)是 SpringMVC 的基础 -
Servlet (java 服务器小程序),它的特点:
- 他是由服务器端调用和执行的(一句话:是Tomcat解析和执行)
- 他是用java语言编写的,本质就是Java类
- 他是按照Servlet (接口)规范开发的 (除了tomcat->Servlet weblogic->Servlet)
- 功能强大,可以完成几乎所有的网站功能(在以前,老程员,使用Servlet开发网站)
- 不过,现在技术栈要求高,只用Servlet不行了,框架的底层还是用的 Servlet
8.4 Servlet 在 JavaWeb 项目位置
8.5 Servlet 基本使用
8.5.1 Servlet 开发方式说明
- servlet3.0 前使用 web.xml ,servlet3.0 版本以后(包括 3.0)支持注解, 同时支持 web.xml配置
- 如何查看 servlet 版本
- 后面学习 SpringBoot 时,我们用注解方式, 从 ssm,springboot 后面全部使用注解
- 这里专门讲 servlet,为让大家更清晰知道 servlet 使用原理,用配置方式(说明,原生的 Servlet 在项目中使用很少)
- 不管使用哪种方式,本质都一样
8.5.2 快速入门- 手动开发 Servlet
- 需求说明
- 开发一个 HelloServlet
- 当浏览器 访问 http://localhost:8080/web 应用/helloServlet 时,后台输出 “hi~ HelloServelt”
- 具体步骤
- 编写类HelloServlet去实现 Servlet 接口
- 实现 service 方法,处理请求,并响应数据
- 在 web.xml 中去配置 servlet 程序的访问地址
- 实现
- 创建servlet JavaWeb工程,并配置好Tomcat
- 添加servlet-api.jar(在tomcat/lib下) 到工程,因为servlet.jar 不是jdk自带的,要引入
- 在src 下 包 com.xjs.servlet.HelloServlet.java ,并实现Servlet接口
package com.xjs.servlet;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/19-02-19-14:26
* @Version: 1.0
*/
/**
* 解读:
* 1.开发一个servlet 需要实现 Servlet 接口
* 2.实现Servlet接口的5个方法
*/
public class HelloServlet implements Servlet {
//HttpServlet
private int count = 0; //属性
/**
* 1.初始化 servlet
* 2.当 Tomcat 创建 HelloServlet 实例时,会调用 init 方法
* 3.该方法只会被调用一次
*
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init() 被调用~~~");
}
/**
* 返回 ServletConfig 对象,也就是返回 Servlet 的配置
*
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 1.service 方法处理浏览器的请求(包括 get/post)
* 2.当浏览器每次请求 Servlet 时,就会调用一次 service 方法
* 3.当 tomcat 调用该方法时,会把 http 请求封装成 实现了 ServletRequest 接口的 request 对象
* 4.通过 servletRequest 对象,可以得到用户提交的数据
* 5.servletResponse 对象可以用于返回数据给 tomcat --> 浏览器
*
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
count++;
//如果count的值,在不停的累计,说明HelloServlet是单例的
System.out.println("service() 被调用~~~ count= " + count);
//tomcat每处理一次HTTP请求,就生成一个新的线程
System.out.println("当前线程id= " + Thread.currentThread().getId());
//GET 和 POST 请求的分发处理
//思路:从 servletRequest对象来获取请求方式 -> 找到其子类或者子接口
//1. servletRequest对象 没有得到 提交方法 的方法
//2. 要想到看看 servletRequest 的子接口有没有相关方法
//3. 小技巧:ctrl + alt + B 就可以看到接口的子接口和实现类
//4. 把servletRequest接口转成 HttpServletRequest 引用
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
//System.out.println(method); //GET
if ("GET".equals(method)) {
doGet(); //用doGet() 处理GET请求
} else if ("POST".equals(method)) {
doPost(); //用doPost() 处理POST请求
}
}
/**
* 用于响应get请求
*/
public void doGet() {
System.out.println("doGet() 被调用~~");
}
/**
* 用于响应post请求
*/
public void doPost() {
System.out.println("doPost() 被调用~~");
}
/**
* 返回 servlet 信息,使用较少
*
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 1.该方法是在 servlet 销毁时,被 tomcat 调用
* 2.只会调用一次
*/
@Override
public void destroy() {
System.out.println("destroy() 被调用...");
}
}
- 在web.xml配置HelloServlet,即:给HelloServlet 提供对外访问地址
<?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">
<!--小技巧:web.xml 文件主要用来配置该web应用到的Servlet-->
<!--配置 HelloServlet
解读:
1.servlet-name:给Servlet取名(程序员来决定),该名字是唯一的,不能重复
2.servlet-class:Servlet的类的全路径,因为Tomcat 在反射生成该Servlet时,需要使用
3.url-pattern:这个就是该servlet访问的url的匹配(路径)
4.这时我们应该这样访问该servlet:http://localhost:8080/servlet/helloServlet
5.url-pattern 取名是程序员来决定的,注意 / 不能丢
6.一切配置完成后,记得要 redeploy 或者 restart,然后浏览器再访问HelloServlet
7.load-on-startup:表示tomcat启动时,会自动加载servlet实例, 1 表示加载的顺序
小技巧: alt + R ==> 自己配的快捷键(settings ==> Keymap)
-->
<servlet>
<servlet-name>HelloServlet</servlet-name> <!--一般以类名作为名字-->
<servlet-class>com.xjs.servlet.HelloServlet</servlet-class>
<!--<load-on-startup>1</load-on-startup> 表示tomcat启动时,会自动加载servlet实例 -->
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name> <!--这里的名字要和上面的保持一致-->
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
- 通过浏览器访问HelloServlet ,看是否正确
8.5.3 浏览器调用 Servlet 流程分析
- 一图胜千言
8.5.4 Servlet 生命周期
- 主要有三个方法:
init()
初始化阶段service()
处理浏览器请求阶段destroy()
终止阶段
- 示意图(比较重要,而且形象)
-
初始化阶段
- Servlet 容器 (比如: Tomcat) 加载 Servlet
- 加载完成后,Servlet 容器会创建一个 Servlet 实例并调用
init()
方法 init()
方法只会调用一次
-
Servlet 容器在下面的情况装载 Servlet :
① Servlet 容器(Tomcat)启动时自动装载某些 servlet,实现这个需要在 web.xml 文件中添加
<load-on-startup>1</load-on-startup>
1 表示装载的顺序
② 在 Servlet 容器启动后,浏览器首次向 Servlet 发送请求(这个前面说过)
③ Servlet 重新装载时(比如 tomcat 进行 redeploy,redeploy 会销毁所有的 Servlet 实例),浏览器再向 Servlet 发送请求的第 1 次
- 处理浏览器请求阶段(service 方法)
- 每收到一个 http 请求,服务器就会产生一个新的线程去处理
- 创建一个用于封装 HTTP 请求消息的 ServletRequest 对象和一个代表 HTTP 响应消息的ServletResponse 对象
- 然后调用 Servlet 的
service()
方法并将请求和响应对象作为参数传递进去
-
终止阶段 destory 方法(体现 Servlet 完整的生命周期)
- web 应用被终止
- 或者 Servlet 容器终止运行
- 或者 Servlet 类重新装载时
- 都会调用
destroy()
方法,比如重启 tomcat,或者 redeploy web 应用
8.5.5 GET 和 POST 请求的分发处理
- 开发 Servlet,通常编写 doGet、doPost 方法
- 来对表单的 get 和 post 请求进行分发处理
- 代码演示
- 创建 E:\javaweb_project\servlet\web\register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet/helloServlet" method="post">
user: <input type="text" name="username"/><br/><br/>
<input type="submit" value="注册用户"/>
</form>
</body>
</html>
- 修改 E:\javaweb_project\servlet\web\src\com\xjs\servlet\HelloServlet.java,核心代码, 修改 HelloServlet.java
/**
* 1.service 方法处理浏览器的请求(包括 get/post)
* 2.当浏览器每次请求 Servlet 时,就会调用一次 service 方法
* 3.当 tomcat 调用该方法时,会把 http 请求封装成 实现了 ServletRequest 接口的 request 对象
* 4.通过 servletRequest 对象,可以得到用户提交的数据
* 5.servletResponse 对象可以用于返回数据给 tomcat --> 浏览器
*
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
count++;
//如果count的值,在不停的累计,说明HelloServlet是单例的
System.out.println("service() 被调用~~~ count= " + count);
//tomcat每处理一次HTTP请求,就生成一个新的线程
System.out.println("当前线程id= " + Thread.currentThread().getId());
//GET 和 POST 请求的分发处理
//思路:从 servletRequest对象来获取请求方式 -> 找到其子类或者子接口
//1. servletRequest对象 没有得到 提交方法 的方法
//2. 要想到看看 servletRequest 的子接口有没有相关方法
//3. 小技巧:ctrl + alt + B 就可以看到接口的子接口和实现类
//4. 把servletRequest接口转成 HttpServletRequest 引用
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
//System.out.println(method); //GET
if ("GET".equals(method)) {
doGet(); //用doGet() 处理GET请求
} else if ("POST".equals(method)) {
doPost(); //用doPost() 处理POST请求
}
}
8.5.6 通过继承 HttpServlet 开发 Servlet
- HttpServlet 介绍
在实际项目中,都是使用继承 HttpServlet 类开发 Servlet 程序,更加方便
- 应用实例
- 通过继承 HttpServlet 开发一个 HiServlet
- 当浏览器 访问 http://localhost:8080/web 应用名/hiServlet 时,后台输出 “hi HiServelt”
- 具体的开发步骤
- 编写一个类去继承 HttpServlet 类
- 根据业务需要重写 doGet 或 doPost 方法
- 到 web.xml 中的配置 Servlet 程序
- 代码实现
- 创建 HiServlet.java
package com.xjs.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/21-02-21-8:12
* @Version: 1.0
*/
public class HiServlet extends HttpServlet {
//重写 HttpServlet 的 doGet 和 doPost 方法
/**
* 处理doGet请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HiServlet doGet() ~~~");
}
/**
* 处理doPost请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HiServlet doPost() ~~~");
}
}
- 修改 web.xml 完成配置
<!--配置HiServlet-->
<servlet>
<servlet-name>HiServlet</servlet-name>
<servlet-class>com.xjs.servlet.HiServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HiServlet</servlet-name>
<url-pattern>/hiServlet</url-pattern>
</servlet-mapping>
- 完成测试
8.5.7 IDEA 开发 Servlet 程序
-
编手动开发 Servlet 需要程序员自己配置 Servlet 比较麻烦
-
在工作中,直接使用 IDEA 开发 Servlet 会更加方便
-
应用实例
- 创建 OkServlet.java
package com.xjs.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/21-02-21-8:41
* @Version: 1.0
*/
public class OkServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("OkServlet doPost()");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("OkServlet doGet()");
}
}
<!--这是idea自动生成的配置-->
<servlet>
<servlet-name>OkServlet</servlet-name>
<servlet-class>com.xjs.servlet.OkServlet</servlet-class>
</servlet>
<!--对OkServlet进行url配置-->
<servlet-mapping>
<servlet-name>OkServlet</servlet-name>
<url-pattern>/okServlet</url-pattern>
</servlet-mapping>
- 完成测试
8.5.8 Servlet 注意事项和细节
- Servlet 是一个供其他 Java 程序 (Servlet 引擎) 调用的 Java 类,不能独立运行
- 针对浏览器的多次 Servlet 请求,通常情况下,服务器只会创建一个 Servlet 实例对象
- 也就是说 Servlet 实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务
- 直至 web 容器退出/或者 redeploy 该 web 应用,servlet 实例对象才会销毁
- 在 Servlet 的整个生命周期内,init 方法只被调用一次,而对每次请求都导致 Servlet 引擎调用一次 servlet 的 service 方法
- 对于每次访问请求,Servlet 引擎都会创建一个新的 HttpServletRequest 请求对象和一个新的HttpServletResponse 响应对象,然后将这两个对象作为参数传递给它调用的 Servlet的
service()
方法,service 方法再根据请求方式分别调用 doXXX 方法 - 如果在
<servlet>
元素中配置了一个<load-on-startup>
元素,那么 WEB 应用程序在启动时,就会装载并创建 Servlet 的实例对象、以及调用 Servlet 实例对象的init()
方法,聊聊(定时发送邮件的服务/自动启动->完成务)
<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>
org.apache.catalina.servlets.InvokerServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
8.6 Servlet - 注解方式
8.6.1 快速入门
- 编写类OkServlet去继承HttpServlet
- 注解方式配置OkServlet, 一个Servlet支持配置多个urlPattern
- 代码实现
package com.xjs.servlet.annotation;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/21-02-21-9:04
* @Version: 1.0
* 注解方式来配置
*/
/**
* 解读:
* 小技巧:显示方法间的分隔线
* 1. @WebServlet 是一个注解
* 2. @WebServlet 源码:
* @Target({ElementType.TYPE})
* @Retention(RetentionPolicy.RUNTIME)
* @Documented ==> 在javadoc工具生成文档时,会有记录
* public @interface WebServlet {
* String name() default "";
*
* String[] value() default {};
*
* String[] urlPatterns() default {};
*
* int loadOnStartup() default -1;
*
* WebInitParam[] initParams() default {};
*
* boolean asyncSupported() default false;
*
* String smallIcon() default "";
*
* String largeIcon() default "";
*
* String description() default "";
*
* String displayName() default "";
* }
* 3. urlPatterns 对应 web.xml 的 <url-pattern></url-pattern>
* 4. {"ok1","ok2"} 可以给OkServlet配置多个 url-pattern
* 5. 相当于这个@WebServlet(urlPatterns = {"/ok1","/ok2"}) 代替了 web.xml的配置
* 底层使用了 反射 + 注解 + IO + 集合 来完成一个支撑
* 6. 浏览器可以访问OkServlet时,可以这样写:http://localhost:8080/servlet/ok1 或者
* http://localhost:8080/servlet/ok2
* 7. 我们可以根据 @interface WebServlet 源码知道可以配置哪些信息
* web.xml init-param 在注解中,如何指定呢? 看源码,寻找思路
* <init-param>
* <param-name></param-name>
* <param-value></param-value>
* </init-param>
* 源码==>思路:当注解里面又包含注解时
* initParams = {@WebInitParam(name = "xx",value = "yy"),@WebInitParam(name = "kk",value = "zz")}
* 8. 注解方式开发Servlet 和 web.xml 配置Servlet 流程机制是一样的
*/
@WebServlet(urlPatterns = {"/ok1","/ok2"}, initParams = {@WebInitParam(name = "xx",value = "yy"),@WebInitParam(name = "kk",value = "zz")}) //注意 / 不能少,否则会报错
public class OkServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("注解方式 OkServlet init() 被调用");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("注解方式 OkServlet doGet()");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("注解方式 OkServlet doPost()");
}
}
8.6.2 Servlet urlPattern 配置
8.6.2.1 精确匹配
- 配置路径 : @WebServlet(“/ok/zs”)
- 访问 servlet: localhost:8080/servlet/ok/zs
8.6.2.2 目录匹配
- 配置路径 : @WebServlet(“/ok/*”)
- 访问文件:
- localhost:8080/servlet/ok/aaa
- localhost:8080/servlet/ok/bbb
- …
8.6.2.3 扩展名匹配
-
配置路径 : @WebServlet(“*.action”)
-
访问文件:
- localhost:8080/xjs/xxx.action
- localhost:8080/xjs/yyy.action
- …
-
特别提示: @WebServlet(“/*.action”) ,不能带 /,否则 tomcat 报错
8.6.2.4 任意匹配
-
配置路径 :
@WebServlet("/")
,@WebServlet("/*")
-
访问文件:
- localhost:8080/xjs/aaa
- localhost:8080/xjs/bbb
- localhost:8080/xjs/ccc
-
特别提醒:/ 和 /* 的配置,会匹配所有的请求,这个比较麻烦,要避免
8.6.2.5 注意事项和使用细节
- 当 Servlet 配置了 “/”,会覆盖 tomcat 的 DefaultServlet,当其他的 utl-pattern 都匹配不上时 , 都会走这个Servlet,这样可以拦截到其它静态资源
- 查看:tomcat/conf/web.xml ,配置的 DefaultServlet
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
- 当 Servelt 配置了 “/*”,表示可以匹配任意访问路径
- 提示: 建议不要使用 / 和 /* ,建议尽量使用精确匹配
- 优先级遵守: 精确路径 > 目录路径 > 扩展名路径 > /* > /
8.7 Servlet -阶段课后作业
-
使用 idea 开发一个 CatServlet ,要求完成
① 通过实现 Servlet 接口开发该 Servlet
② 要求访问 url 为 http://localhost:8080/servlet/cat
③ 在 Servlet 的 service 方法 ,可以输出 该 servlet 访问次数,并输出访问是 get 还是 post
④ 在 web.xml 配置,编写一个 login.html ,有登录表单
package com.xjs.servlet.homework;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/21-02-21-17:17
* @Version: 1.0
*/
public class CatServlet implements Servlet {
//定义一个记录访问次数的变量
private int count = 0;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("CatServlet init() 被调用~");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
count++;
System.out.println("CatServlet service() 被调用 count= " + count);
//快捷键:可以快捷在访问的文件切换
//ctrl + alt + <- 回到上一层
//ctrl + alt + -> 回到下一层
//ServletRequest ==> HttpServletRequest
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
//get post 请求的分发处理
if ("GET".equals(method)) {
doGet();
} else if ("POST".equals(method)) {
doPost();
}
}
/**
* 处理get请求
*/
public void doGet() {
System.out.println("CatServlet doGet() 被调用~~~");
}
/**
* 处理Post请求
*/
public void doPost() {
System.out.println("CatServlet doPost() 被调用~~~");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
<!--配置CatServlet(作业1)-->
<servlet>
<servlet-name>CatServlet</servlet-name>
<servlet-class>com.xjs.servlet.homework.CatServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CatServlet</servlet-name>
<url-pattern>/cat</url-pattern>
</servlet-mapping>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/servlet/cat" method="post">
user: <input type="text" name="username"/><br/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
-
使用 idea 开发一个 DogServlet,要求完成 梳理思路
① 通过继承 HttpServlet 开发该 Servlet
② 要求访问 url 为 http://localhost:8080/servlet/dog
③ 当访问时,分别输出 get 和 post 访问的次数(即分开统计)
④ 在 web.xml 配置,使用前面的 login.html,完成测试
package com.xjs.servlet.homework;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/21-02-21-17:54
* @Version: 1.0
*/
public class DogServlet extends HttpServlet {
private int count1 = 0;
private int count2 = 0;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
count1++;
System.out.println("DogServlet doPost() 调用次数= " + count1);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
count2++;
System.out.println("DogServlet doGet() 调用次数= " + count2);
}
}
<!--配置DogServlet(作业2)-->
<servlet>
<servlet-name>DogServlet</servlet-name>
<servlet-class>com.xjs.servlet.homework.DogServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DogServlet</servlet-name>
<url-pattern>/dog</url-pattern>
</servlet-mapping>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/servlet/dog" method="post">
user: <input type="text" name="username"/><br/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
-
使用 idea 开发一个 PigServlet,要求完成
① 通过继承 HttpServlet 开发该 Servlet
② 要求访问 url 为 http://localhost:8080/servlet/pig1 或者 /pig2
③ 当访问时,分别输出 get 和 post 访问的次数(即分开统计) ,并输出浏览器/
客户端的 ip
④ 通过注解配置,使用前面的 login.html,完成测试
⑤ 要求 PigServlet,在 Tomcat 启动时,就自动加载
package com.xjs.servlet.homework;
import javax.servlet.ServletConfig;
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;
/**
* @Author: 谢家升
* @Date: 2022/2/21-02-21-18:08
* @Version: 1.0
*/
@WebServlet(urlPatterns = {"/pig1", "/pig2"}, loadOnStartup = 1)
public class PigServlet extends HttpServlet {
private int getCount = 0;
private int postCount = 0;
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("PigServlet init() 被调用~");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//我是一个 OOP 程序员,一定要考虑使用对象,
//如果这个对象没有,就去它的父类或者父接口去找
// String localAddr = request.getLocalAddr();
// System.out.println(localAddr);//127.0.0.1
String remoteAddr = request.getRemoteAddr();
// System.out.println(remoteAddr);//127.0.0.1
System.out.println("PigServlet doGet() 调用次数= " + (++getCount) + " 浏览器IP= " + remoteAddr);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String remoteAddr = request.getRemoteAddr();
//System.out.println(remoteAddr);//127.0.0.1
System.out.println("PigServlet doPost() 调用次数= " + (++postCount) + " 浏览器IP= " + remoteAddr);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/servlet/pig2" method="post">
user: <input type="text" name="username"/><br/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
- 自己手动画出访问 login.html 的完整 UML 时序图
8.8 关联篇:HTTP 协议!!!
==> 文章链接
8.9 ServletConfig
8.9.1 ServletConfig 基本介绍
- ServletConfig 类是为 Servlet 程序的配置信息的类
- Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建
- Servlet 程序默认是第 1 次访问的时候创建,ServletConfig 在 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象
8.9.2 ServletConfig 类能干什么
- 获取 Servlet 程序的 servlet-name 的值
- 获取初始化参数 init-param
- 获取 ServletContext 对象
8.9.3 ServletConfig 应用实例
- 需求: 编写 DBServlet.java 完成如下功能
- 在 web.xml 配置连接 mysql 的用户名和密码
- 在 DBServlet 执行 doGet()/doPost() 时,可以获取到 web.xml 配置的用户名和密码
- 示意图(思路分析)
- 代码实现
package com.xjs.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/24-02-24-9:09
* @Version: 1.0
*/
public class DBServlet extends HttpServlet {
/**
* 梳理 ServletConfig config 使用流程
* 1.当 DBServlet 对象初始化时,Tomcat会同时创建一个 ServletConfig对象
* 2.这时如果 DBServlet init() 方法中你调用了 super.init(config);
* 3.调用 父类 GenericServlet
* public void init(ServletConfig config) throws ServletException {
* this.config = config;
* this.init();
* }
* 这时就会把 Tomcat创建的这个 ServletConfig 对象赋给 GenericServlet的属性 config
* 4.因此如果你重写了 init() 方法,如果你在其他方法通过 getServletConfig() 方法获取 ServletConfig 对象
* 则一定要记住,调用 super.init(config);
*
* @param config
* @throws ServletException
*/
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("init() config= "+config);
super.init(config);//这里如果不写该方法,下面 52 行代码将会空指针
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//DBServlet 执行 doGet()/doPost() 时,可以获取到web.xml配置的用户名和密码
//你是OOP程序员 --> 借助现有的方法或对象来搞定
//DBServlet 的父类 GenericServlet 有 getServletConfig()
/**
* 解读:
* 1.getServletConfig() 是 GenericServlet 的一个方法
* 2.返回的 servletConfig 是 GenericServlet 的 一个属性:private transient ServletConfig config;
* 3.当一个属性被 transient 修饰,表示该属性不会被串行化(有些重要信息,不希望保存到文件)
*/
ServletConfig servletConfig = getServletConfig();
System.out.println("doPost() config= " + servletConfig);
String username = servletConfig.getInitParameter("username");
String pwd = servletConfig.getInitParameter("pwd");
System.out.println("初始化参数username= " + username);
System.out.println("初始化参数pwd= " + pwd);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
<!--配置DBServlet-->
<servlet>
<servlet-name>DBServlet</servlet-name>
<servlet-class>com.xjs.servlet.DBServlet</servlet-class>
<!--配置信息,而不是硬编码到程序-->
<init-param>
<param-name>username</param-name>
<param-value>xjs</param-value>
</init-param>
<init-param>
<param-name>pwd</param-name>
<param-value>123456</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DBServlet</servlet-name>
<url-pattern>/db</url-pattern>
</servlet-mapping>
8.10 ServletContext
8.10.1 为什么需要 ServletContext
- 需求: 统计某个 web 应用的所有 Servlet 被访问的次数,怎么办?
- 方案 1-DB
- 方案 2-ServletContext
8.10.2 ServletContext 基本介绍
- ServletContext 是一个接口,它表示 Servlet 上下文对象
- 一个 web 工程,只有一个 ServletContext 对象实例
- ServletContext 对象 是在 web 工程启动的时候创建,在 web 工程停止的时销毁
- ServletContext 对象可以通过
ServletConfig.getServletContext()
方法获得对ServletContext对象的引用,也可以通过this.getServletContext()
来获得其对象的引用 - 由于一个 WEB 应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 Servlet 对象之间可以通过ServletContext 对象来实现多个 Servlet 间通讯
- ServletContext 对象通常也被称之为域对象
8.10.3 ServletContext 可以做什么
- 获取 web.xml 中配置的上下文参数 context-param 【信息和整个 web 应用相关,而不是属于某个 Servlet】
- 获取当前的工程路径,格式: /工程路径,比如 /servlet
- 获取工程部署后在服务器硬盘上的绝对路径,比如:E:\javaweb_paoject\servlet\out\artifacts\servlet_war_exploded
- 像 Map 一样存取数据,多个 Servlet 共享数据
8.10.4 应用实例 1-获取工程相关信息
- 需求如下:
- 获取 web.xml 中配置的上下文参数 context-param
- 获取当前的工程路径,格式: /工程路径
- 获取工程部署后在服务器硬盘上的绝对路径
- 代码实现
package com.xjs.servlet.servletcontext;
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;
/**
* @Author: 谢家升
* @Date: 2022/2/25-02-25-8:07
* @Version: 1.0
*/
public class ServletContext_ extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取web.xml的context-parameter
//1.获取servletContext对象
ServletContext servletContext = getServletContext();
//2.获取相应的值
String website = servletContext.getInitParameter("website");
String company = servletContext.getInitParameter("company");
//3.获取项目的工程路径
String contextPath = servletContext.getContextPath();
//4.获取项目发布后,真正的工作路径
// / 表示我们的项目(发布后)的 根路径 E:\javaweb_project\servlet\out\artifacts\servlet_war_exploded\
String realPath = servletContext.getRealPath("/");
System.out.println("项目路径= " + contextPath);// /servlet
System.out.println("website= " + website);
System.out.println("company= " + company);
System.out.println("项目发布后的绝对路径= " + realPath); // E:\javaweb_project\servlet\out\artifacts\servlet_war_exploded\
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
<!--配置ServletContext_-->
<servlet>
<servlet-name>ServletContext_</servlet-name>
<servlet-class>com.xjs.servlet.servletcontext.ServletContext_</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletContext_</servlet-name>
<url-pattern>/servletContext_</url-pattern>
</servlet-mapping>
<!--配置整个网站的信息-->
<context-param>
<param-name>website</param-name>
<param-value>http://www.xjs.net</param-value>
</context-param>
<context-param>
<param-name>company</param-name>
<param-value>红红火火</param-value>
</context-param>
8.10.5 应用实例 2-简单的网站访问次数计数
8.10.5.1 需求分析/图解
- 需求: 完成一个简单的网站访问次数计数器:
- 使用 Chrome 访问 Servlet01, 每访问一次,就增加 1 访问次数,在后台输出,并将结果返回给浏览器显示
- 使用火狐访问 Servlet02,每访问一次,就增加 1 访问次数,在后台输出,并将结果返回给浏览器显示
package com.xjs.servlet.servletcontext;
import javax.servlet.Servlet;
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;
import java.io.PrintWriter;
/**
* @Author: 谢家升
* @Date: 2022/2/25-02-25-9:15
* @Version: 1.0
*/
public class OrderServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext对象
ServletContext servletContext = getServletContext();
//System.out.println("OrderServlet servletContext= " + servletContext + " 运行类型= " + servletContext.getClass());
//OrderServlet servletContext= org.apache.catalina.core.ApplicationContextFacade@2e36e195 运行类型= class org.apache.catalina.core.ApplicationContextFacade
从 ServletContext 获取 visit-count 属性 【类似hashMap的 k-v】
//Object visit_count = servletContext.getAttribute("visit_count");
判断visit_count是否为null
//if (visit_count == null) { //第一次访问
// servletContext.setAttribute("visit_count", 1);
// visit_count = 1;
//} else {
// visit_count = Integer.parseInt(visit_count + "") + 1;
// servletContext.setAttribute("visit_count", visit_count);
//}
Integer visit_count = WebUtils.visitCount(servletContext);
//输出显示
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>网站被访问次数是:" + visit_count + "</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
package com.xjs.servlet.servletcontext;
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;
import java.io.PrintWriter;
/**
* @Author: 谢家升
* @Date: 2022/2/25-02-25-9:17
* @Version: 1.0
*/
public class PayServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext对象
ServletContext servletContext = getServletContext();
//System.out.println("PayServlet servletContext= " + servletContext + " 运行类型= " + servletContext.getClass());
//PayServlet servletContext= org.apache.catalina.core.ApplicationContextFacade@2e36e195 运行类型= class org.apache.catalina.core.ApplicationContextFacade
从 ServletContext 获取 visit-count 属性 【类似hashMap的 k-v】
//Object visit_count = servletContext.getAttribute("visit_count");
判断visit_count是否为null
//if (visit_count == null) { //第一次访问
// servletContext.setAttribute("visit_count", 1);
// visit_count = 1;
//} else {
// visit_count = Integer.parseInt(visit_count + "") + 1;
// servletContext.setAttribute("visit_count", visit_count);
//}
Integer visit_count = WebUtils.visitCount(servletContext);
//输出显示
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>网站被访问次数是:" + visit_count + "</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
package com.xjs.servlet.servletcontext;
import javax.servlet.ServletContext;
/**
* @Author: 谢家升
* @Date: 2022/2/25-02-25-9:52
* @Version: 1.0
*/
public class WebUtils {
//这个方法就是对访问次数的一个累计,同时返回访问次数
public static Integer visitCount(ServletContext servletContext) {
//从 ServletContext 获取 visit-count 属性 【类似hashMap的 k-v】
Object visit_count = servletContext.getAttribute("visit_count");
//判断visit_count是否为null
if (visit_count == null) { //第一次访问
servletContext.setAttribute("visit_count", 1);
visit_count = 1;
} else {
visit_count = Integer.parseInt(visit_count + "") + 1;
servletContext.setAttribute("visit_count", visit_count);
}
return Integer.parseInt(visit_count + "");
}
}
<!--配置OrderServlet-->
<servlet>
<servlet-name>OrderServlet</servlet-name>
<servlet-class>com.xjs.servlet.servletcontext.OrderServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>OrderServlet</servlet-name>
<url-pattern>/orderServlet</url-pattern>
</servlet-mapping>
<!--配置PayServlet-->
<servlet>
<servlet-name>PayServlet</servlet-name>
<servlet-class>com.xjs.servlet.servletcontext.PayServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PayServlet</servlet-name>
<url-pattern>/payServlet</url-pattern>
</servlet-mapping>
- 完成测试
8.11 HttpServletRequest
8.11.1 HttpServletRequest 介绍
- HttpServletRequest 对象代表客户端的请求
- 当客户端/浏览器通过 HTTP 协议访问服务器时,HTTP 请求头中的所有信息都封装在这个对象中
- 通过这个对象的方法,可以获得客户端这些信息
8.11.2 HttpServletRequest 类图
8.11.3 HttpServletRequest 常用方法
getRequestURI()
获取请求的资源路径,比如:http://localhost:8080/servlet/loginServletgetRequestURL()
获取请求的统一资源定位符 (绝对路径) ,比如:http://localhost:8080/servlet/loginServletgetRemoteHost()
获取客户端的 主机getRemoteAddr()
获取客户端IP地址getHeader()
获取请求头getParameter()
获取请求的参数getParameterValues()
获取请求的参数 (多个值的时候使用),比如 checkbox 返回的数组getMethod()
获取请求的方式 GET 或 POSsetAttribute(key, value)
设置域数据getAttribute(key)
获取域数据getRequestDispatcher()
获取请求转发对象, 请求转发的核心对象
8.11.4 HttpServletRequest 应用实例
- 需求说明: 在一个表单提交数据给 Servlet,然后在 Servlet 通过 HttpServletRequest 对象获取相关数据
- 创建 register_request.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet/requestMethods"
method="post">
u: <input type="text" name="username"/><br><br>
p: <input type="password" name="pwd"/><br><br>
选择你喜欢的水果:
<input type="checkbox" name="hobby" value="clz">车厘子
<input type="checkbox" name="hobby" value="cm">草莓
<input type="checkbox" name="hobby" value="yt">樱桃<br/><br/>
<input type="submit" value="注册用户"/>
</form>
</body>
</html>
- 创建 HttpServletRequestMethods.java
package com.xjs.servlet.request;
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;
/**
* @Author: 谢家升
* @Date: 2022/2/27-02-27-10:05
* @Version: 1.0
*/
public class HttpServletRequestMethods extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里我们使用request对象,获取表单提交的各种数据
System.out.println("HttpServletRequestMethods doPost() 被调用~~");
/*==========================
获取和HTTP请求头相关的信息
===========================*/
System.out.println("请求的资源路径URI= " + request.getRequestURI());// /servlet/requestMethods
System.out.println("请求的统一资源定位符(绝对路径) URL= " + request.getRequestURL());
// http://localhost:8080/servlet/requestMethods
System.out.println("请求的客户端的IP地址= " + request.getRemoteAddr()); //127.0.0.1
System.out.println("HTTP请求头Host= " + request.getHeader("Host")); //localhost:8080
//如果我们希望得到请求头的相关信息,可以使用request.gerHeader("请求头字段")
System.out.println("该请求的发起地址是= " + request.getHeader("Referer"));
// http://localhost:8080/servlet/register_request.html
//取出firefox
String header = request.getHeader("User-Agent");
String[] s = header.split(" ");
//System.out.println(s[s.length-1]);//Firefox/97.0
System.out.println("浏览器= " + s[s.length - 1].split("\\/")[0]); //Firefox
System.out.println("请求方式= " + request.getMethod()); //POST
/*==========================
获取和请求参数相关的信息,注意要求在返回数据前,获取参数
===========================*/
//解决参数的中文乱码问题,注意,要写在返回参数前
request.setCharacterEncoding("utf-8");
//1.获取表单数据【单个数据】
//username=tom&pwd=123&hobby=clz&hobby=cm&hobby=yt
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
String pwd1 = request.getParameter("pwd1");
String[] hobbies = request.getParameterValues("hobby");
//输出信息
System.out.println("username= " + username);
System.out.println("pwd= " + pwd);
System.out.println("pwd1= " + pwd1);//null
for (String hobby : hobbies) {
System.out.println("喜欢的水果= " + hobby);
}
//返回接收到的信息给浏览器
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("提交的用户名= " + username);
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 修改 web.xml 添加配置
<!--配置HttpServletRequestMethods-->
<servlet>
<servlet-name>HttpServletRequestMethods</servlet-name>
<servlet-class>com.xjs.servlet.request.HttpServletRequestMethods</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HttpServletRequestMethods</servlet-name>
<url-pattern>/requestMethods</url-pattern>
</servlet-mapping>
- 完成测试
8.11.5 HttpServletRequest 注意事项和细节
- 获取 doPost 参数中文乱码解决方案:注意
setCharacterEncoding("utf-8")
要写在request.getParameter()
前
- 注意:如果通过 PrintWriter writer,有返回数据给浏览器,建议将获取参数代码写在
writer.print()
之前,否则可能获取不到参数值(doPost) - 处理 http 响应数据中文乱码问题
- 再次理解 Http 协议响应 Content-Type 的含义,比如 text/plain ,application/x-tar
8.11.6 课堂小练习
- 请通过 HttpServletRequest 对象获取到,是什么浏览器访问服务器(前面案例有)
- 请想办法获取到 JSESSIONID 的值
package com.xjs.servlet.request.homework;
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.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author: 谢家升
* @Date: 2022/2/28-02-28-7:20
* @Version: 1.0
*/
public class ComputerServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ComputerServlet 被调用~~");
//解读:
// (1) Windows NT 10.0; Win64; x64; rv:97.0
// (2) Windows NT 10.0 和 Win64
// (3) 回顾使用正则表达式
String userAgent = request.getHeader("User-Agent");
//java基础 正则表达式
String regStr = "\\((.*)\\)";
Pattern compile = Pattern.compile(regStr);
Matcher matcher = compile.matcher(userAgent);
matcher.find();// 因为我们的userAgent只有一组 ()
String group = matcher.group(0);// (Windows NT 10.0; Win64; x64; rv:97.0)
String group1 = matcher.group(1);// Windows NT 10.0; Win64; x64; rv:97.0
String[] operInfos = group1.split(";");
System.out.println("操作系统=" + operInfos[0]);//Windows NT 10.0
System.out.println("操作系统位数=" + operInfos[1].trim());// Win64
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
8.11.7 请求转发
8.11.7.1 为什么需要请求转发
- 目前我们学习的都是一次请求,对应一个 Servlet, 如图
- 但是在实际开发中,往往业务比较复杂,需要在一次请求中,使用到多个 Servlet 完成一个任务(Servlet 链, 流水作业) 如图:
8.11.7.2 请求转发说明
-
实现请求转发:请求转发指一个 web 资源收到客户端请求后,通知服务器去调用另外一个 web 资源进行处理
-
HttpServletRequest 对象(也叫 Request 对象)提供了一个
getRequestDispatcher()
方法,该方法返回一个 RequestDispatcher 对象,调用这个对象的forward()
方法可以实现请求转发 -
request 对象同时也是一个域对象,开发人员通过 request 对象在实现转发时,把数据通过 request 对象带给其它 web 资源处理
● setAttribute 方法
● getAttribute 方法
● removeAttribute 方法
● getAttributeNames 方法
8.11.7.3 实现请求转发
- 请求转发原理示意图
8.11.7.4 请求转发应用实例
- 需求说明/图解, 如果是 tom,提示为管理员,其它是普通用户
- 代码实现
- 开发一个登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/servlet/checkServlet" method="post">
user: <input type="text" name="username"/><br/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
- 创建 CheckServlet.java
package com.xjs.servlet.request;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/27-02-27-21:18
* @Version: 1.0
*/
public class CheckServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("CheckServlet 被调用~~");
//根据用户名来确定该用户是什么身份
String username = request.getParameter("username");
//注意:如果是同一个request对象(请求转发),那么可以在不同的servlet中,使用 getParameter()
if ("tom".equals(username)) {
//分配
request.setAttribute("role", "管理员");
} else {
request.setAttribute("role", "普通用户");
}
//获取分发器
//解读:
//(1) /manageServlet 写的是 要转发的 servlet 的 url
//(2) / 会被解析成 /servlet
//(3) forward(request,response) 表示把当前 servlet 的 request对象 和 response对象,传递给下一个 servlet使用
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/manageServlet");
//把当前 servlet 的 request对象 和 response对象,传递给下一个 servlet使用
requestDispatcher.forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 创建 ManageServlet.java
package com.xjs.servlet.request;
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;
/**
* @Author: 谢家升
* @Date: 2022/2/27-02-27-21:19
* @Version: 1.0
*/
public class ManageServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ManageServlet 被调用~~");
String username = request.getParameter("username");
String role = (String) request.getAttribute("role");
//输出信息
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("用户名:" + username + "<br/>");
writer.print("角 色:" + role);
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 完成测试:注意看浏览器的请求次数和数据通信情况
8.11.7.5 请求转发注意事项和细节
- 浏览器地址不会变化(地址会保留在第 1 个 servlet 的 url)
- 在同一次 HTTP 请求中,进行多次转发,仍然是一次 HTTP 请求
- 在同一次 HTTP 请求中,进行多次转发,多个 Servlet 可以共享 request 域/对象的数据(因为始终是同一个 request 对象)
- 可以转发到 WEB-INF 目录下 (后面做项目使用)
- 不能访问当前 WEB 工程外的资源
- 因为浏览器地址栏会停止在第一个 servlet ,如果你刷新页面,会再次发出请求(并且会带数据), 所以在支付页面情况下,不要使用请求转发,否则会造成重复支付
8.11.8 课后作业
- 下面是一个表单,前面我们使用过,完成如下功能
(1)编写一个 RegisterServlet,能够接收到提交的各种数据
(2)并把接收到的数据返回给浏览器,并显示(回显)
(3)注意处理中文乱码问题
(4)暂不处理文件提交
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表单综合案例</title>
</head>
<body>
<form action="http://localhost:8080/servlet/registerServlet" method="post">
用户注册信息<br/>
用户名称: <input type="text" name="username"><br/>
用户密码: <input type="password" name="pwd1"><br/>
确认密码: <input type="password" name="pwd2"><br/>
选择你喜欢的运动项目:
<input type="checkbox" name="sport" value="篮球">篮球<br/>
<input type="checkbox" name="sport" value="足球" checked>足球<br/>
<input type="checkbox" name="sport" value="手球" checked>手球<br/>
请选择性别 :
<input type="radio" name="gender" value="male">男<br/>
<input type="radio" name="gender" value="female">女<br/>
请选择城市:
<select name="city">
<option>--选择--</option>
<option value="cd">成都</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select><br/>
自我介绍:
<textarea name="info" rows="6" cols="20"></textarea><br/>
选择你的文件(头像)<input type="file" name="myfile"><br/>
<input type="submit" value="提交"/> <input type="reset" value="重置"/>
</form>
</body>
</html>
package com.xjs.servlet.request.homework;
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;
/**
* @Author: 谢家升
* @Date: 2022/2/27-02-27-22:28
* @Version: 1.0
*/
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接受用户提交的数据
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
String pwd1 = request.getParameter("pwd1");
String pwd2 = request.getParameter("pwd2");
String[] sports = request.getParameterValues("sport");
String gender = request.getParameter("gender");
String[] cities = request.getParameterValues("city");
String info = request.getParameter("info");
//解决乱码的方式
//解读:
//1. 设置服务器使用utf-8
//response.setCharacterEncoding("utf-8");
//2. 设置浏览器端是utf-8, 而且类型是 text/html
//response.setHeader("Content-Type", "text/html;charset=utf-8");
//回显数据
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("用户名称:" + username + "<br/>");
writer.print("用户密码:" + pwd1 + "<br/>");
writer.print("确认密码:" + pwd2 + "<br/>");
writer.print("请选择你喜欢的运动项目:<br/>");
for (String sport : sports) {
writer.print(sport);
}
writer.print("<br/>");
writer.print("请选择性别:");
writer.print(gender + "<br/>");
writer.print("请选择城市:<br/>");
for (String city : cities) {
writer.print(city);
}
writer.print("<br/>");
writer.print("自我介绍:<br/>");
writer.print(info);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 请编写一个 Servlet, 可以获取到浏览器所在电脑的操作系统版本和位数(32 还是 64),显示在页面即可
package com.xjs.servlet.request.homework;
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.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author: 谢家升
* @Date: 2022/2/28-02-28-7:20
* @Version: 1.0
*/
public class ComputerServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ComputerServlet 被调用~~");
//解读:
// (1) Windows NT 10.0; Win64; x64; rv:97.0
// (2) Windows NT 10.0 和 Win64
// (3) 回顾使用正则表达式
String userAgent = request.getHeader("User-Agent");
//java基础 正则表达式
String regStr = "\\((.*)\\)";
Pattern compile = Pattern.compile(regStr);
Matcher matcher = compile.matcher(userAgent);
matcher.find();// 因为我们的userAgent只有一组 ()
String group = matcher.group(0);// (Windows NT 10.0; Win64; x64; rv:97.0)
String group1 = matcher.group(1);// Windows NT 10.0; Win64; x64; rv:97.0
String[] operInfos = group1.split(";");
System.out.println("操作系统=" + operInfos[0]);//Windows NT 10.0
System.out.println("操作系统位数=" + operInfos[1].trim());// Win64
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
8.12 HttpServletResponse
8.12.1 HttpServletResponse 介绍
- 每次 HTTP 请求,Tomcat 会创建一个 HttpServletResponse 对象传递给 Servlet 程序去使用
- HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,通过 HttpServletResponse
8.12.2 HttpServletResponse 类图
8.12.3 向客户端返回数据方法
- 字节流
getOutputStream()
,常用于下载 (处理二进制数据) - 字符流
getWriter()
,常用于回传字符串 - 细节:两个流同时只能使用一个,使用了字节流,就不能再使用字符流,反之亦然,否则就会报错
8.12.4 向客户端返回数据应用实例
- 需求:浏览器请求 , 返回 hello, world
- 代码实现
package com.xjs.servlet.response;
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;
/**
* @Author: 谢家升
* @Date: 2022/3/1-03-01-9:53
* @Version: 1.0
*/
public class ResponseServlet_ extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ResponseServlet_ 被调用~~");
//注意:
//1. setContentType 会设置服务器和客户端都用 utf-8 字符集,还设置了响应头
//2. setContentType 要在获取流对象(getWriter)之前调用才有效
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>hello,world~~</h1>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 在 web.xml 中完成配置
<!--配置ResponseServlet_-->
<servlet>
<servlet-name>ResponseServlet_</servlet-name>
<servlet-class>com.xjs.servlet.response.ResponseServlet_</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseServlet_</servlet-name>
<url-pattern>/responseServlet_</url-pattern>
</servlet-mapping>
- 完成测试
8.12.5 向客户端返回数据注意事项和细节
- 处理中文乱码问题-方案 1
//解决乱码的方式1
//解读:
//1. 设置服务器使用utf-8
response.setCharacterEncoding("utf-8");
//2. 设置浏览器端是utf-8, 而且类型是 text/html
response.setHeader("Content-Type", "text/html;charset=utf-8");
- 处理中文乱码问题-方案 2
//处理中文乱码问题-方式2:
//1. setContentType 会设置服务器和客户端都用 utf-8 字符集,还设置了响应头
//2. setContentType 要在获取流对象(getWriter)之前调用才有效
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
8.12.6 请求重定向
8.12.6.1 请求重定向介绍
- 请求重定向指:一个 web 资源收到客户端请求后,通知客户端去访问另外一个 web资源,这称之为请求重定向
- 请求重定向原理示意图
8.12.6.2 请求重定向应用实例
- 需求 : 演示请求重定向的使用,当访问DownServlet 下载 文件 , 重定向到DownServletNew 下载文件
- 创建 DownServlet.java
package com.xjs.servlet.response;
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;
/**
* @Author: 谢家升
* @Date: 2022/2/28-02-28-9:15
* @Version: 1.0
*/
public class DownServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("DownServlet 被调用~~");
//response.setContentType("application/x-tar;charset=utf-8");
//PrintWriter writer = response.getWriter();
//writer.print("hi");
//writer.flush();
//writer.close();
//完成自己的业务
//发出请求重定向 ==> DownServletNew
//解读:
//(1) sendRedirect 本质就会 返回 302 状态码 以及 Location: /servlet/downServletNew
//(2) 因此302 和 /servlet/downServletNew 是浏览器解析,而不是服务器
//(3) 浏览器在解析 /servlet/downServletNew => http://localhost:8080/servlet/downServletNew
//(4) 动态获取 Application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath= " + contextPath);//contextPath= /servlet
response.sendRedirect(contextPath + "/downServletNew");
//response.sendRedirect("http://www.baidu.com");
//第二种重定向的写法
//System.out.println("第2种重定向方式~~~");
//response.setStatus(302);//设置http响应状态码
设置http响应的 Location: /servlet/downServletNew
//response.setHeader("Location","/servlet/downServletNew");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 创建 DownServletNew.java
package com.xjs.servlet.response;
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;
/**
* @Author: 谢家升
* @Date: 2022/2/28-02-28-9:17
* @Version: 1.0
*/
public class DownServletNew extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("DownServletNew 被调用~~");
System.out.println("DownServlet 被调用~~");
response.setContentType("application/x-tar;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("hi");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 在 web.xml 中完成 servlet 配置
<servlet>
<servlet-name>DownServlet</servlet-name>
<servlet-class>com.xjs.servlet.response.DownServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownServlet</servlet-name>
<url-pattern>/downServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>DownServletNew</servlet-name>
<servlet-class>com.xjs.servlet.response.DownServletNew</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownServletNew</servlet-name>
<url-pattern>/downServletNew</url-pattern>
</servlet-mapping>
- 完成测试
8.12.6.3 请求重定向注意事项和细节
- 最佳应用场景:网站迁移,比如原域名是 www.xjs.com 迁移到 www.xjs.cn ,但是百度抓取的还是原来网址
- 浏览器地址会发生变化,本质是两次 http 请求
- 不能共享 Request 域中的数据,本质是两次 http 请求,会生成两个 HttpServletRequest对象
- 不能重定向到 /WEB-INF 下的资源
- 可以重定向到 Web 工程以外的资源, 比如到 www.baidu.com
- 重定向有两种方式,推荐使用第 1 种
- 第1种重定向方式【推荐】
//完成自己的业务
//发出请求重定向 ==> DownServletNew
//解读:
//(1) sendRedirect 本质就会 返回 302 状态码 以及 Location: /servlet/downServletNew
//(2) 因此302 和 /servlet/downServletNew 是浏览器解析,而不是服务器
//(3) 浏览器在解析 /servlet/downServletNew => http://localhost:8080/servlet/downServletNew
//(4) 动态获取 Application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath= " + contextPath);//contextPath= /servlet
response.sendRedirect(contextPath + "/downServletNew");
//response.sendRedirect("http://www.baidu.com");
- 第2种重定向方式
//第二种重定向的写法
//System.out.println("第2种重定向方式~~~");
response.setStatus(302);//设置http响应状态码
//设置http响应的 Location: /servlet/downServletNew
response.setHeader("Location","/servlet/downServletNew");
- 动态获取到 application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath= " + contextPath);// /servlet
response.sendRedirect(contextPath + "/downServletNew");
8.12.7 课后作业
- 具体需求
- 编写一个 MyPayServlet , 能够接收到提交的数据
- 编写一个简单的支付页面 pay.html(如图)
- 如果支付金额大于 100,则重定向到 payok.html,否则重定向到原来的 pay.html
【pay.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付页面</title>
</head>
<body>
<h1>支付页面</h1>
<!--注意:这里action="/servlet/myPayServlet"的第一/被浏览器解析从浏览器地址栏的主机-->
<form action="/servlet/myPayServlet" method="post">
用户编号: <input type="text" name="userid"><br/>
支付金额: <input type="text" name="money"><br/><br/>
<input type="submit" value="点击支付">
</form>
</body>
</html>
【payOk.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付成功</title>
</head>
<body>
<h1>恭喜你,支付成功~</h1>
</body>
</html>
package com.xjs.servlet.response.homework;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 谢家升
* @Date: 2022/2/28-02-28-18:25
* @Version: 1.0
*/
public class MyPayServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("myPayServlet 被调用~~~");
//获取表单提交的数据
String userid = request.getParameter("userid");
String money = request.getParameter("money");
String contextPath = getServletContext().getContextPath();
//int moneyCount = Integer.parseInt(money);
//处理一下这个异常
int i = WebUtils.parseStr(money);
if (i > 100) {
//重定向到 payOk.html
response.sendRedirect(contextPath + "/payOk.html");
} else {
//重定向到 pay.html
response.sendRedirect(contextPath + "/pay.html");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
【WebUtils.java】
package com.xjs.servlet.response.homework;
/**
* @Author: 谢家升
* @Date: 2022/2/28-02-28-20:18
* @Version: 1.0
*/
public class WebUtils {
public static int parseStr(String str) {
int num = 0;
try {
num = Integer.parseInt(str);
} catch (NumberFormatException e) {
//这个异常不会throw 给 tomcat
System.out.println("你输入的str格式不正确...");
}
return num;
}
}