Servlet与Request
一、概述
- Servlet
- 是sun公司提供的一门用于开发动态web资源的技术
- 按照这套规范写出来的servlet可以放置在web应用中在servlet容器中运行 。
开发servlet步骤
- 写一个类实现servlet接口 , 并实现其中的方法
- 在web.xml中为servlet配置对外访问路径
案例:
手写一个servlet: 1.在指定位置(如D盘)创建一个FirstServlet.java文件, 创建FirstServlet类, 继承GenericServlet类 public class FirstServlet extends GenericServlet{} 2.添加未实现的service方法, 实现内部代码 public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{ String dateStr = new Date().toLocaleString(); res.getWriter().write(dateStr); } 3.添加包结构, 并导入依赖包 package cn.servlet; import java.util.*; import java.io.*; import javax.servlet.*; 4.编译Servlet类 在编译之前, 找到 [tomcat7.0]\lib\servlet-api.jar 包, 在cmd窗口中将其所在路径设置给classpath 如: set classpath=E:\software\tomcat7.0\lib\servlet-api.jar 带包编译: 进入到FirstServlet.java所在路径 命令: javac -d . FirstServlet.java 其中: -d是带包编译 点(.)表示编译后的class存放在当前目录下 5.将编译的class所在的整个目录放在WEB应用下的/WEB-INF/classes目录下 6.在web.xml中配置Servlet对外访问的虚拟路径 <servlet> <servlet-name>FirstServlet</servlet-name> <servlet-class>cn.servlet.FirstServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>FirstServlet</servlet-name> <url-pattern>/FirstServlet</url-pattern> </servlet-mapping> 7.将WEB应用配置到tomcat服务器中, 并启动服务器访问如下路径来访问FirstServlet http://localhost/news/servlet/FirstServlet
二、使用myeclipse开发servlet
- 使用myeclipse开发servlet程序时 , 可以新建一个servlet , 默认继承HttpServlet , 在Servlet内部会覆盖doGet()和 doPost()方法 , 分别来处理GET请求和POST请求 。
- 新建项目
- 新建一个web project, 给设置一个项目名称 ,一般其他保持默认即可 , 然后finish , 弹出的对话框选择yes即可。
- 新建一个Servlet
- 新建一个servlet , 如果new中找不到servlet , 注意视图是否换到MyEclipse中 , 给一个包名和类名 , 下面对勾只留doGet()和doPost(),其他去掉 , 下一步
- 这里将最后两行删掉,是一些xml中的提示,其他地方一般保持默认即可,也可以修改对外访问路径,其中name和URL会自动配置到xml中。最后finash。
- 一些疑问
- 为什么要继承HttpServlet?
- 因为这是一个继承了GenericServlet的类,已经提供doGet()和doPost()方法,可以方便我们开发web项目。
- 而GenericServlet是一个基础的实现,如果要用此类,需要自己写doGet()和doPost()方法,并且需要在service方法中对请求进行判断,会比较麻烦。
- HttpServlet底层又是如何实现的?
- 可以打开HttpServlet源码,发现HttpServlet也是继承自GenericServlet,同时,HttpServlet会写各种方法,比如doGet()和doPost(),然后在service方法中进行判断,不同的请求调用不同的方法。
- 为什么要继承HttpServlet?
- 注意:
- 复制一个servlet之后 , web.xml文件中不会自动生成配置文件 ,, 所以一般不要复制 , 直接新建 。
- 在myeclipse配置tomcat ,并将项目发布到tomcat中
三、Servlet继承结构
四、修改servlet模板
- 方式一:
- 下载http://dl.download.csdn.net/down11/20171203/44e21f5d54727c09ffa2214f7427287b.jar?response-content-disposition=attachment%3Bfilename%3D%22com.genuitec.eclipse.wizards_9.0.0.me201108091322.jar%22&OSSAccessKeyId=9q6nvzoJGowBj4q1&Expires=1512269518&Signature=J1sMRCVX4mp3WUiO%2FO2%2BloDU52U%3D&user=chou_out_man&sourceid=10142677
- 拷贝到【MyEclipse安装目录】\Common\pligins目录下 , 会提示是否替换文件 , 确定提换即可 , 如果没有提示 , 则有可能进错目录了
- 方式二:
- 在【MyEclipse安装目录】\Common\pligins目录下找到文件:com.(1)genuitec.eclipse.wizards.xxx.jar,在此文件中的Templates目录下可以看到Servlet.java源代码。
- 打开源代码 , 将doGet()和doPost()两个方法的注释和方法中的内容删掉即可
- 修改之后保存
- 重启MyEclipse后新建servlet即可看到效果 。
五、Servlet调用过程
- 调用过程
- Servlet生命周期
- Servlet实例在第一次被访问时创建, 创建之后服务器会立即调用init方法进行初始化的操作, 从此以后该实例会一直驻留在服务器的内存中, 为后续的请求服务, 只要有请求访问servlet, 服务器就会调用service方法来处理这个请求, 直到服务器关闭或者是web应用被移出容器时为止, 随着web应用的销毁, servlet实例也会跟着销毁, 在销毁之前, 服务器就调用destroy方法进行善后的处理.
六、servlet虚拟路径
在web.xml文件中可以配置对外访问的虚拟路径 , 可以直接写一个路径或者通过通配符*号写一个路径
1. 方式一:直接写一个路径
/servlet/dotestservlet
2. 方式二:通过通配符*号写一个路径
1. 以 / 开头, 以 /* 结尾
如: /servlet/* /a/* /*
2. 以 *.后缀 的形式,
如: *.html *.servlet *.do *.action
3. 使用*号匹配符写路径, 路径的配置变得更加灵活, 但是也可能会造成, 一个url会被多个servlet Mapping所匹配 , 如:
Url:http://localhost/day09/servlet/SecondServlet.do
Servlet1:Test1: /servlet/*
Servlet2:Test2: *.do
4. 匹配规则:
1. *.后缀的优先级永远最低
2. 哪一个最接近哪一个起作用
3. 示例:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应
Servlet引擎将调用Servlet1。
当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应
Servlet引擎将调用Servlet3。
当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet1。
当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet2。
七、Request
- 代表HTTP请求的对象
- 继承结构
request功能
获取客户端相关信息
getRequestURL方法 – 返回客户端发出请求完整URL
如: http://localhost/day09/servlet/SecondServlet
getRequestURI方法 – 返回请求行中的资源名部分
如: /day09/servlet/SecondServlet
getQueryString方法 – 返回请求行中的参数部分
如: username=zhangfei&password=123
getRemoteAddr方法 – 返回发出请求的客户机的IP地址
如: 127.0.0.1 //可能会出现0:0:0:0:0:0:0:1形式,是ipv6的表现形式。
getMethod – 得到客户机请求方式
如: GET或POST
getContextPath – 获得当前web应用虚拟目录名称
如: /day09
- 注意:在写路径时不要将web应用的虚拟路径的名称写死, 应该在需要写web应用的名称的地方通过getContextPath方法动态获取
获取请求头信息
- getHeader(name)方法 –String
getHeaders(String name)方法 — Enumeration
可以通过遍历枚举遍历每一个信息例如:while (values.hasMoreElements()) { String value = (String) values.nextElement(); System.out.println(value); }
- getHeaderNames方法 — Enumeration
- getIntHeader(name)方法 — int
- getDateHeader(name)方法 — long(日期对应毫秒)
获取请求参数
- getParameter(String name) — String 通过name获得值
- getParameterValues(String name) — String[ ] 通过name获得多值 checkbox
- getParameterMap() — Map key :name value: 多值
- getParameterNames() — Enumeration 获得所有name
请求参数时的乱码问题
- 乱码分析: 编码和解码时使用的 码表不一致造成的 。
- 编码是在浏览器中进行的 , 浏览器打开页面时使用的是什么码表 ,在发送数据时使用的也是相同的码表 。
- 解码在服务器端进行 , 如果不指定解码时使用的码表 , 则默认使用ISO-8859-1
- 解决方案: 在服务器解码前 , 通知服务器解码时应该使用的码表。使浏览器编码和服务器解码时的码表一致 。
Post方式提交参数的解决方案:
request.setCharacterEnCoding("utf-8");//用来通知服务器使用什么编码来接受请求实体内容中的数据, 如果使用的是POST提交, POST提交的请求参数就是在请求实体内容中!, 所有这个方法可以解决POST提交的乱码问题!!!
Get方式提交参数的解决方案
GET提交的请求参数由于不在请求实体内容中,而是在请求行中的请求资源路径后面拼接着, 所以这行代码对GET提交的参数乱码不起作用!!! GET提交的乱码问题可以通过手动编解码来解决!! //>>username为乱码, 通过乱码反向编码得回二进制数组 byte[] bytes = username.getBytes("iso8859-1"); //>>通过二进制数组查询正确的码表, 得出正确的数据 username = new String(bytes, "utf-8");
实现请求转发
- 请求重定向: 302状态码+Location响应头
- 请求转发: 与请求重定向都可以实现资源的跳转 , 但是区别是 :请求转发是服务器内部的并且是同一web应用内部的跳转 , 而重定向的资源跳转没有限制 。
请求转发的特点:
- 一次请求一次响应 。
- 地址栏不会发生变化
- 转发被限制在同一个web应用中
实现代码
创建servlet:RequestDemo3和RequestDemo4,RequestDemo3转发到RequestDemo4 在RequestDemo3中: /* * 在web阶段写路径时,除了请求转发和请求包含在写 * 路径是不用包含web应用的虚拟路径,其他地方都需要 * 加上web应用的虚拟路径。 * 完整路径:http://localhost/day09/servlet/RequestDemo4 */ request.getRequestDispatcher("/servlet/RequestDemo4").forward(request, response); 在RequestDemo4中响应: response.getWriter().write("1$");
request开发细节:
在转发之前, 如果response缓冲区被写入了数据但是还没有打给浏览器, 在转发时response缓冲区(数据)将会被清空! 例如:在RequestDemo3中添加代码: response.getWriter().write("no money!"); 发现浏览器中并未得到"no money!"这段字符串的响应。 在转发之前, 如果response缓冲区被写入了数据并且已经打给了浏览器, 转发将会失败!! 例如:在RequestDemo3中添加代码: response.getWriter().write("no money!"); response.flushBuffer(); 发现浏览器可以显示"no money!",因为强制刷新了,但是转发就会报错,因为已经响应过了,一次请求对应一次响应。 在同一个Servlet中转发不能进行多次!!(A既转发B, 又转发给C) 但是可以进行多重转发(比如A转发给B, B再转发给C)
- 作为域对象来使用
- 域对象: 一个对象具有一个可被看见的范围 , 并且利用该对象上的map可以在整个范围内实现资源的共享
- 域对象提供的方法(可以操作map中的数据)
- setAttribute(String name,Object value) :用来存储一个对象,也可以称之为存储一个域属性
- getAttribute(String name); 用来获取request中的数据
- removeAttribute(String name); 用来移除request中的域属性
- getAttributeNames(); 获取所有域属性的名称
- 域对象生命周期
- 请求开始时创建 , 请求结束时死亡
- 作用范围: 一次请求链
- 主要功能:
- 在作用范围内共享数据
实现请求包含
- 请求包含是服务器中内部资源合并的现象
如果浏览器请求ServletA , 但是A不能独立完成处理这个请求 , 需要另外一个ServletB帮忙 , 于是在A中可以将B包含进来 ,包含进来之后将由A和B共同处理这次请求 , 处理的结果也将合并在一起 , 一同发送给浏览器 。 如:
request.getRequestDispatcher(“B的路径”).include(request, response);