- 1、介绍
- 1.1、什么是 Servlet
- 1.2、自定义类直接实现 Servlet 程序
- 1.2.1、url 地址到 Servlet 程序的访问过程
- 1.2.2、Servlet 的生命周期
- 1.2.3、GET 和 POST 请求的分发处理
- 1.3、自定义类继承已有的实现子类 HttpServlet
- 1.7、使用 IDEA 自定义类继承 HttpServlet
- 1.8、Servlet 类的继承体系
- 2、ServletConfig、ServletContext
- 2.1、ServletConfig 类
- ServletConfig 类的三大作用:
- 注意点:
- 2.2、ServletContext 类
- ServletContext 类的四个作用
- 3、HTTP 协议
- 3.1、请求的 HTTP 协议格式
- 3.1.1、GET 请求
- 3.1.2、POST 请求
- 3.1.3、常用请求头的说明
- 3.1.4、常用请求
- 3.2、响应的 HTTP 协议格式
- 3.2.1、常用的响应码说明
- 3.2.2、MIME 类型说明
- 谷歌浏览器如何查看 HTTP 协议
- 4、HttpServletRequest 类
- 4.1、常用方法
- 4.2、如何获取请求参数
- 4.3、请求的转发getRequestDispatcher()
- 4.4、base 标签的作用
- 4.5、Web 中的相对路径和绝对路径
- 4.6、web 中 / 斜杠的不同意义
- 5、HttpServletResponse 类
- 5.1、两个输出流的说明
- 5.2、如何往客户端回传数据
- 5.3、响应的乱码解决
- 5.4、请求重定向sendRedirect()
导入源码
Tomcat 配置
- Servlet 是 JavaEE 规范之一。规范就是接口
- Servlet 就 JavaWeb 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器
- Servlet 是运行在服务器上的一个 java 小程序,它 可以接收客户端发送过来的请求,并响应数据给客户端
- 编写一个类去实现 Servlet 接口
- 实现 service 方法,处理请求,并响应数据
- 到 web.xml 中去配置 servlet 程序的访问地址
编写 Hello 类去实现 Servlet 接口
实现 service 方法
service 方法是专门用来处理请求和响应的
(没啥特别的,就是实现接口,并重写方法)
public class Hello implements Servlet { public Hello() { System.out.println("1、构造器"); } @Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println("2、执行 init 初始化方法"); } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("3、service方法 ========>>> hello servlet"); } @Override public String getServletInfo() { return null; } @Override public void destroy() { System.out.println("4、执行 destroy 销毁方法"); } }
web.xml 中去配置 servlet 程序的访问地址
Hello com.atguigu.servlet.Hello Hello /aba
注意:
- 首先,在 web.xml 中有两个大标签
- servlet 标签给 Tomcat 配置 Servlet 程序
- servlet-mapping 标签给 servlet 程序配置访问地址
- 在 servlet 标签中
- servlet-name 标签是给 Servlet 程序起一个别名(一般是类名)
- servlet-class 是 Servlet 程序的全类名,即自定义的实现 Servlet 接口的类的全名
- 在 servlet-mapping 标签中
- servlet-name 标签的作用是告诉服务器,我当前配置的地址给哪个 Servlet 程序使用,所以这里填写的内容要和 servlet 标签中的 servlet-name 标签内容保持一致
- url-pattern 标签配置访问地址,记得要有斜杠 /
/ 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径
/hello 表示地址为:http://ip:port/工程路径/hello
这里的 /hello 在硬盘中没有实际位置,只是客户端通过该标签就可以访问到自定义类对应的 Servlet 程序
然后运行
先点击该工程的 Tomcat ,再点击三角形的运行按钮
在d出来的浏览器页面,在链接地址后面 加上 刚刚设置的访问地址
运行的结果会输出在 IDEA 控制台
经常会碰到几个问题:
-
url-pattern 中配置的路径没有以斜杠开头
-
servlet-name 配置的值不存在
就是指在 servlet-mapping 标签中给的程序名一定要与 上一个标签中给 程序起的别名保持一致
-
servlet-class 标签的全类名配置错误
此种情况浏览器会正常d出,但是会报错
客户端拿到 ip:port 定位到哪个电脑的哪个线程,通过工程路径得到工程
根据 web.xml 里面的 servlet-mapping 标签得到访问地址和地址配置给哪个 servlet 程序
根据程序名在 servlet 标签中找到全类名,找到类,再得到类里面重写的 service 方法
1.2.2、Servlet 的生命周期1、执行 Servlet 构造器方法
2、执行 init 初始化方法
3、执行 service 方法
4、执行 destroy 销毁方法
第一、二步,是在第一次访问,的时候创建 Servlet 程序会调用
第三步,每次访问都会调用
第四步,在 web 工程停止的时候调用
第一次执行得到如下:
刷新浏览器得到如下,可见非第一次访问仅执行 service 方法
点击 IDEA 的停止执行
在学习 HTML 的时候,知道提交表单的时候,有两种请求方式 GET 、POST
但是在自定义实现 Servlet 接口的类中,只有一个 service 方法,有时候需要不同的请求方式去做不同的事情,此时就需要进行请求的分发处理
通过 F4 ,可以发现 ServletRequest 是接口,为了调用其子类中的getMethod()方法,强转为 HttpServletRequest 类型
service() 方法体如下
@Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("3、service方法 ========>>> hello servlet"); HttpServletRequest ser = (HttpServletRequest) servletRequest; //获取请求的类型 String method = ser.getMethod(); //System.out.println(method); //输出就是GET if("GET".equals(method)){ doget(); }else if("POST".equals(method)){ dopost(); }; } //将不同请求方式做的不同事情全部封装进方法内 public void doget(){System.out.println("GET方式");} public void dopost(){System.out.println("POST方式");}
下面介绍的是怎么实现之前学习的表单提交,提交到这里给定的工程
同时验证输出语句 System.out.println(method);
现在写一个页面,来验证getMethod()方法能够返回请求的方式
1、创建文件
注意是在 web 的文件夹下 写页面
2、写一个表单,提交按钮
注意这里 提交的地址需要是在最开始 web.xml 中设定的 url-pattern 的地址
3、运行
注意运行的时候只需要在跳转的 链接后面加上文件名
4、执行结果:
页面提交一次,service 方法就会执行一次
通过继承已经实现 Servlet 的 HttpServlet 类的方式去实现 Servlet 程序
一般在实际项目开发中,都是使用继承 HttpServlet 类的方式去实现 Servlet 程序
1、编写一个类去继承 HttpServlet 类
2、根据业务需要重写 doGet 或 doPost 方法
3、到 web.xml 中的 加 一个配置 Servlet 程序和访问地址的 *** 作
编写一个类,并重写方法
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("继承方式的 doGet 方法"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("继承方式的 doPost 方法"); } }
加 一个配置 Servlet 程序和访问地址的 *** 作
HelloServlet com.atguigu.servlet.HelloServlet HelloServlet /hello
执行
使用 exp.html 文件
执行结果:
页面提交一次,service 方法就会执行一次
在包名上右击
IDEA 会创建好类
继承了 HttpServlet
提供了doPost和doGet的重写
提供了 web.xml 文件中的
需要手动补上
ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类
Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用
Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象
Tomcat 有几个提供可以使用 ServletConfig 对象的位置:
1、将对象传给了init(ServletConfig servletConfig)方法,我们可以通过重写此方法,实现调用
2、在doPost和doGet的重写方法中,可以使用方法getServletConfig(),返回的是一个ServletConfig 类的对象
(都是通过实例化对象调用)
- 可以获取 Servlet 程序的别名 servlet-name 的值
servletConfig.getServletName() - 获取初始化参数 init-param
servletConfig.getInitParameter("参数名") - 获取 ServletContext 对象
servletConfig.getServletContext()
初始化参数 init-param
要先配置,才有该参数
在 web.xml 文件中的
是呈现 键值对式的,一个参数名对应一个参数值
可以有多个 init-param
Hello com.atguigu.servlet.Hello username root
@Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println("2、执行 init 初始化方法"); System.out.println(servletConfig.getServletName()); System.out.println(servletConfig.getInitParameter("username")); System.out.println(servletConfig.getServletContext()); }注意点:
- servletconfig仅作用于单个servlet程序
在创建servlet程序的时候,会创建一个对应的 servletconfig ,有一个一一对应的关系,包括servletconfig对应servlet程序在web.xml中配置的相关信息。所以在这个自定义类下的servletconfig对象无法访问另一个自定义类下的init-param,因为servlet程序程序不同 - 因为父类的 init 方法中有一句 this.config = config;,该句给父类的 config 属性进行了赋值,否则拿到的 config 就是 null值的。
在子类重写了同名方法时,如果没有使用 super.init(config),那么子类重写的方法就会覆盖父类方法,父类方法得不到执行,那么 config 就是null 值的
那这样在使用方法getServletConfig()的时候,就会返回一个 null 值
public class HelloServlrt extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { System.out.println("重写的方法init"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("继承方式的 doGet 方法"); ServletConfig servletConfig = getServletConfig(); System.out.println(servletConfig); //null } }2.2、ServletContext 类
什么是 ServletContext?
- ServletContext 是一个接口,它表示 Servlet 上下文对象
- 一个 web 工程,只有一个 ServletContext 对象实例
- ServletContext 对象是一个域对象
- ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。
什么是域对象?
域对象,是可以像 Map 一样存取数据的对象,叫域对象
这里的域指的是存取数据的 *** 作范围,整个 web 工程
- 获取 web.xml 中配置的上下文参数 context-param
servletContext.getInitParameter("参数名")
context-param 参数也是类似于参数 init-param是一个键值对的对应关系 但是不同于 参数 init-param,context-param 参数是声明在 web.xml 文件中的
标签外面,供一整个 web工程进行共享使用 所以同一个工程下的多个自定义类都可以去访问context-param 参数
- 获取当前的工程路径,格式: /工程路径
context.getContextPath()
其实就是 Tomcat 配置页面的 Application context
- 获取工程部署后在服务器硬盘上的绝对路径
context.getRealPath("/")
这里的参数 “ 斜杠 ”
获取到的路径实际上就是 IDEA 代码中,该 web 工程下的 web 文件夹的路径
- 像 Map 一样存取数据
此处解释 idea 中获取的工程路径一直都是 /工程路径:
在 IDEA 中,实际上是复制了tomcat部分文件夹,然后使用第二种 web 工程部署方式,即创建 工程路径.xml 配置文件(此处默认已将工程路径名称更改为工程名一致,所以同样是 工程名.xml), 配置文件中指明了工程在硬盘中的实际位置
访问的时候直接访问配置文件的位置即可http://localhost:8080/工程路径
1、获取 web.xml 中配置的上下文参数 context-param
该参数是定义在标签外面
HelloServlrt com.atguigu.servlet.HelloServlrt HelloServlrt /hello age 18 username 123
在同一个 web 工程下的不同类(即在不同 servlet 程序中),都可以访问上下文参数 context-param
public class HelloServlrt extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("继承方式的 doGet 方法"); ServletContext servletContext = getServletConfig().getServletContext(); System.out.println(servletContext.getInitParameter("age")); //18 } } public class HelloServlet3 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("另一个自定义类中"); ServletContext servletContext = getServletConfig().getServletContext(); System.out.println(servletContext.getInitParameter("age")); } }
2、获取当前的工程路径
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = getServletConfig().getServletContext(); System.out.println(servletContext.getContextPath()); ///servlet }
3、获取工程部署后在服务器硬盘上的绝对路径
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = getServletConfig().getServletContext(); //获取工程部署后在服务器硬盘上的绝对路径 // "/"斜杠被服务器解析地址为:http://ip:port/工程名/ ,映射到 IDEA 代码的 web 目录 System.out.println(servletContext.getRealPath("/")); //F:workspaceJavaWebJavaWeboutartifactsservlet_war_exploded }
获取当前的工程路径/servlet
获取工程部署后在服务器硬盘上的绝对路径
F:workspaceJavaWebJavaWeboutartifactsservlet_war_exploded
在 IDEA 运行 web 工程的时候,会加载一个地址
Using CATALINA_base: "C:UsersASUS.IntelliJIdea2019.2systemtomcatTomcat_8_0_50_JavaWeb_3"
这里是IDEA整合Tomcat的时候复制了的部分文件,在在里面部署 web工程的方法就是在 conf 目录 Catalinalocalhost 下创建配置文件,文件里面指向的真实工程地址就是上面的F盘文件
而配置文件的名称就是上面获取当前的工程路径的名称servlet
这里的绝对路径F:...对应的就是工程下面的 web 文件夹
所以如果要访问web文件夹里面其他内容的路径,只需要在 斜杠 后面加上文件名即可
("工程下 imgs 目录 1.jpg 的绝对路径是:" + context.getRealPath("/imgs/1.jpg")
4、ServletContext 像 Map 一样存取数据
在父类中封装了方法getServletContext()来获取 ServletContext 对象
一个 web 工程,只有一个 ServletContext 对象实例
在不同的 servlet 程序里面,打印 ServletContext 对象,得到的是一致的地址值,说明 ServletContext 对象是整个 web 工程共有的
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = getServletContext(); System.out.println("一个servlet程序里的"+context); }
public class HelloServlet3 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("另一个自定义类中"); ServletContext context = getServletContext(); System.out.println("另一个servlet程序里的"+context); } }
因为不同的 servlet 程序共有一个 ServletContext 对象,所以里面存放的数据是不同的 servlet 程序共有的
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = getServletContext(); //设置数据之前 System.out.println("设置数据之前"+context.getAttribute("key1")); //设置数据 context.setAttribute("key1","value1"); //设置数据之后访问 System.out.println("设置数据之后"+context.getAttribute("key1")); }
public class HelloServlet3 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("另一个自定义类中"); ServletContext context = getServletContext(); //不同的servlet程序中访问数据 System.out.println("另一个自定义类中"+context.getAttribute("key1")); } }
而 ServletContext 是在 web 工程部署启动的时候创建,在 web 工程停止的时候销毁(web工程的重新部署、重启就是销毁)
所以里面存放的数据也会随着重新部署而销毁,如果没有重新部署,就会一直存在
什么是 HTTP 协议?
协议是指双方,或多方,相互约定好,大家都需要遵守的规则,叫协议
HTTP 协议,就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫 HTTP 协议
HTTP 协议中的数据又叫报文
3.1、请求的 HTTP 协议格式客户端给服务器发送数据叫请求
服务器给客户端回传数据叫响应
请求又分为 GET 请求,和 POST 请求两种
3.1.1、GET 请求- 请求行
请求的方式 GET
请求的资源路径 [+?+请求参数]
请求的协议的版本号 HTTP/1.1 - 请求头
key : value 键值对组成
不同的键值对,表示不同的含义
- 请求行
请求的方式 POST
请求的资源路径 [+?+请求参数] 中括号为可选内容
请求的协议的版本号 `HTTP/1.1 - 请求头
key : value `
不同的请求头,表示不同的含义 - 空行
- 请求体 ---->>> 就是发送给服务器的数据
Accept: 表示客户端可以接收的数据类型
Accpet-Languege: 表示客户端可以接收的语言类型
User-Agent: 表示客户端浏览器的信息
Host: 表示请求时的服务器 ip 和端口号
大部分情况下都是 get 请求的方式
GET 请求有哪些:
- form 标签 method=get
- a 标签
- link 标签引入 css
- script 标签引入 js 文件
- img 标签引入图片
- iframe 引入 html 页面
- 在浏览器地址栏中输入地址后敲回车
POST 请求有哪些:
8. form 标签 method=post
- 响应行
- 响应的协议和版本号
- 响应状态码
- 响应状态描述符
- 响应头
key : value
不同的响应头,有其不同含义 - 空行
- 响应体 ---->>> 就是回传给客户端的数据
200 表示请求成功
302 表示请求重定向
404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)
500 表示服务器已经收到请求,但是服务器内部错误(代码错误int i = 12/0)
MIME 是 HTTP 协议中的数据类型
MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务
MIME 类型的格式是“大类型/小 类型”,并与某一种文件的扩展名相对应。
常见的 MIME 类型:
火狐浏览器如何查看 HTTP 协议:
每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。 然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的 信息
HttpServletRequest 对象是 Tomcat 创建的,每次请求就会创建一个,请求过后,就会销毁
4.1、常用方法@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //getRequestURI() 获取请求的资源路径 System.out.println("URI ==> "+req.getRequestURI()); //URI ==> /servlet/hello //getRequestURL() 获取请求的统一资源定位符(绝对路径) System.out.println("URL ==> "+req.getRequestURL()); //URL ==> http://localhost:8080/servlet/hello //getRemoteHost() 获取客户端的 ip 地址 System.out.println("客户端的 ip 地址 ==> "+req.getRemoteHost()); //客户端的 ip 地址 ==> 127.0.0.1 //getHeader() 获取请求头 System.out.println("请求头User-Agent ==> "+req.getHeader("User-Agent")); //getMethod() 获取请求的方式 GET 或 POST System.out.println("请求的方式 ==> "+req.getMethod()); //请求的方式 ==> GET }
getRemoteHost() 获取客户端的 ip 地址
在 IDEA 中,使用 localhost 访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1
在 IDEA 中,使用 127.0.0.1 访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1
在 IDEA 中,使用 真实 ip 访问时,得到的客户端 ip 地址是 ===>>> 真实的客户端 ip 地址
当将Tomcat上网址交给同局域网内的其他电脑访问时,这里可以得到已访问电脑的 ip 地址
4.2、如何获取请求参数@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //getParameter() 获取请求的参数,参数只有一个值 String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println("用户名:" + username); System.out.println("密码:" + password); //getParameterValues() 获取请求的参数(多个值的时候使用) String[] hobbies = req.getParameterValues("hobby"); System.out.println("兴趣:" + Arrays.asList(hobbies)); }
在浏览器页面输入参数信息
在 IDEA 输出请求参数
Get 请求的中文乱码解决
一般 get 请求没有中文乱码
// 获取请求参数 String username = req.getParameter("username"); //1 先以 iso8859-1 进行编码 //2 再以 utf-8 进行解码 username = new String(username.getBytes("iso-8859-1"), "UTF-8");
在 Post 请求里面容易出现中文乱码的问题
POST 请求的中文乱码解决
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置请求体的字符集为 UTF-8,从而解决 post 请求的中文乱码问题 // 要在获取请求参数之前才有效,只要有一个请求参数的语句在前面,此句就无效 req.setCharacterEncoding("UTF-8"); System.out.println("---------doPost方法-----------"); String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println("用户名:" + username); System.out.println("密码:" + password); String[] hobbies = req.getParameterValues("hobby"); System.out.println("兴趣:" + Arrays.asList(hobbies)); }
注意:
设置请求体的字符集要在所有的获取请求参数语句之前才有效
请求转发是指,服务器收到请求后,从一次资源跳转到另一个资源的 *** 作叫请求转发
Servlet1 代码
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取请求的参数(办事的材料)查看 String username = req.getParameter("username"); System.out.println("在 Servlet1(柜台 1)中查看参数(材料):" + username); // 给材料 盖一个章,并传递到 Servlet2(柜台 2)去查看 //setAttribute()这里的数据时web工程下所有servlet程序共有的 req.setAttribute("key1","柜台 1 的章"); // 问路:Servlet2(柜台 2)怎么走 //请求转发必须要以斜杠开头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到 IDEA 代码的 web 目录 //这里填写的是Servlet2的RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2"); // 带着参数数据走向 Sevlet2(柜台 2) requestDispatcher.forward(req,resp); }
Servlet2 代码
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取请求的参数(办事的材料)查看 String username = req.getParameter("username"); System.out.println("在 Servlet2(柜台 2)中查看参数(材料):" + username); // 查看 柜台 1 是否有盖章 Object key1 = req.getAttribute("key1"); System.out.println("查看章:" + key1); // 处理自己的业务 System.out.println("Servlet2 处理自己的业务 "); }
执行
注意:
-
虽然是从一个程序转发到第二个程序,但是浏览器的网址没有发生变化
-
请求转发是在一次请求中的
-
因为在一次请求中只会产生一个请求对象,在一个请求对象里面存储的数据是多个 servlet 程序都可以访问到的
setAttribute(key, value);设置域数据
getAttribute(key);获取域数据 -
WEB-INF 文件夹是受服务器保护的,如果将定义的 html 文件放到该目录下,是无法访问的
-
但是请求转发可以转到 WEB-INF 文件夹内
req.getRequestDispatcher("/WEB-INF/abc.html")
-
请求转发无法转到外网,因为访问的地址是该工程下
a文件
a 这里是first下second下的a.html
跳转到首页index.jsp