Servlet/过滤器/拦截器/监听器:
1.概述:
1.1 服务程序 Servlet:
Servlet 是 Sun公司在开发动态Web的一门技术,Sun在API中提供一个接口叫做:Servlet,如果你想开发一个Servlet程序(把实现了Servlet接口的Java程序叫做:Servlet),只需要完成两个小步骤:
- 编写一个类,实现Servlet接口
- 把开发好的Java类部署到Web服务器中
Servlet接口在Sun公司有两个默认的实现类:HttpServlet、GenericServlet。Servlet是一种运行服务器端的Java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层。
1.2 过滤器 Filter:
filter是一个可以复用的代码片段,可以用来转换HTTP请求、响应和头信息。Filter不像Servlet,它不能产生一个请求或者响应,它只是修改对某一资源的请求,或者修改从某一的响应。
1.3 拦截器 Interceptor:
在面向切面编程中,就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法,比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
1.4 监听器 Listener:
监听器,从字面上可以看出listener主要用来监听应用。通过listener可以监听web服务器中某一个执行动作,并根据其要求作出相应的响应。通俗的语言说就是在application,session,request三个对象创建消亡或者往其中添加修改删除属性时自动执 行代码的功能组件。
2.生命周期:
2.1 Servlet 生命周期:
Servlet的生命周期始于它被装入web服务器的内存时,并在web服务器终止或重新装入servlet时结束。servlet一旦被装入web服务器,一般不会从web服务器内存中删除,直至web服务器关闭或重新结束。
- 装入:启动服务器时加载Servlet的实例;
- 初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工作有
init()
方法负责执行完成; - 调用:从第一次到以后的多次访问,都是只调用doGet()或doPost()方法;
- 销毁:停止服务器时调用destroy()方法,销毁实例。
2.2 Filter 生命周期:
(一定要实现javax.servlet包的Filter接口的三个方法init()
、doFilter()
、destroy()
,空实现也行)
- 启动服务器时加载过滤器的实例,并调用
init()
方法来初始化实例; - 每一次请求时都只调用方法
doFilter()
进行处理; - 停止服务器时调用
destroy()
方法,销毁实例。
2.3 Listener 生命周期:
Listener 类似于 servlet 和 filter,web.xml 的加载顺序是:context- param
-> listener
-> filter
-> servlet
2.4 Interceptor 生命周期:
以struts的拦截器为例,加载了struts.xml以后,初始化相应拦截器。当action请求来时调用intercept方法,服务器停止销毁interceptor。
3.Servlet 工程:
- 步骤一:编写一个Servlet程序,编写一个普通类。实现Servlet接口,这里我们直接继承HttpServlet实现类
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; public class HelloServlet extends HttpServlet { //由于get或者post只是请求实现的不同方式,可以相互调用,业务逻辑一样; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); //获取响应输出流 writer.println("Hello,Servlet!!!"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
- 步骤二:编写Servlet映射
为什么需要映射?我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接Web服务器,所以我们需要在Web服务中注册我们写的Servlet,还需要给他一个浏览器访问的路径。
<!--注册Servlet--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.krian.servlet.HelloServlet</servlet-class> </servlet> <!--Servlet请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
- 步骤三:配置Tomcat服务器
注意部署时细节:
4.Servlet 原理:
Servlet是由Web服务器调用,Web服务器在收到浏览器请求之后,处理请求,返回处理结果!!!
Servlet 中主要的接口:
Servlet接口定义了Servlet与servlet容器之间的契约。这个契约是:Servlet容器将Servlet类载入内存,并产生Servlet实例和调用它具体的方法。但是要注意的是,在一个应用程序中,每种Servlet类型只能有一个实例。
用户请求致使Servlet容器调用Servlet的Service()方法,并传入一个ServletRequest对象和一个ServletResponse对象。ServletRequest对象和ServletResponse对象都是由Servlet容器(例如TomCat)封装好的,并不需要程序员去实现,程序员可以直接使用这两个对象。
ServletRequest中封装了当前的Http请求,因此,开发人员不必解析和操作原始的Http数据。ServletResponse表示当前用户的Http响应,程序员只需直接操作ServletResponse对象就能把响应轻松的发回给用户。
对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。
Mapping映射问题:
1.一个Servlet可以指定一个映射路径:
<!--Servlet请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
2.一个Servlet可以指定多个映射路径:
请求的是同一个资源,映射了不同的请求路径!!!
<!--Servlet请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello1</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping>
3.一个Servlet可以指定通用映射路径:
<!--Servlet通用请求--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>hello/*</url-pattern> </servlet-mapping>
4.指定一些后缀或者前缀等......
<!--自定义后缀实现请求映射--> <!--注意,*前面不能加项目映射的路径(/)--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
5.默认请求路径:
<!--默认请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
6.优先级问题:
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求。
package com.krian.servlet; 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: Lunaticer * @Create: 2021-06-04 12:35 * @Tip: Keeping the eyes of the prize ! * @Description: Error类 */ @SuppressWarnings({"all"}) public class ErrorServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); PrintWriter writer = resp.getWriter(); writer.println("<h1>404,糟糕,页面跑丢了!!!</h1>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
<!--注册Servlet--> <servlet> <servlet-name>error</servlet-name> <servlet-class>com.krian.servlet.ErrorServlet</servlet-class> </servlet> <!--Servlet请求路径--> <servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
ServletContext对象:
Web容器在启动时,他会为每一个Web程序都创建一个对应的ServletContext对象,他代表当前的Web应用。
1.共享数据:
在一个Servlet中保存的数据,可以在另外一个Servlet中拿到:
package com.krian.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; /** * @Author: Lunaticer * @Create: 2021-06-04 14:24 * @Tip: Keeping the eyes of the prize ! * @Description: */ @SuppressWarnings({"all"}) public class ContextServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //ServletContext可以存放一些数据!!! //this.getServletContext():获取Servlet上下文 ServletContext servletContext = this.getServletContext(); //处理响应以及网页编码: resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); String userName = "krian"; //将一个数据保存在ServletContext中,使用键值对的形式: servletContext.setAttribute("userName",userName); } }
package com.krian.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: Lunaticer * @Create: 2021-06-04 14:31 * @Tip: Keeping the eyes of the prize ! * @Description: */ @SuppressWarnings({"all"}) public class GetServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); String userName = (String) servletContext.getAttribute("userName"); resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); PrintWriter writer = resp.getWriter(); writer.println("<h1> 姓名:" + userName + "</h1>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
注册Servlet,在web.xml中配置Servlet:
<?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--> <servlet> <servlet-name>contextServlet</servlet-name> <servlet-class>com.krian.Servlet.ContextServlet</servlet-class> </servlet> <!--映射Servlet--> <servlet-mapping> <servlet-name>contextServlet</servlet-name> <url-pattern>/context</url-pattern> </servlet-mapping> <servlet> <servlet-name>getServlet</servlet-name> <servlet-class>com.krian.Servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>getServlet</servlet-name> <url-pattern>/get</url-pattern> </servlet-mapping> </web-app>
2.获取初始化参数:
<!--配置web初始化参数--> <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param>
package com.krian.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; /** * @Author: Lunaticer * @Create: 2021-06-04 14:48 * @Tip: Keeping the eyes of the prize ! * @Description: */ @SuppressWarnings({"all"}) public class ParamServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置响应配置: resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html"); ServletContext servletContext = this.getServletContext(); String url = servletContext.getInitParameter("url"); resp.getWriter().println("url:" + url); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
<servlet> <servlet-name>paramServlet</servlet-name> <servlet-class>com.krian.Servlet.ParamServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>paramServlet</servlet-name> <url-pattern>/param</url-pattern> </servlet-mapping>
3.请求转发:
package com.krian.Servlet; import javax.servlet.RequestDispatcher; 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: Lunaticer * @Create: 2021-06-04 14:59 * @Tip: Keeping the eyes of the prize ! * @Description: 请求转发!!! */ @SuppressWarnings({"all"}) public class DispatcherServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); //equestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/param"); //请求转发路径 //requestDispatcher.forward(req,resp); //调用forward实现请求转发 servletContext.getRequestDispatcher("/param").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>com.krian.Servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/dispatcher</url-pattern> </servlet-mapping>
4.读取资源文件:
资源导出配置:
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
HttpServletResponse:
Web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的对象HttpServletResponse
- 如果需要获取客户端请求过来的参数:使用HttpServletRequest
- 如果要给客户端响应一些信息:使用HttpServletResponse
1.简单分类:
向浏览器发送数据的方法:
ServletOutStream getOutStream() throws IOException; PrintWrite getWriter() throws IOEception;
向浏览器发送响应头的方法:
响应的状态码:
2.下载文件:
- 向浏览器输出信息
- 下载文件
- 要获取下载文件路径
- 下载文件名称
- 设置让浏览器能够支持下载的文件内容
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端
package com.krian.Servlet; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.FileInputStream; import java.io.IOException; import java.net.URLEncoder; /** * @Author: Lunaticer * @Create: 2021-06-04 19:59 * @Tip: Keeping the eyes of the prize ! * @Description: 文件下载!!! */ @SuppressWarnings({"all"}) public class FileServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取下载文件路径: String url = "C:\\Users\\L\\Desktop\\JavaWeb\\No03_Response_Servlet\\src\\main\\resources\\OIP.jpg"; //这里使用的绝对路径 System.out.println("文件下载地址:" + url); //2.下载文件名称: String fileName = url.substring(url.lastIndexOf("\\") + 1); //3.设置想办法让浏览器能够支持文件的下载(Content-Disposition),中文文件名URLEncoder.encode编码,否则有可能乱码 resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8")); //4.获取下载文件的输入流 FileInputStream fileInputStream = new FileInputStream(url); //5.创建缓存区: int len = 0; byte[] buffer = new byte[1024]; //6.获取OutputStream对象 ServletOutputStream outputStream = resp.getOutputStream(); //7.将FileOutputStream流写入到buffer缓冲区中,使用OutputStream将缓冲区中的数据输出到客户端 while ((len = fileInputStream.read(buffer)) > 0){ outputStream.write(buffer,0,len); } //8.关闭输入输出流 fileInputStream.close(); outputStream.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
3.验证码:
package com.krian.Servlet; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; /** * @Author: Lunaticer * @Create: 2021-06-04 20:22 * @Tip: Keeping the eyes of the prize ! * @Description: */ public class ImageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置浏览器刷新: resp.setHeader("refresh","5"); //在内存中创建一张图片: BufferedImage image = new BufferedImage(100,30,BufferedImage.TYPE_INT_RGB); //获取图片: Graphics2D graphics2D = (Graphics2D) image.getGraphics(); //设置图片背景颜色: graphics2D.setColor(Color.CYAN); graphics2D.fillRect(0,0,100,30); //给图片写入数据: graphics2D.setColor(Color.DARK_GRAY); graphics2D.setFont(new Font(null,Font.BOLD,25)); graphics2D.drawString(makeNumber(),0,30); //声明浏览器,使用这个请求方式打开: resp.setContentType("image/jpeg"); //浏览器存在缓存,清除浏览器缓存: resp.setDateHeader("Expires",-1); resp.setHeader("Cache-Control","no-cache"); resp.setHeader("Pragma","no-cache"); //输出图片到浏览器: ImageIO.write(image,"jpg",resp.getOutputStream()); } private String makeNumber(){ Random random = new Random(); String num = random.nextInt(9999999) + ""; StringBuffer sb = new StringBuffer(); for (int i = 0 ; i < 7 - num.length();i++){ sb.append("0"); } return num; } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
4.实现重定向:
B有一个Web资源收到客户端A的请求后,B会通知A客户端去访问另外一个Web资源C,这个过程叫做重定向
常见场景:
- 登陆注册
package com.krian.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: Lunaticer * @Create: 2021-06-05 0:09 * @Tip: Keeping the eyes of the prize ! * @Description: */ @SuppressWarnings({"all"}) public class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //重定向: resp.sendRedirect("/image"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
面试题:请你聊聊重定向和转发的区别:
相同点:
- 页面都会发生跳转
不同点:
- 请求转发的时候,url不会发生改变
- 重定向的时候,url地址栏会发生变化
HttpServletRequest:
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest。
1.通过HttpServletRequest获取前端参数和转发请求:
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); String username = req.getParameter("username"); String password = req.getParameter("password"); String[] hobbys = req.getParameterValues("hobbys"); System.out.println("============================="); //后台接收中文乱码问题 System.out.println(username); System.out.println(password); System.out.println(Arrays.toString(hobbys)); System.out.println("============================="); System.out.println(req.getContextPath()); //通过请求转发 //这里的 / 代表当前的web应用 req.getRequestDispatcher("/success.jsp").forward(req,resp); }