Servlet servlet在Eclipce中的配置和体系结构详解

Servlet servlet在Eclipce中的配置和体系结构详解,第1张

Servlet servlet在Eclipce中的配置和体系结构详解

Servlet笔记
    • 学习目标
    • 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 的属性

学习目标
  1. 掌握 servlet 实现原理
  2. 掌握 servlet 生命周期
  3. 掌握 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 和缓存参数,以及其他类似的任务。
Servlet 包

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的属性列表。

属性名类型描述nameString指定Servlet 的 name 属性,等价于 。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。valueString[]该属性等价于 urlPatterns 属性。两个属性不能同时使用。urlPatternsString[]指定一组 Servlet 的 URL 匹配模式。等价于 标签。loadOnStartupint指定 Servlet 的加载顺序,等价于 标签。initParamsWebInitParam[]指定一组 Servlet 初始化参数,等价于 标签。asyncSupportedboolean声明 Servlet 是否支持异步 *** 作模式,等价于 标签。descriptionString该 Servlet 的描述信息,等价于 标签。displayNameString该 Servlet 的显示名,通常配合工具使用,等价于 标签。

从上表可见,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()方法

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执行过程

  1. 同一个servlet是单例多线程的,只存在一个servlet对象,由多个线程异步执行该对象的方法。因此servlet的类属性是“线程不安全的”,对象属性是线程安全的,因为每个线程都会拷贝一份对象属性。
  2. Servelet容器维护了一个线程(等待执行代码的一组线程【Worker Thread】)池来处理各种请求。
  3. Servlet容器使用一个调度线程【Dispatcher Thread】来管理【Worker Thread】。
  4. 当tomcat启动时,按照在web.xml文件元素中配置的序号的顺序,容器会自动创建每个不同的Servlet对象,并执行。
  5. 当接受到第一个请求时,容器创建非Servlet的servlet对象,并执行。
  6. 执行过程按照Servlet生命周期执行
Servlet体系结构 ServletRequest接口
ServletRequest` -> `HttpServletRequest
相关方法 方 法说 明getAttributeNames()返回当前请求的所有属性的名字集合getAttribute(String name)返回name指定的属性值getcookies()返回客户端发送的cookiegetsession()返回和客户端相关的session,如果没有给客户端分配session,则返回nullgetsession(boolean create)返回和客户端相关的session,如果没有给客户端分配session,则创建一个session并返回getParameter(String name)获取请求中的参数,该参数是由name指定的getParameterValues(String name)返回请求中的参数值,该参数值是由name指定的getCharacterEncoding()返回请求的字符编码方式getContentLength()返回请求体的有效长度getInputStream()获取请求的输入流中的数据getMethod()获取发送请求的方式,如get、postgetParameterNames()获取请求中所有参数的名字getProtocol()获取请求所使用的协议名称getReader()获取请求体的数据流getRemoteAddr()获取客户端的IP地址getRemoteHost()获取客户端的名字getServerName()返回接受请求的服务器的名字getServerPath()获取请求的文件的路径 获得客户端信息
  • 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); Enumeration e = 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文件



	
		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 = "" 
            	+ "填写的编号:{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里面的内容到客户端浏览器显示
	}

}
Request请求转发

请求转发:指一个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;
		Collection 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接口
ServletResponse` -> `HttpServletResponse
相关方法 方 法说 明addcookie(cookie cookie)将指定的cookie加入到当前的响应中addHeader(String name,String value)将指定的名字和值加入到响应的头信息中containsHeader(String name)返回一个布尔值,判断响应的头部是否被设置encodeURL(String url)编码指定的URLsendError(int sc)使用指定状态码发送一个错误到客户端sendRedirect(String location)发送一个临时的响应到客户端setDateHeader(String name,long date)将给出的名字和日期设置响应的头部setHeader(String name,String value)将给出的名字和值设置响应的头部setStatus(int sc)给当前响应设置状态码setContentType(String ContentType)设置响应的MIME类型

常用的方法和 *** 作说明:

常用的方法

  • 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头。
Response 跨域

跨域是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对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配置一些初始化参数。

当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");
		// 获得配置文档中标签下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);
		}
	}
}
ServletContext对象

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文件中使用标签,正如在该文件中为Servlet配置参数而使用标签一样。

这种配置标签的好处在于属于全局性的配置,而每个Servlet的配置参数仅局限于在Servlet的范围内。

举个例子,对于整个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配置


  Servlet
  
    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
  

使用 Listener
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();

		Map depts = new HashMap();

		depts.put(1, "张三");
		depts.put(2, "132456");

		// 将所取到的值存放到一个属性键值对中
		sct.setAttribute("dept", depts);

	}
}
Servlet Filter 过滤器

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过滤器占用的资源。
案例:统计IP
web.xml


	Servlet
	
		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();
		Map ipCount = 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();
		Map ipCount = (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("");
	}

}

结果

@WebFilter

Servlet 3.0 之后可以使用 @WebFilter 来配置Filter

@WebFilter 的属性 属性名类型描述filterNameString指定过滤器的 name 属性,等价于 valueString[]该属性等价于 urlPatterns 属性。但是两者不应该同时使用。urlPatternsString[]指定一组过滤器的 URL 匹配模式。等价于 标签。servletNamesString[]指定过滤器将应用于哪些 Servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 的取值。dispatcherTypesDispatcherType指定过滤器的转发模式。具体取值包括: ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。initParamsWebInitParam[]指定一组过滤器初始化参数,等价于 标签。asyncSupportedboolean声明过滤器是否支持异步 *** 作模式,等价于 标签。descriptionString该过滤器的描述信息,等价于 标签。displayNameString该过滤器的显示名,通常配合工具使用,等价于 标签。

之前控制多个filter的执行顺序是通过web.xml中控制filter的位置来控制的。

放在上面的会比放在下面的先执行,如下“用户登录检查过滤器”会比“接口日志过滤器”先执行。

但是当我们使用@WebFilter注解的时候发现注解里面没有提供可以控制执行顺序的参数。

通过实践发现如果想要控制filer的执行顺序可以通过控制filter的文件名来控制

比如:

UserLoginFilter.java 和 ApiLog.java 这两个文件里面分别是“用户登录检查过滤器”和“接口日志过滤器”,因为这两个文件的首字母A排U之前,导致每次执行的时候都是先执行“接口日志过滤器”再执行“用户登录检查过滤器”,所以我们现在修改两个文件的名称分别为

  • Filter0_UserLogin.java
  • Filter1_ApiLog.java

这样就能先执行“用户登录检查过滤器”再执行“接口日志过滤器”。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5695388.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存