监听器&拦截器&过滤器

1、监听器

详见《Tomcat与Java Web开发技术详解》4.4.2
在Servlet API中有一个ServletContextListener接口,它能够监听ServletContext对象的生命周期,实际上就是监听Web应用的生命周期。

当Servlet容器启动或终止Web应用时,会触发ServletContextEvent事件,该事件由ServletContextListener来处理。在ServletContextListener接口中定义了处理ServletContextEvent事件的两个方法。

  • contextInitialized(ServletContextEvent sce):当Servlet容器启动Web应用时调用该方法。在调用完该方法之后,容器再对Filter初始化,并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
  • contextDestroyed(ServletContextEvent sce):当Servlet容器终止Web应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet和Filiter过滤器。

应用举例:

记录当Web应用启动后,网页被客户端访问的次数。

  • 在应用启动时,从文件中读取计数器的数值,并把表示计数器的Counter对象存到Web应用范围内。存放计数器的文件的路径为helloapp/count/count.txt。
  • 在应用终止时把Web应用范围内的计数器数值保存在count.txt文件中。
public class MyServletContextListener implements ServletContextListener {
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("application is initialized.");
		ServletContext context = sce.getServletContext();
		try {
		BufferedReader reader = new BufferedReader(new InputStreamReader(context.getResourceAsStream("/count/count.txt")));
		int count = Integer.parseInt(reader.readLine());
		reader.close();

		Counter counter = new Counter(counter);
		context.setAttribute("counter",counter);
		} catch(IOException e) {
			e.printStackTrace();
		}
	}

	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("application is destyoyed.");
		ServletContext context = sce.getServletContext();
		Counter counter = (Counter)context.getAttribute("counter");
		if (counter != null) {
			try {
				String filepath = context.getRealPath("/count");
				filepath = filepath + "/count.txt";
				PrintWriter pw = new PrintWriter(filepath);
				pw.println(counter.getCounter());
				pw.close();
			} catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
}

注册:

用户自定义的MyServletContextListener监听器只有先向Servlet容器注册,Servlet容器在启动或终止Web应用时,才会调用该监听器的相关方法。在web.xml中,<listener>元素用于向容器注册监听器。

<listener>
  <listener-class>mypack.MyServletContextListener<listener-class/>
</listener>

2、拦截器

详见《JavaEE的颠覆者 SpringBoot实战》4.4.2
拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理,类似于Servlet的Filter。

可以让普通的Bean实现HandlerInterceptor接口或继承HandlerInterceptorAdapter类来实现自定义拦截器。

应用举例:

计算每一次请求的处理时间

public class DemoInterceptor extends HandlerInterceptorAdapter {
	
	@Override
	public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {
		Long startTime = System.currentTimeMillis();
		request.setSttribute("startTime",startTime);
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {
		Long startTime = (Long) request.getAttribute("startTime");
		request.removeAttribute("startTime");
		Long endTime = System.currentTimeMillis();
		System.out.println("本次请求处理时间为:"+new Long(endTime-startTime)+"ms");
		request.setSttribute("handlingTime",endTime-startTime);
	}
}

注册:

@Configuration
@EnableWebMvc
@ComponentScan("com.wisely.highlight")
public class MyMvcConfig extends WebMvcConfigurerAdapter {
	@Bean
	public DemoOnterceptor demoOnterceptor() {
		return new DemoInterceptor();
	}
	
	@Overrice
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(demoOnterceptor());
	}
}

3、过滤器

详见《Tomcat与Java Web开发技术详解》第20章
在一个Web应用中,每个Web组件都用于响应特定的客户请求。在这些Web组件响应客户请求的过程中,可能都会完成一些相同的操作,如检测客户IP地址是否位于预定义的拒绝IP地址范围内。
为了避免重复编码,各个Web组件中的相同操作可以放到同一个过滤器中来完成。

  • 过滤器能够在Web组件被调用前检查ServletRequest对象,修改请求头和请求正文的内容,或对请求进行预处理
  • 过滤器能够在Web组件被调用之后检查ServletResponse对象,修改响应头和响应正文。

过滤器负责过滤的Web组件可以是Servlet、JSP或HTML文件。其过滤过程如图:
在这里插入图片描述
过滤器具有以下特点:

  • 可以检查ServletRequest和ServletResponse对象,并利用ServletRequestWrapper和ServletResponseWrapper包装类来修改ServletRequest和ServletResponse对象
  • 可以在web.xml中为过滤器映射特定的URL。当客户请求访问此URL时,Servlet容器就会先触发过滤器工作。
  • 所有实现Java Servlet 2.3规范及其以上版本的Servlet容器都支持过滤器。
  • 多个过滤器可以被串联在一起,协同为Web组件过滤请求对象和响应对象。

