Servlet
Servlet?
从广义上来讲, Servlet 规范是 Sun 公司制定的一套技术标准, 包含与 web 应用相关的一系列接口, 是 Web 应用实现方式的宏观解决方案. 而具体的 Servlet 容器负责提供标准的实现.
从狭义上来讲, Servlet 指的是 javax.servlet.Servlet 接口及其子接口, 也可以指实现了 Servlet 接口的实现类.
Servlet(Server Applet)作为服务器端的一个组件, 它的本意是 "服务器端的小程序".
Servlet 的实例对象由 Servlet 容器负责创建;
Servlet 的方法由容器在特定情况下调用;
Servlet 容器会在 Web 应用卸载时销毁 Servlet 对象的实例.
操作步骤
使用 Servlet 接口的方式:
1 搭建 Web 开发环境
2 创建动态 Web 工程
3 创建 javax.servlet.Servlet 接口的实现类: com.atguigu.servlet.MyFirstServlet
4 在 service(ServletRequest, ServletResponse)方法中编写代码
5 在 Web.xml 配置文件中注册 MyFirstServlet
- <!-- 声明一个 Servlet, 配置的是 Servlet 的类信息 -->
- <servlet>
- <!-- 这是 Servlet 的别名, 一个名字对应一个 Servlet. 相当于变量名 -->
- <servlet-name>MyFirstServlet</servlet-name>
- <!-- Servlet 的全类名, 服务器会根据全类名找到这个 Servlet -->
- <servlet-class>com.servlet.MyFirstServlet</servlet-class>
- </servlet>
- <!-- 建立 Servlet 的请求映射信息 -->
- <servlet-mapping>
- <!-- Servlet 的别名, 说明这个 Servlet 将会响应下面 url-pattern 的请求 -->
- <servlet-name>MyFirstServlet</servlet-name>
- <!-- Servlet 响应的请求路径. 如果访问这个路径, 这个 Servlet 就会响应 -->
- <url-pattern>/MyFirstServlet</url-pattern>
- </servlet-mapping>
说明:
- <url-pattern>
- : 这个 url-pattern 可以配置多个, 这时表示的就是访问这些 url 都会触发这个 Servlet 进行响应, 运行浏览器, 访问刚才配置的
- url 路径, Servlet 的 service 方法就会被调用.
- <url-pattern>
- 中的文本内容必须以 / 或 *. 开始书写路径. 相当于将资源映射到项目根目录下形成虚拟的资源文件.
- <servlet-mapping>
- 中的
- < url-pattern>
- 可以声明多个, 可以通过任意一个都可以访问. 但是开发中一般只会配置一个.
6 在 WebContent 目录下创建 index.html
7 在 index.HTML 中加入超链接 <a href="MyFirstServlet">To Servlet</a>
8 点击超链接测试 Servlet
如果配置文件一旦修改, 需要重启服务器来重新部署 Web 项目.
Servlet 作用
接收请求 [解析请求报文中的数据: 请求参数]
处理请求 [DAO 和数据库交互]
完成响应 [设置响应报文]
Servlet 生命周期
简单的叙述生命周期, 就是对象在容器中从开始创建到销毁的过程.
Servlet 对象是 Servlet 容器创建的, 生命周期方法都是由容器调用的. 这里指的就是 Tomcat
1 Servlet 对象的创建: 构造器
默认情况下, Servlet 容器第一次收到 HTTP 请求时创建对应 Servlet 对象.
容器之所以能做到这一点是由于我们在注册 Servlet 时提供了全类名, 容器使用反射技术创建了 Servlet 的对象.
2 Servlet 对象初始化: init()
Servlet 容器创建 Servlet 对象之后, 会调用 init(ServletConfig config)方法.
作用: 是在 Servlet 对象创建后, 执行一些初始化操作. 例如, 读取一些资源文件, 配置文件, 或建立某种连接(比如: 数据库连接)
init()方法只在创建对象时执行一次, 以后再接到请求时, 就不执行了
在 javax.servlet.Servlet 接口中, public void init(ServletConfig config)方法要求容器将 ServletConfig 的实例对象传入, 这也是我们获取 ServletConfig 的实例对象的根本方法.
3 处理请求: service()
在 javax.servlet.Servlet 接口中, 定义了 service(ServletRequest req, ServletResponse res)方法处理 HTTP 请求.
在每次接到请求后都会执行.
Servlet 的作用, 主要在此方法中体现.
同时要求容器将 ServletRequest 对象和 ServletResponse 对象传入.
4 Servlet 对象销毁: destroy()
服务器重启, 服务器停止执行或 Web 应用卸载时会销毁 Servlet 对象, 会调用 public void destroy()方法.
此方法用于销毁之前执行一些诸如释放缓存, 关闭连接, 保存内存数据持久化等操作.
Servlet 请求过程
第一次请求
调用构造器, 创建对象
执行 init()方法
执行 service()方法
后面请求
执行 service()方法
对象销毁前
执行 destroy()方法
ServletConfig 接口
ServletConfig 接口封装了 Servlet 配置信息, 这一点从接口的名称上就能够看出来.
每一个 Servlet 都有一个唯一对应的 ServletConfig 对象, 代表当前 Servlet 的配置信息.
对象由 Servlet 容器创建, 并传入生命周期方法 init(ServletConfig config)中. 可以直接获取使用.
代表当前 Web 应用的 ServletContext 对象也封装到了 ServletConfig 对象中, 使 ServletConfig 对象成为了获取 ServletContext 对象的一座桥梁.
ServletConfig 对象的主要功能
获取 Servlet 名称: getServletName()
获取全局上下文 ServletContext 对象: getServletContext()
获取 Servlet 初始化参数: getInitParameter(String) / getInitParameterNames().
ServletContext 接口
Web 容器在启动时, 它会为每个 Web 应用程序都创建一个唯一对应的 ServletContext 对象, 意思是 Servlet 上下文, 代表当前 Web 应用.
由于一个 Web 应用程序中的所有 Servlet 都共享同一个 ServletContext 对象, 所以 ServletContext 对象也被称为 application 对象(Web 应用程序对象).
对象由 Servlet 容器在项目启动时创建, 通过 ServletConfig 对象的 getServletContext()方法获取. 在项目卸载时销毁.
ServletContext 对象的主要功能
1 获取项目的上下文路径(带 / 的项目名): getContextPath()
2 获取虚拟路径所映射的本地真实路径: getRealPath(String path)
虚拟路径: 浏览器访问 Web 应用中资源时所使用的路径.
本地路径: 资源在文件系统中的实际保存路径.
作用: 将用户上传的文件通过流写入到服务器硬盘中.
3 获取 Web 应用程序的全局初始化参数(基本不用)
设置 Web 应用初始化参数的方式是在 Web.xml 的根标签下加入如下代码
- <Web-App>
- <!-- Web 应用初始化参数 -->
- <context-param>
- <param-name>ParamName</param-name>
- <param-value>ParamValue</param-value>
- </context-param>
- </Web-App>
获取 Web 应用初始化参数
4 作为域对象共享数据
作为最大的域对象在整个项目的不同 Web 资源内共享数据.
setAttribute(key,value): 以后可以在任意位置取出并使用
getAttribute(key): 取出设置的 value 值
实现类
实现类体系
GenericServlet 实现 Servlet 接口
HttpServlet 继承 GenericServlet
创建 Servlet 的最终方式
继承 HttpServlet
GenericServlet 抽象类
GenericServlet 对 Servlet 功能进行了封装和完善, 重写了 init(ServletConfig config)方法, 用来获取 ServletConfig 对象. 此时如果 GenericServlet 的子类 (通常是自定义 Servlet) 又重写了 init(ServletConfig config)方法有可能导致 ServletConfig 对象获取不到, 所以子类不应该重写带参数的这个 init()方法.
如果想要进行初始化操作, 可以重写 GenericServlet 提供的无参的 init()方法, 这样就不会影响 ServletConfig 对象的获取.
将 service(ServletRequest req,ServletResponse res)保留为抽象方法, 让使用者仅关心业务实现即可.
HttpServlet 抽象类
专门用来处理 Http 请求的 Servlet.
对 GenericServlet 进行进一步的封装和扩展, 在 service(ServletRequest req, ServletResponse res)方法中, 将 ServletRequest 和 ServletResponse 转换为 HttpServletRequest 和 HttpServletResponse, 根据不同 HTTP 请求类型调用专门的方法进行处理.
今后在实际使用中继承 HttpServlet 抽象类创建自己的 Servlet 实现类即可. 重写 doGet(HttpServletRequest req, HttpServletResponse resp)和 doPost(HttpServletRequest req, HttpServletResponse resp)方法实现请求处理, 不再需要重写 service(ServletRequest req, ServletResponse res)方法了.
又因为我们业务中 get,post 的处理方式又都是一样的, 所以我们只需要写一种方法即可, 使用另外一种方法调用我们写好的 doXXX 方法. Web.xml 配置与之前相同.
HttpServletRequest 接口
该接口是 ServletRequest 接口的子接口, 封装了 HTTP 请求的相关信息.
浏览器请求服务器时会封装请求报文交给服务器, 服务器接受到请求会将请求报文解析生成 request 对象.
由 Servlet 容器创建其实现类对象并传入 service(HttpServletRequest req, HttpServletResponse res)方法中.
功能
1. 使用 HttpServletRequest 对象获取请求参数, 即浏览器向服务器提交的数据
- // 一个 name 对应一个值
- String userId = request.getParameter("userId");
- // 一个 name 对应一组值
- String[] soccerTeams = request.getParameterValues("soccerTeam");
- for(int i = 0; i <soccerTeams.length; i++){
- System.out.println("team"+i+"="+soccerTeams[i]);
- }
2. 获取 url 地址参数
- String path = request.getContextPath();// 重要
- System.out.println("上下文路径:"+path);
- System.out.println("端口号:"+request.getServerPort());
- System.out.println("主机名:"+request.getServerName());
- System.out.println("协议:"+request.getScheme());
3. 获取请求头信息
- String header = request.getHeader("User-Agent");
- System.out.println("user-agent:"+header);
- String referer = request.getHeader("Referer");
- System.out.println("上个页面的地址:"+referer);// 登录失败, 返回登录页面让用户继续登录
4. 请求的转发
- // 获取请求转发对象
- RequestDispatcher dispatcher = request.getRequestDispatcher("success.html");
- dispatcher.forward(request, response);// 发起转发
5. 向请求域中保存数据
- // 将数据保存到 request 对象的属性域中
- request.setAttribute("attrName", "attrValueInRequest");
- // 两个 Servlet 要想共享 request 对象中的数据, 必须是转发的关系
- request.getRequestDispatcher("/ReceiveServlet").forward(request, response);
- // 从 request 属性域中获取数据
- Object attribute = request.getAttribute("attrName");
- System.out.println("attrValue="+attribute);
HttpServletResponse 接口
该接口是 ServletResponse 接口的子接口, 封装了服务器针对于 HTTP 响应的相关信息.(暂时只有服务器的配置信息, 没有具体的和响应体相关的内容)
由 Servlet 容器创建其实现类对象, 并传入 service(HttpServletRequest req, HttpServletResponse res)方法中.
功能
1. 使用 PrintWriter 对象向浏览器输出数据
- // 通过 PrintWriter 对象向浏览器端发送响应信息
- PrintWriter writer = res.getWriter();
- writer.write("Servlet response");
- writer.close();
写出的数据可以是页面, 页面片段, 字符串等
当写出的数据包含中文时, 浏览器接收到的响应数据就可能有乱码. 为了避免乱码, 可以使用 Response 对象在向浏览器输出数据前设置响应头.
2. 设置响应头
response.setHeader("Content-Type", "text/html;charset=UTF-8");
设置好以后, 会在浏览器的响应报文中看到设置的响应头中的信息.
3. 重定向请求
- // 注意路径问题, 加上 / 会失败, 会以主机地址为起始, 重定向一般需要加上项目名
- response.sendRedirect("success.html");
通过重定向将页面的地址交给浏览器并设置响应状态码为 302, 浏览器会自动进行跳转.
转发与重定向
请求转发
第一个 Servlet 接收到了浏览器端的请求, 进行了一定的处理, 然后没有立即对请求进行响应, 而是将请求 "交给下一个 Servlet" 继续处理, 下一个 Servlet 处理完成之后对浏览器进行了响应. 在服务器内部将请求 "交给" 其它组件继续处理就是请求的转发.
转发的情况下, 两个 Servlet 可以共享同一个 Request 对象中保存的数据.
当需要将后台获取的数据传送到 JSP 上显示的时候, 就可以先将数据存放到 Request 对象中, 再转发到 JSP 从属性域中获取. 此时由于是 "转发", 所以它们二者共享 Request 对象中的数据.
转发的情况下, 可以访问 Web-INF 下的资源.
转发以 "/" 开始表示项目根路径, 重定向以 "/" 开始表示主机地址.
- //1. 使用 RequestDispatcher 对象封装目标资源的虚拟路径
- RequestDispatcher dispatcher = request.getRequestDispatcher("/index.html");
- //2. 调用 RequestDispatcher 对象的 forward()方法 "前往" 目标资源
- //[注意: 传入的参数必须是传递给当前 Servlet 的 service 方法的
- // 那两个 ServletRequest 和 ServletResponse 对象]
- dispatcher.forward(request, response);
- }
请求重定向
第一个 Servlet 接收到了浏览器端的请求, 进行了一定的处理, 然后给浏览器一个特殊的响应消息, 这个特殊的响应消息会通知浏览器去访问另外一个资源, 这个动作是服务器和浏览器自动完成的. 整个过程中浏览器端会发出两次请求, 且在浏览器地址栏里面能够看到地址的改变, 改变为下一个资源的地址.
重定向的情况下, 原 Servlet 和目标资源之间就不能共享请求域数据了.
HttpServletResponse 代表 HTTP 响应, 对象由 Servlet 容器创建.
- //1. 调用 HttpServletResponse 对象的 sendRedirect()方法
- //2. 传入的参数是目标资源的虚拟路径
- response.sendRedirect("index.html");
对比请求的转发与重定向
转发 | 重定向 | |
---|---|---|
浏览器感知 | 在服务器内部完成,浏览器感知不到 | 服务器以 302 状态码通知浏览器访问新地址,浏览器有感知 |
浏览器地址栏 | 不改变 | 改变 |
整个过程发送请求次数 | 一次 | 两次 |
执行效率 | 效率高 | 效率低 |
API(或发起者) | Request 对象 | Response 对象 |
能否共享 request 对象数据 | 能 | 否 |
WEB-INF 下的资源 | 能访问 | 不能访问 |
目标资源 | 不局限于当前 web 应用 |
说明: 默认情况下, 浏览器是不能访问服务器 Web-inf 下的资源的, 而服务器是可以访问的.
字符编码问题
解决乱码的方法: 就是统一字符编码
GET 请求乱码
GET 请求参数是在地址后面的. 我们需要修改 tomcat 的配置文件. 需要在 server.xml 文件修改 Connector 标签, 添加 URIEncoding="utf-8" 属性.
一旦配置好以后, 可以解决当前工作空间中所有的 GET 请求的乱码问题.
POST 请求乱码
POSTt 请求服务器解析出现问题, 解决方法: 在获取参数值之前, 设置请求的解码格式, 使其和页面保持一致.
request.setCharacterEncoding("utf-8");
POST 请求乱码问题的解决, 只适用于当前的操作所在的类中. 不能类似于 GET 请求一样统一解决. 因为请求体有可能会上传文件.
响应乱码
向浏览器发送响应的时候, 要告诉浏览器, 我使用的字符集是哪个, 浏览器就会按照这种方式来解码
- // 方法一:
- response.setHeader("Content-Type", "text/html;charset=utf-8");
- // 方法二:
- response.setContentType("text/html;charset=utf-8");
路径设置问题
相对路径和绝对路径
相对路径: 虚拟路径如果不以 "/" 开始, 就是相对路径, 浏览器会以当前资源所在的虚拟路径为基准对相对路径进行解析, 从而生成最终的访问路径.
绝对路径: 虚拟路径以 "/" 开始, 就是绝对路径.
1 在服务器端: 虚拟路径最开始的 "/" 表示当前 Web 应用的根目录. 只要是服务端解析的绝对路径, 都是以 Web 根目录为起始的. 由服务器解析的路径包括:(1) Web.xml 的配置路径,(2)request 转发的路径.
2 在浏览器端: 虚拟路径最开始的 "/" 表示当前主机地址.
例如: 链接地址 "/Path/dir/b.html" 经过浏览器解析后为: 相当于 http://localhost:8989/Path/dir/b.HTML
由浏览器解析的路径包括:
- <!-- 给页面中的相对路径设置基准地址 -->
- <base href="http://localhost:8080/Test_Path/" />
来源: https://www.cnblogs.com/Open-ing/p/12075074.html