文章目录
来源于how2j
Filter过滤器
概念
- WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
- Filter就像一个一个哨卡,用户的请求需要经过Filter
并且可以有多个过滤器
Filter是如何实现拦截的?
- Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
调用目标资源之前,让一段代码执行。
是否调用目标资源(即是否让用户访问web资源)。
调用目标资源之后,让一段代码执行。
- web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对 象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方 法,即web资源就会被访问,否则web资源不会被访问。
写一个Filter
- 开发一个简单的FirstFilter,用来打印用户访问ip地址和访问的页面:
- 代码:
package filter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String ip = request.getRemoteAddr();
String url = request.getRequestURL().toString();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = new Date();
String date = sdf.format(d);
System.out.printf("%s %s 访问了 %s%n", date, ip, url);
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
- 解释:
HttpServletRequest request = (HttpServletRequest) req;
doFilter()方法中的req参数的类型是ServletRequest,需要转换为HttpServletRequest类型方便调用某些方法;chain.doFilter(request, response);
:过滤器放行,表示继续运行下一个过滤器,或者最终访问的某个servlet,jsp,html等等
- 效果:
- web.xml配置:
<filter>
<filter-name>FirstFilter</filter-name>
<filter-class>filter.FirstFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>FirstFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 解释:
<url-pattern>/*</url-pattern>
表示所有的访问都会过滤- 如果配置成
<url-pattern>*.jsp</url-pattern>
;就表示只过滤jsp
- init()方法 :
- 与Servlet需要配置自启动才会随着tomcat的启动而执行init()方法不一样。Filter一定会随着tomcat的启动自启动。
- FIlter启动失败 :Filter是web应用非常重要的一个环节,如果Filter启动失败,或者本身有编译错误,不仅这个Filter不能使用,整个web应用会启动失败,导致用户无法访问页面
在启动tomcat过程中,也会看到这样的字样:严重: Context [] startup failed due to previous errors
Filter配置登录验证
- 我们可以通过在HeroListServlet中增加对session的判断代码来做到登陆验证。
- 但是按照这样的做法,所有的Servlet都要加上一样的代码,就会显得比较累赘。
- 与通过Filter处理中文问题一样,也可以通过Filter一次性解决所有的登陆验证问题
- 代码:
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AuthFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String uri = request.getRequestURI();
if (uri.endsWith("login.html") || uri.endsWith("login")) {
chain.doFilter(request, response);
return;
}
String userName = (String) request.getSession().getAttribute("userName");
if (null == userName) {
response.sendRedirect("login.html");
return;
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
- 解释:
- 首先判断是否是访问的login.html和loginHero,因为这两个页面就是在还没有登陆之前就需要访问的
- 代码:
tring uri = request.getRequestURI(); if (uri.endsWith("login.html") || uri.endsWith("login")) { chain.doFilter(request, response); return; }
- 从Session中获取userName,如果没有,就表示不曾登陆过,跳转到登陆页面。
- 代码:
String userName = (String) request.getSession().getAttribute("userName"); if (null == userName) { response.sendRedirect("login.html"); return; }
- web.xml配置:
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>filter.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Listener监听器
概念
- 所谓的监听器是指对整个WEB环境的监听,当被监视的对象发生改变时,立即调用相应的方法进行处理
- 监听器最常见的应用场景:
- Java SE GUI编程,如之前学习java swing时配置各种监听器
- Android 手机开发编程
- JAVA WEB监听器:在JavaWeb的Listener也是这么个原理,但是它监听的内容不同,它可以监听Application、Session、Request对象,当这些对象发生变化就会调用对应的监听方法。
监听Context
对Context的监听分生命周期的监听,和Context上Attribute变化的监听两种。
- 生命周期监听:ContextListener
- 代码:
package listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ContextListener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("web 应用销毁 ");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("web 应用初始化 ");
}
}
- 解释:
- ContextListener 实现接口ServletContextListener
有两个方法: public void contextDestroyed(ServletContextEvent arg0)
对应当前web应用的销毁public void contextInitialized(ServletContextEvent arg0)
对应当前web应用的初始化
- ContextListener 实现接口ServletContextListener
- 配置web.xml
<listener>
<listener-class>listener.ContextListener</listener-class>
</listener>
- 效果:web初始化监听
- servlet发生变化:
- 其他类发生变化:也监听到并做出反应
- 不过我的修改是:
- 说明销毁调用的是之前的监听方法
监听ContextAttribute
- ContextAttributeListener 实现接口ServletContextAttributeListener,
分别提供如下方法:
//监听属性的增加
public void attributeAdded(ServletContextAttributeEvent e)
//监听属性的移除
public void attributeRemoved(ServletContextAttributeEvent e)
//监听属性的替换
public void attributeReplaced(ServletContextAttributeEvent e)
- 代码:
package listener;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
public class ContextAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent e) {
System.out.println("增加属性 ");
System.out.println("属性是" + e.getName());
System.out.println("值是" + e.getValue());
}
@Override
public void attributeRemoved(ServletContextAttributeEvent e) {
// TODO Auto-generated method stub
System.out.println("移除属性 ");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent e) {
// TODO Auto-generated method stub
System.out.println("替换属性");
}
}
- web.xml:
<listener>
<listener-class>listener.ContextAttributeListener</listener-class>
</listener>
- 用JSPtest:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
application.setAttribute("test", 1);
application.setAttribute("test", 2);
application.removeAttribute("test");
%>
- 效果:
监听Request
对Request的监听分生命周期的监听,和Request上Attribute变化的监听两部分。
- RequestListner:
//当新创建了一个Request的时候触发,只要访问了服务端的资源,
就会创建一个Request,无论是jsp,servlet还是html
requestInitialized()
//当本次请求结束的时候触发
requestDestroyed()
//当有新增属性时触发
attributeAdded()
//当有替换属性时触发
attributeReplaced()
//当有移除属性时触发
attributeRemoved()
- 代码:
package listener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class RequestListener implements ServletRequestListener, ServletRequestAttributeListener {
@Override
public void requestDestroyed(ServletRequestEvent arg0) {
System.out.println("销毁了一个Request ");
}
@Override
public void requestInitialized(ServletRequestEvent arg0) {
System.out.println("创建了一个Request ");
}
@Override
public void attributeAdded(ServletRequestAttributeEvent e) {
System.out.println("request 增加属性 ");
System.out.println("属性是" + e.getName());
System.out.println("值是" + e.getValue());
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent arg0) {
System.out.println("request 移除属性 ");
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent arg0) {
System.out.println("request 替换属性 ");
}
}
- web.xml
<listener>
<listener-class>listener.RequestListener</listener-class>
</listener>
- 效果:
利用Session监听器统计在线人数
HTTP协议是短链接的,所以无法在服务端根据建立了多少连接来统计当前有多少人在线。 不过可以通过统计session有多少来估计在线人数。
一旦一个用户访问服务器,就会创建一个session. 如果该用户持续访问,那么该session会持续有效。
如果经历了30分钟,该用户也没有做任何操作,就表示该用户“下线” 了,其对应的session也会被销毁。
所以可以通过统计有多少session被保留来估计当前在线人数。
编写 OnlineNumberListener:
OnlineNumberListener 实现接口HttpSessionListener
当创建一个session的时候,就把数字+1,并且放在application里。
这就是jsp里的application:
ServletContext application = e.getSession().getServletContext();
注: 第一次从application里取数据的时候,是空的,要设置为0。当销毁一个session的时候,把这个数字-1
关于applicationContext什么的?
代码:
package listener;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class OnlineNumberListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent e) {
ServletContext application = e.getSession().getServletContext();
Integer online_number = (Integer) application.getAttribute("online_number");
if (null == online_number)
online_number = 0;
online_number++;
application.setAttribute("online_number", online_number);
System.out.println("新增一位在线用户");
}
@Override
public void sessionDestroyed(HttpSessionEvent e) {
ServletContext application = e.getSession().getServletContext();
Integer online_number = (Integer) application.getAttribute("online_number");
if(null==online_number){
online_number = 0;
}
else
online_number--;
application.setAttribute("online_number", online_number);
System.out.println("一位用户离线");
}
}
- 配置web.xml
<listener>
<listener-class>listener.OnlineNumberListener</listener-class>
</listener>
- checkOnlineNumber.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
当前 在线人数 : ${online_number}
- 效果:
版权声明:本文为weixin_38719347原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。