过滤器创建:

所有自定义实现的过滤器类都必须实现javax.servlet.Filter接口,该接口中含有以下3个过滤器类必须实现的方法:

  • init(FilterConfig config) : 过滤器的初始化方法。在Web应用启动时,Servlet容器先加载过滤器类,创建包含了过滤器配置信息的FilterConfig对象,然后创建Filter对象,接着调用Filter对象的 init(FilterConfig config)方法,在该方法中可通过config参数读取web.xml文件中为过滤器配置的初始化参数。
  • doFilter(ServletRequest req,ServletResponse res,FilterChain chain):实际的过来操作。当客户请求访问的URL与为过滤器映射的URL匹配时,Servlet容器将先调用过滤器的 doFilter()方法。FilterChain参数用于访问后续过滤器或者Web组件。
  • destroy():Servlet容器在销毁过滤器对象前调用该方法,在该方法中可以释放过滤器占用的资源。

应用举例:

NoteFilter过滤器为NoteServlet(表示留言簿)提供以下过滤功能:

  • 判断客户IP地址是否在预定义的拒绝IP地址范围内,若在,直接返回拒绝信息
  • 判断username请求参数表示的姓名是否位于预定义的黑名单中,若在,直接返回拒绝信息
  • 将NoteServlet响应客户请求所花的时间写入日志
public class NoteFilter implements Filter {
	private FilterConfig config = null;
	private String blackList = null;
	private String ipblock = null;

	public void init(FilterConfig config) throws ServletException {
		System.out.println("NoteFilter:init()");
		this.config = config;

		ipblock = config.getInitParameter("ipblock");
		blackList = config.getInitParameter("blackList");
	}

	public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,ServletException {
		System.out.println("NoteFilter:doFilter()");
		if (!checkRemoteIP(request,response)) return;
		if (!checkUsername(request,response)) return;

		long before = System.currentTimeMillis();
		config.getServletContext().log("NoteFilter:before call chain.doFilter()");
		chain.doFilter(request,response);
		config.getServletContext().log("NoteFilter:after call chain.doFilter()");

		String name = "";
		if(request instanceof HttpServletRequest) {
			name = ((HttpServletRequest)request).getRequestURI();
		}
		config.getServletContext().log("NoteFilter:"+name+":"+(after-before)+"ms");
	}
	
	private boolean checkRemote(ServletRequest request,ServletResponse response) throws IOException,ServletException{
		String addr = request.getRemoteAddr();
		if (addr.indexOf(ipblock)==0) {
			response.setContentType("text/html;charset=GB2312");
			PrintWriter out = response.getWriter();
			out.println("<h1>对不起,服务器无法为你提供服务。</h1>");
			out.flush();
			return false;
		} else {
			return true;
		}
	}
	
	private boolean checkUsername(ServletRequest request,ServletResponse response) throws IOException,ServletException{
		String username = ((HttpServletRequest)request).getParameter("username");
		if (username != null) 
			username = new String(username.getBytes("ISO-8859-1"),"GB2312");
		if (username != null && username.indexOf(blackList)!=-1) {
			response.setContentType("text/html;charset=GB2312");
			PrintWriter out = response.getWriter();
			out.println("<h1>对不起,"+username+",你没有权限留言</h1>");
			out.flush();
			return false;
		} else {
			return true;
		}
	}

	public void destroy() {
		System.out.println("NoteFilter:destroy()");
		config = null;
	}
}

工作流程:
在这里插入图片描述

发布

在发布过滤器时,必须在web.xml文件中加入<filter>元素和<filter-mapping>元素。

<filter>
	<filter-name>NoteFilter</filter-name>
	<filter-class>mypack.NoteFilter</filter-class>

	<init-param>
		<param-name>ipblock</param-name>
		<param-value>221.45<param-value>
	</init-param>
	
	<init-param>
		<param-name>blacklist</param-name>
		<param-value>捣蛋鬼<param-value>
	</init-param>
</filter>

<filter-mapping>
	<filter-name>NoteFilter</filter-name>
	<url-pattern>/note</url-pattern>
</filter-mapping>

注意:
1、如果希望过滤器能为所有的URL过滤,可以把<url-pattern>的值设为“/*”
2、在web.xml文件中,必须先配置所有过滤器,再配置Servlet

串联过滤器!!

详见《Tomcat与Java Web开发技术详解》20.4节!!

发布了34 篇原创文章 · 获赞 1 · 访问量 1817

猜你喜欢

转载自blog.csdn.net/weixin_44761910/article/details/104927375
今日推荐