- 学习目标
- Servlet概论
- Servlet 是什么?
- Servlet 架构
- Servlet 任务
- Servlet 包
- Servlet与Tomcat版本
- Servlet基础
- 创建动态web工程:
- web.xml配置servlet
- 注解配置Servlet
- Servlet 生命周期
- init()方法
- service()方法
- destroy()方法
- Servlet体系结构
- ServletRequest接口
- 相关方法
- 获得客户端信息
- 获得客户端请求参数
- 案例:获取请求头
- 获得客户端请求参数
- `html`文件
- 案例:处理表单
- Request请求转发
- 案例:转发
- 文件上传
- 案例:文件上传
- ServletResponse接口
- 相关方法
- Response重定向
- Response 跨域
- 案例:文件下载
- SerlvetConfig对象
- ServletContext对象
- 获取MIME类型数据
- ServletContext转发
- ServletContextListener 接口
- `web.xml`配置
- 使用 Listener
- Servlet Filter 过滤器
- Servlet 过滤器方法
- 案例:统计IP
- @WebFilter
- @WebFilter 的属性
- 掌握 servlet 实现原理
- 掌握 servlet 生命周期
- 掌握 servlet 监听器的使用
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
–servlet
使用 Servlet,可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Servlet 架构下图显示了 Servlet 在 Web 应用程序中的位置。
Servlet 任务Servlet 执行以下主要任务:
- 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
- 读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
- 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
- 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
- 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
Java Servlet 是运行在带有支持 Java Servlet 规范的解释器的 web 服务器上的 Java 类。
Servlet 可以使用 javax.servlet 和 javax.servlet.http 包创建,它是 Java 企业版的标准组成部分,Java 企业版是支持大型开发项目的 Java 类库的扩展版本。
Servlet与Tomcat版本官方说明:
Servlet基础 创建动态web工程:说明:
dynamic web module指servlet的版本,servlet被包含在tomcat中。
- dynamic web module 2.4 对应Tomcat 5.5
- dynamic web module 2.5 对应Tomcat 6.0
- dynamic web module 3.0 对应Tomcat 7.0
- dynamic web module 3.1 对应Tomcat 7.0或者更高
- dynamic web module 4 对应Tomcat 8.0或者更高
一直next直到如下:
web.xml配置servlet 注解配置Servlet在servlet3.0以后,我们可以不用再web.xml里面配置servlet,只需要加上@WebServlet注解就可以修改该servlet的属性了。
下面是@WebServlet的属性列表。
从上表可见,web.xml可以配置的servlet属性,在@WebServlet中都可以配置。
如:
@WebServlet(description="描述", urlPatterns={ "/aaa"}, loadonStartup=1) public class indexServlet extends HttpServlet{}Servlet 生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。
- Servlet 通过调用 init () 方法进行初始化。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 通过调用 destroy() 方法终止(结束)。
- Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
init 方法被设计成只调用一次。
它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。
因此,它是用于一次性初始化。
Servlet 创建于用户第一次方位该Servlet的URL 时,但是也可以指定 Servlet 在服务器第一次启动时被加载。
每一个用户请求都会创建一个 Servlet 实例,并且产生一个新的线程。
init() 方法简单地创建或加载一些数据,这些数据将作用于 Servlet 的整个生命周期。
service()方法service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
destroy()方法destroy() 方法只会在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
Servlet执行过程
- 同一个servlet是单例多线程的,只存在一个servlet对象,由多个线程异步执行该对象的方法。因此servlet的类属性是“线程不安全的”,对象属性是线程安全的,因为每个线程都会拷贝一份对象属性。
- Servelet容器维护了一个线程(等待执行代码的一组线程【Worker Thread】)池来处理各种请求。
- Servlet容器使用一个调度线程【Dispatcher Thread】来管理【Worker Thread】。
- 当tomcat启动时,按照在web.xml文件
元素中配置的 序号 的顺序,容器会自动创建每个不同的Servlet对象,并执行。 - 当接受到第一个请求时,容器创建非Servlet
的servlet对象,并执行。 - 执行过程按照Servlet生命周期执行
ServletRequest` -> `HttpServletRequest相关方法
- getRequestURL方法返回客户端发出请求时的完整URL。
- getRequestURI方法返回请求行中的资源名部分。
- getQueryString 方法返回请求行中的参数部分。
- getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以/开头。
- getRemoteAddr方法返回发出请求的客户机的IP地址。
- getRemoteHost方法返回发出请求的客户机的完整主机名。
- getRemotePort方法返回客户机所使用的网络端口号。
- getLocalAddr方法返回WEB服务器的IP地址。
- getLocalName方法返回WEB服务器的主机名。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z3HlqTJc-1641215496469)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211230155915260.png)]
获得客户端请求参数获取(客户端提交的数据)
- getParameter(String)方法(常用)-获取单个字段
- getParameterValues(String name)方法(常用)-获取多选字段
- getParameterMap()方法(编写框架时常用)- 将所有字段全部封装成map
- getParameterNames()方法(不常用)
package com.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServletTestRequest2 extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("UTF-8");// 设置将字符以"UTF-8"编码输出到客户端浏览器 // 通过设置响应头控制浏览器以UTF-8的编码显示数据 resp.setHeader("content-type", "text/html;charset=UTF-8"); PrintWriter out = resp.getWriter(); Enumeration获得客户端请求参数reqHeadInfos = req.getHeaderNames();// 获取所有的请求头 out.write("获取到的客户端所有的请求头信息如下:"); out.write("
"); while (reqHeadInfos.hasMoreElements()) { String headName = (String) reqHeadInfos.nextElement(); String headValue = req.getHeader(headName);// 根据请求头的名字获取对应的请求头的值 out.write(headName + ":" + headValue); out.write("
"); } out.write("
"); out.write("获取到的客户端Accept-Encoding请求头的值:"); out.write("
"); String value = req.getHeader("Accept-Encoding");// 获取Accept-Encoding请求头对应的值 out.write(value); Enumeratione = req.getHeaders("Accept-Encoding"); while (e.hasMoreElements()) { String string = (String) e.nextElement(); System.out.println(string); } } }
获取(客户端提交的数据)
- getParameter(String)方法(常用)-获取单个字段
- getParameterValues(String name)方法(常用)-获取多选字段
- getParameterMap()方法(编写框架时常用)- 将所有字段全部封装成map
- getParameterNames()方法(不常用)
案例:处理表单Html的Form表单元素
package com.servlet; import java.io.IOException; import java.text.MessageFormat; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServletTestRequest3 extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 客户端是以UTF-8编码提交表单数据的,所以需要设置服务器端以UTF-8的编码进行接收,否则对于中文数据就会产生乱码 req.setCharacterEncoding("UTF-8"); String userid = req.getParameter("userid");// 获取填写的编号,userid是文本框的名字, String username = req.getParameter("username");// 获取填写的用户名 String userpass = req.getParameter("userpass");// 获取填写的密码 String sex = req.getParameter("sex");// 获取选中的性别 String dept = req.getParameter("dept");// 获取选中的部门 // 获取选中的兴趣,因为可以选中多个值,所以获取到的值是一个字符串数组,因此需要使用getParameterValues方法来获取 String[] insts = req.getParameterValues("inst"); String note = req.getParameter("note");// 获取填写的说明信息 String hiddenField = req.getParameter("hiddenField");// 获取隐藏域的内容 String instStr = ""; for (int i = 0; insts != null && i < insts.length; i++) { if (i == insts.length - 1) { instStr += insts[i]; } else { instStr += insts[i] + ","; } } String htmlStr = "" + "Request请求转发填写的编号:{0} " + "填写的用户名:{1} " + "填写的密码:{2} " + "选中的性别:{3} " + "选中的部门:{4} " + "选中的兴趣:{5} " + "填写的说明:{6} " + "隐藏域的内容:{7} " + ""; htmlStr = MessageFormat.format(htmlStr, userid, username, userpass, sex, dept, instStr, note, hiddenField); resp.setCharacterEncoding("UTF-8");// 设置服务器端以UTF-8编码输出数据到客户端 resp.setContentType("text/html;charset=UTF-8");// 设置客户端浏览器以UTF-8编码解析数据 resp.getWriter().write(htmlStr);// 输出htmlStr里面的内容到客户端浏览器显示 } }
请求转发:指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。
请求转发的应用场景:MVC设计模式
案例:转发package com.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServletTestRequest4 extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher reqDispatcher =this.getServletContext().getRequestDispatcher("/test.jsp"); reqDispatcher.forward(req, resp); } }文件上传
实现文件上传需要在 form 表单中使用 multipart/form-data 属性,让表单处理文件上传 。文件的上传是以流的形式提交给服务器的
案例:文件上传页面
图片上传
servlet 中处理上传的文件的时候需要在 servlet 上添加一个 @MultipartConfig ,表示处理 multipart/form-data 类型的表单。
package com.test.servlet; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import javax.imageio.ImageIO; import javax.imageio.ImageReadParam; import javax.imageio.ImageReader; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; @MultipartConfig @WebServlet("/imageProcess") public class ImageProcessPrimitiveServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("request.getContentType(): " + request.getContentType()); if(!request.getContentType().split(";")[0].equals("multipart/form-data")) return; CollectionServletResponse接口parts = request.getParts(); System.out.println(parts); for(Part part:parts){ System.out.println(part); FileProcess(part); } response.getWriter().print("end"); } private void FileProcess(Part part) throws IOException { System.out.println("part.getName(): " + part.getName()); if(part.getName().equals("fileUploader")){ String cd = part.getHeader("Content-Disposition"); String[] cds = cd.split(";"); String filename = cds[2] .substring(cds[2].indexOf("=")+1) .substring(cds[2].lastIndexOf("//")+1) .replace(""", ""); String ext = filename.substring(filename.lastIndexOf(".")+1); System.out.println("filename:" + filename); System.out.println("ext:" + ext); InputStream is = part.getInputStream(); if(Arrays.binarySearch(ImageIO.getReaderFormatNames(),ext) >= 0) imageProcess(filename, ext, is); else{ commonFileProcess(filename, is); } } } private void commonFileProcess(String filename, InputStream is) { FileOutputStream fos = null; try{ fos=new FileOutputStream(new File(getClass().getResource("/").getPath()+filename)); int b = 0; while((b = is.read())!=-1){ fos.write(b); } }catch(Exception e){ e.printStackTrace(); }finally{ try{ fos.close(); }catch(Exception e){ e.printStackTrace(); } } } private void imageProcess(String filename, String ext, InputStream is) throws IOException { Iterator irs = ImageIO.getImageReadersByFormatName(ext); ImageReader ir = irs.hasNext()?irs.next():null; if(ir == null) return; ir.setInput(ImageIO.createImageInputStream(is));//必须转换为ImageInputStream,否则异常 ImageReadParam rp = ir.getDefaultReadParam(); Rectangle rect = new Rectangle(0,0,200,200); rp.setSourceRegion(rect); int imageNum = ir.getNumImages(true);//allowSearch必须为true,否则有些图片格式imageNum为-1。 System.out.println("imageNum:" + imageNum); for(int imageIndex = 0;imageIndex < imageNum;imageIndex++){ BufferedImage bi = ir.read(imageIndex,rp); ImageIO.write(bi, ext, new File(getClass().getResource("/").getPath()+filename)); } } }
ServletResponse` -> `HttpServletResponse相关方法
常用的方法和 *** 作说明:
常用的方法
- addcookie(cookie cookie) 向客户端写入cookie
- addHeader(String name, String value) 写入给定的响应头
- encodeURL(String url) 默认cookie中包含Session ID,如果客户端不支持 cookie,就在参数 url 中加入 Session ID 信息,可以解决用户禁用cookie的问题。
- setStatus(200) 设置响应的状态码。
- sendError(404, "您要查找的资源不存在"):当发送错误状态码时,Tomcat会跳转到固定的错误页面去,但可以显示错误信息。
- setCharaceterEncoding(String arg);设置字符集编码
- setContentType("text/html;charset=utf-8"):等同与调用response.setHeader("content-type", "text/html;charset=utf-8");
getOutputStream和getWriter方法的区别
- getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。
- getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。
这两个方法写入的数据会作为响应消息的正文,与响应状态行和各响应头组合后输出到客户端。
Serlvet的service方法结束后,web容器将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,web容器将调用close方法关闭该输出流对象。
Response重定向- 重定向指的是一个web资源收到客户端请求后,web服务器通知客户端去访问另外一个web资源,这称之为请求重定向。
- 实现方式是调用response.sendRedirect()方法。实现的原理就是给客户端返回了302状态码和location头。
跨域是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对Javascript实施的安全限制。
所谓的同源是指,域名、协议、端口均为相同。如果其中一项不同则为跨域
同源策略限制了一下行为:
- 无法读取非同源的cookie、LocalStorage 和 IndexDB
- 无法获取非同源的 DOM 和 JS 对象
- 无法发送 Ajax 请求
解决跨域的方法:设置允许跨域访问
response.setHeader("Access-Control-Allow-Origin", "*"); // 允许的域名,逗号分割 response.setHeader("Access-Control-Allow-Methods", "*"); // 允许的请求方法,逗号分割 response.setHeader("Access-Control-Max-Age", "3600"); // 允许跨域的超时时间,单位秒 response.setHeader("Access-Control-Allow-Headers", "*"); // 允许的请求头参数,逗号分割 response.setHeader("Access-Control-Allow-Credentials", "true"); // 是否将响应头信息暴露给页面,如:cookies案例:文件下载
package com.servlet; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServletTestResponse extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { downloadFileByOutputStream(response);// 下载文件,通过OutputStream流 } private void downloadFileByOutputStream(HttpServletResponse response) throws FileNotFoundException, IOException { // 1.获取要下载的文件的绝对路径 String realPath = this.getServletContext().getRealPath("/download/demo.jpg"); // 2.获取要下载的文件名 String fileName = realPath.substring(realPath.lastIndexOf("\") + 1); // 3.设置content-disposition响应头控制浏览器以下载的形式打开文件 response.setHeader("content-disposition", "attachment;filename=" + fileName); // 4.获取要下载的文件输入流 InputStream in = new FileInputStream(realPath); int len = 0; // 5.创建数据缓冲区 byte[] buffer = new byte[1024]; // 6.通过response对象获取OutputStream流 OutputStream out = response.getOutputStream(); // 7.将FileInputStream流写入到buffer缓冲区 while ((len = in.read(buffer)) > 0) { // 8.使用OutputStream将缓冲区的数据输出到客户端浏览器 out.write(buffer, 0, len); } in.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }SerlvetConfig对象
SerlvetConfig对象:代表的是当前Servlet初始化配置项的信息。
利用ServletConfig对象主要是为了得到Context对象。(Context对象可以帮助我们与用户进行交互)
-
获得指定初始化参数的值:
- String getInitParameter(String name); 【需要在web.xml文件中进行配置】
-
获得所有初始化参数的名称:
- Enumeration getInitParameterNames();【需要在web.xml文件中进行配置】
-
获得
ServletContext
对象:
- ServletContext getServletContext();
-
获得当前
Servlet
的名称:
- String getServletName();
在Servlet 的配置文件中,可以用一个或多个
当servlet配置了初始化参数之后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给Servlet。进而,程序员通过Servlet对象得到当前servlet的初始化参数信息。
获取ServletConfig中初始化信息步骤:
Web.xml配置文件
Servlet index.html index.htm index.jsp default.html default.htm default.jsp MyServlet com.servlet.MyServlet charset UTF-8 name 张三 MyServlet /star
案例
package com.servlet; import java.util.Enumeration; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; public class MyServletTestConfig extends HttpServlet { private ServletConfig config = null; @Override public void init(ServletConfig config) throws ServletException { this.config = config; // 获取初始化参数 String value1 = this.config.getInitParameter("charset"); // 获得配置文档中ServletContext对象标签下name对应的value String value2 = this.config.getInitParameter("name"); // 获取所有初始化参数 Enumeration e = this.config.getInitParameterNames(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String value = this.config.getInitParameter(name); System.out.println(name + "=" + value); } } }
Web容器在启动时会为每个web应用创建一个ServletContext对象,而这个ServletContext对象就代表当前这个web应用。
因为一个ServletContext对象代表一个web应用,所以该web应用中所有的Servlet和其他资源都共享一个ServletContext对象,
可以通过ServletContext对象进行Servlet对象之间的通讯。而ServletContext对象也称之为Context域对象。
获取ServletContext对象的两种方式
//两种获取ServletContext对象的方法: ServletContext context = this.getServletConfig().getServletContext(); ServletContext context2 = this.getServletContext(); //System.out.println(context1 == context2); //ture context.setAttribute("lover", "LRR"); context.getAttribute("lover");
获取和设置参数
在ServletContext类中还有getInitParameter(String name)方法或者getInitParameterNames()方法。
这两个方法获取的是web应用所配置的参数(毕竟ServletContext代表web应用),就像ServletConfig中类似的方法获取的是某个Servlet中的
而对于配置web应用的参数是在web.xml文件中使用
这种配置
举个例子,对于整个web应用配置数据库连接,这样在web应用中的每个Servlet都可以使用,而无需再在每个Servlet中都单独设置一次,提高了效率。
Web.xml配置文件
Servlet index.html index.htm index.jsp default.html default.htm default.jsp username root password root MyServlet com.servlet.MyServlet charset UTF-8 name 张三 MyServlet /star
案例
package com.servlet; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServletTestContext extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 两种获取ServletContext对象的方法: ServletContext context = this.getServletConfig().getServletContext(); ServletContext context2 = this.getServletContext(); // System.out.println(context1 == context2); //ture String username = context.getInitParameter("username"); String password = context.getInitParameter("password"); System.out.println(username + ":" + password); } }获取MIME类型数据
ServletContext`类中的`getMimeType(String file)`方法用于返回该文件的`MIME`类型。用于确定返回类型`resp.setContentType(type)
案例
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String filename = "1.html"; ServletContext context = this.getServletContext(); System.out.println(context.getMimeType(filename)); resp.setContentType(context.getMimeType(filename)); }ServletContext转发
在ServletContext对象中还有这么两个方法:getNameDispatcher(String name)(不常用)和getRequestDispatcher(String path),返回的是RequestDispatcher对象。
可以实现将一个Servlet中的数据交个另一个Servlet来处理,或者Servlet将某个实时数据交给JSP来显示,虽然我们在浏览器中访问的是最开始的Servlet,但是进行转发后看到的其他web资源,而浏览器的地址栏不会改变。
注:在请求对象request对象中也有这么一个getRequestDispatcher(String path)方法,功能与ServletContext对象的这个方法一样,也可以实现转发,因此用哪个对象都行,没有区别。
案例
main com.star.servlet.MainServlet main / demo01 com.star.servlet.MyServlet demo01 /test
文件1
//统一对外访问的入口 //welcome?path=copy public class MainServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 将数据存至web应用的配置中 this.getServletContext().setAttribute("data", "自定义转发的数据"); // 当访问welcome,调到copy来执行 String path = req.getParameter("path"); if (path == null) { return; } switch (path) { case "copy": // 跳转到MyServlet2去执行 RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/copy"); rd.forward(req, resp); break; default: resp.setContentType("text/html"); resp.setCharacterEncoding("UTF-8"); resp.getWriter().write("参数不合法"); break; } } }
复制一份新的Servlet
public class MyServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); resp.setCharacterEncoding("UTF-8"); resp.getWriter().write("欢迎访问/copy"); } }ServletContextListener 接口
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。
当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。
contextInitialized(ServletContextEvent sce) contextDestroyed(ServletContextEvent sce)web.xml配置
使用 ListenerServlet index.html index.htm index.jsp default.html default.htm default.jsp com.servlet.ServletContextLTest username root password root MyServlet com.servlet.MyServlet charset UTF-8 name 张三 MyServlet /star
package com.servlet; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyServletTestContextListener implements ServletContextListener { // 实现其中的销毁函数 public void contextDestroyed(ServletContextEvent sce) { System.out.println("this is last destroyeed"); } // 实现其中的初始化函数,当有事件发生时即触发 public void contextInitialized(ServletContextEvent sce) { ServletContext sct = sce.getServletContext(); MapServlet Filter 过滤器depts = new HashMap (); depts.put(1, "张三"); depts.put(2, "132456"); // 将所取到的值存放到一个属性键值对中 sct.setAttribute("dept", depts); } }
Servlet 过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。
可以将一个或多个 Servlet 过滤器附加到一个 Servlet 或一组 Servlet。
Servlet 过滤器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 页面。调用 Servlet 前调用所有附加的 Servlet 过滤器。
Servlet 过滤器是可用于 Servlet 编程的 Java 类,可以实现以下目的:
- 在客户端的请求访问后端资源之前,拦截这些请求。
- 在服务器的响应发送回客户端之前,处理这些响应。
根据规范建议的各种类型的过滤器:
- 身份验证过滤器(Authentication Filters)。
- 数据压缩过滤器(Data compression Filters)。
- 加密过滤器(Encryption Filters)。
- 触发资源访问事件过滤器。
- 图像转换过滤器(Image Conversion Filters)。
- 日志记录和审核过滤器(Logging and Auditing Filters)。
- MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。
- 标记化过滤器(Tokenizing Filters)。
- XSL/T 过滤器(XSL/T Filters),转换 XML 内容。
过滤器根据web.xml文件中先后顺序执行
Servlet 过滤器方法过滤器是一个实现了 javax.servlet.Filter 接口的 Java 类。javax.servlet.Filter 接口定义了三个方法:
-
public void doFilter (ServletRequest, ServletResponse, FilterChain)
- 该方法完成实际的过滤 *** 作,当客户端请求方法与过滤器设置匹配的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain用户访问后续过滤器。
-
public void init(FilterConfig filterConfig)
- web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
-
public void destroy()
- Servlet容器在销毁过滤器实例前调用该方法,在该方法中释放Servlet过滤器占用的资源。
web.xmlServlet index.html index.htm index.jsp default.html default.htm default.jsp com.servlet.MyServletTestContextListener username root password root dataFilter com.servlet.MyServletTestFilter test 测试数据 dataFilter /* MyServlet com.servlet.MyServlet charset UTF-8 name 张三 MyServlet /star MyTestFilter com.servlet.MyServletTestFilterServlet MyTestFilter /testfilter
过滤器过滤 IP
DataFilter.java
package com.servlet; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class MyServletTestFilter implements Filter { private FilterConfig filterConfig; @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化参数,ipCount用来存放ip及访问次数 ServletContext application = filterConfig.getServletContext(); MapipCount = new HashMap (); application.setAttribute("ipCount", ipCount); this.filterConfig = filterConfig; } @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletContext application = filterConfig.getServletContext(); Map ipCount = (HashMap ) application.getAttribute("ipCount"); String ip = request.getRemoteAddr(); Integer count = ipCount.get(ip); if (count != null) { // Map中存在该ip count = count + 1; } else { count = 1; } ipCount.put(ip, count); System.out.println(ipCount.size()); application.setAttribute("ipCount", ipCount); chain.doFilter(request, response); } }
以表格形式返回数据
MyServletTestFilterServlet.java
package com.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServletTestFilterServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置响应类型 resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); ServletContext application = req.getServletContext(); MapipCount = (Map ) application.getAttribute("ipCount"); // 实际的逻辑是在这里 PrintWriter out = resp.getWriter(); out.println("分IP统计网站浏览次数"); out.print(""); Iterator > iterator = ipCount.entrySet().iterator(); while (iterator.hasNext()) { out.print(" "); out.print(""); out.print(iterator.next().getKey()); out.print(""); out.print(""); out.print(iterator.next().getValue()); out.print(""); out.print(" "); } out.print(""); } }
结果
@WebFilterServlet 3.0 之后可以使用 @WebFilter 来配置Filter
@WebFilter 的属性之前控制多个filter的执行顺序是通过web.xml中控制filter的位置来控制的。
放在上面的会比放在下面的先执行,如下“用户登录检查过滤器”会比“接口日志过滤器”先执行。
但是当我们使用@WebFilter注解的时候发现注解里面没有提供可以控制执行顺序的参数。
通过实践发现如果想要控制filer的执行顺序可以通过控制filter的文件名来控制
比如:
UserLoginFilter.java 和 ApiLog.java 这两个文件里面分别是“用户登录检查过滤器”和“接口日志过滤器”,因为这两个文件的首字母A排U之前,导致每次执行的时候都是先执行“接口日志过滤器”再执行“用户登录检查过滤器”,所以我们现在修改两个文件的名称分别为
- Filter0_UserLogin.java
- Filter1_ApiLog.java
这样就能先执行“用户登录检查过滤器”再执行“接口日志过滤器”。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)