过滤器
介绍
Filter 即为过滤,用于在 Servlet 之外对 Request 或者 Response 进行修改。它主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行后处理。使用 Filter的完整流程: Filter 对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再 对服务器响应进行后处理。在一个 web 应用中,可以开发编写多个Filter,这些 Filter 组合 起来称之为一个 Filter 链。
实现
-
新建普通Java类
-
实现javax.servlet.Filter接口
-
实现对应的方法
-
配置web.xml
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * 实现过滤器 * 1、新建普通Java类 * 2、实现javax.servlet.Filter接口 * 3、实现对应的方法 * 4、配置web.xml */ public class Filter01 implements Filter { /** * 初始化 */ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("Filter01 init..."); } /** * 过滤/拦截 */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Filter01 filter..."); // 拦截请求 // 放行资源 chain.doFilter(request, response); // 过滤响应数据 System.out.println("Filter01 过滤响应..."); } @Override public void destroy() { System.out.println("Filter01 destroy..."); } }
<filter> <filter-name>Filter01</filter-name> <filter-class>com.demo.filter.Filter01</filter-class> </filter> <filter-mapping> <filter-name>Filter01</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
执行顺序
- 通过观察 web.xml 中的配置和各个 filter 的执行顺序,找出 filter 执行先后的依据。
- 根据之前观察 Servlet 生命周期的的方式,观察一下过滤器的生命周期。
过滤器实例
用户非法登录拦截
import java.io.IOException;
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;
/**
* 非法访问拦截
* 拦截的资源:
* 拦截所有资源 /*
*
* 得到访问的路径:
* request.getRequestURI(); 返回的是部分资源路径,从端口后面,到"?"前面
*
* 放行的资源:(无需登录即可访问的资源)
* 1、放行指定页面 (无需登录即可访问的页面;例如:登录页面login.html、注册页面register.html等)
* 2、放行静态资源 (无需登录即可访问的资源,放置在statics目录下的资源;例如:css、js、images、前端插件等)
* 3、放行执行操作 (无需登录即可执行的操作;例如:登录操作、注册操作等)
* 4、判断是否是登录状态 (session作用域中的数据不为空)
*/
public class LoginAccessFilter implements Filter {
public LoginAccessFilter() { }
/**
* 处理拦截
*/
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException {
// 基于HTPP
HttpServletRequest request= (HttpServletRequest) arg0;
HttpServletResponse response= (HttpServletResponse) arg1;
// 获取请求的路径
String path = request.getRequestURI();
// 1、放行指定页面 (无需登录即可访问的页面;例如:登录页面login.html、注册页面register.html等)
if (path.contains("/login.html")) {
chain.doFilter(request, response);
return;
}
// 2、放行静态资源(无需登录即可访问的资源,放置在statics目录下的资源;例如:css、js、images、前端插件等)
if (path.contains("/statics")) {
chain.doFilter(request, response);
return;
}
// 3、放行执行操作(无需登录即可执行的操作;例如:登录操作、注册操作等)
if (path.contains("/loginServlet")) {
chain.doFilter(request, response);
return;
}
// 4、判断是否是登录状态(session作用域中的数据不为空)
// 获取session作用域中的值
String uname = (String) request.getSession().getAttribute("user");
if (uname != null && !"".equals(uname)) {
chain.doFilter(request, response);
return;
}
// 拦截跳转到登录页面
response.sendRedirect("login.html");
}
public void init(FilterConfig fConfig) throws ServletException { }
public void destroy() { }
}
请求乱码处理
-
出现乱码的情况
Tomcat版本 请求方式 是否乱码 解决方法 Tomcat8及以上版本 POST 乱码 request.setCharacterEncoding(“UTF-8”); Tomcat8及以上版本 GET 不乱码 —— Tomcat7及以下版本 POST 乱码 request.setCharacterEncoding(“UTF-8”); Tomcat7及以下版本 GET 乱码 解决方法new String(request.getParameter(“参数名”).getBytes(“ISO-8859-1”),“UTF-8”); -
解决方案:
- POST请求:
- 无论Tomcat7及以下版本还是Tomcat8及以上版本都会乱码
- request.setCharacterEncoding(“UTF-8”);(只针对于POST请求,GET请求不受影响)
- GET请求:(如果字符串本身不乱码,处理之后反而会乱码)
- 判断Tomcat的版本是否在7或以下
- 判断请求方式是否为GET请求
- 处理乱码:
- 乱码原因:request.getParameter(“参数名”)
- 具体解决方案:重写getParameter()方法(需要继承包装类HttpServletRequestWapper)
- 定义内部类
- 继承包装类HttpServletRequestWapper
- 需要实现带参构造
- 重写getParameter()方法,处理乱码问题
- POST请求:
import java.io.IOException;
import java.io.UnsupportedEncodingException;
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.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
/**
* 请求乱码处理
*/
public class AEncodingFilter implements Filter {
public AEncodingFilter() { }
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException {
// 基于HTPP
HttpServletRequest request = (HttpServletRequest) arg0;
HttpServletResponse response = (HttpServletResponse) arg1;
/**
* POST请求乱码
* 无论Tomcat7及以下版本还是Tomcat8及以上版本都会乱码
* 只针对于POSTQ请求无效,GET请求不受影响
*/
request.setCharacterEncoding("UTF-8");
/**
* GET请求乱码问题 (如果字符串本身不乱码,处理之后反而会乱码)
* 1、请求方式为GET请求
* 2、Tomcat的版本在7或者7以下
* 3、处理乱码 乱码原因: request.getParameter("参数名")
* 具体解决方案: 重写getParameter()方法
* (重写Parameter()方法需要继承包装类HttpServletRequestWapper)
* 1、定义内部类
* 2、继承包装类HttpServletRequestWapper
* 3、需要实现带参构造
* 4、重写getParameter()方法,处理乱码问题
*/
// 获取请求类型 (GET/POST)
String method = request.getMethod();
// 判断是否是GET请求
if ("GET".equalsIgnoreCase(method)) {
// 得到服务器的版本信息
String serverInfo = request.getServletContext().getServerInfo();
// 得到具体的版本号
String version = serverInfo.substring(serverInfo.indexOf("/") + 1, serverInfo.indexOf("."));
// 判断是否是Tomcat7及以下版本
if (Integer.parseInt(version) < 8) {
// 处理乱码
HttpServletRequest myRequest = new MyWapper(request);
chain.doFilter(myRequest, response);
return;
}
}
chain.doFilter(request, response);
}
/**
* 重写getParameter()方法 (需要继承包装类HttpServletRequestWapper)
* 1、定义内部类
* 2、继承包装类HttpServletRequestWapper
* 3、需要实现带参构造
* 4、重写getParameter()方法,处理乱码问题
*/
class MyWapper extends HttpServletRequestWrapper {
// 定义全局变量,提升作用域
HttpServletRequest request;
public MyWapper(HttpServletRequest request) {
super(request);
// 将构造器中的request对象赋值给成员变量,给重写的getParameter()方法使用
this.request = request;
}
@Override
public String getParameter(String name) {
// 获取请求的参数值
String value = request.getParameter(name);
try {
if (value != null && !"".equals(value)) {
value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return value;
}
}
public void init(FilterConfig fConfig) throws ServletException { }
public void destroy() { }
}
监听器
介绍
web 监听器 Listener 是一种 Servlet 中的特殊的类,它们能帮助开发者监听 web 中的特定事件, 比如 ServletContext,HttpSession,ServletRequest 的创建和销毁;变量的创建、销毁和修改等。 可以在某些动作前后增加处理,实现监控,例如可以用来统计在线人数等。
实现
-
监听器有三类 8 种:
- 监听生命周期:
- ServletRequestListener
- HttpSessionListener
- ServletContextListener
- 监听值的变化:
- ServletRequestAttributeListener
- HttpSessionAttributeListener
- ServletContextAttributeListener
- 针对 session 中的对象:
- 监听 session 中的 java 对象(javaBean) 是 javaBean 直接实现监听器的接口。
- 监听生命周期:
-
实现步骤:
- 创建普通的Java类
- 实现监听的接口
- 实现监听的方法
- 配置web.xml
import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * 监听器的实现 * 1、创建普通的Java类 * 2、实现监听的监听 * 3、实现监听的方法 * 4、配置web.xml */ public class Listener01 implements HttpSessionAttributeListener,HttpSessionListener { /** * Session的创建 */ @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("Session的创建..."); } /** * session的销毁 */ @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("Session的销毁..."); } /** * 创建session域对象 */ @Override public void attributeAdded(HttpSessionBindingEvent se) { System.out.println("Session域对象的创建..."); } /** * 删除session域对象 */ @Override public void attributeRemoved(HttpSessionBindingEvent se) { System.out.println("Session域对象的删除..."); } /** * 修改Session域对象 */ @Override public void attributeReplaced(HttpSessionBindingEvent se) { System.out.println("Session域对象的修改..."); } }
<listener> <listener-class>com.demo.listener.Listener01</listener-class> </listener>
监听器实例
-
统计在线人数
import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * 监听在线人数 * 当有用户访问时,创建session对象,在线人数加1 * 当有用户退出时,销毁session对象,在线人数减1 * */ public class OnloineListener implements HttpSessionListener { // 默认在线人数 private Integer onlineNumber = 0; public OnloineListener() { } /** * 当有用户访问时,创建session对象,在线人数加1 */ public void sessionCreated(HttpSessionEvent se) { // 在线人数加1 onlineNumber++; // 将在线人数存到sessio作用域中 // se.getSession().setAttribute("onlineNumber", onlineNumber); // 由于每个用户的session对象都是独立的,所有每个用户只能看到本来的在线人数,不能看到更新后的在线人数,所以需要扩大域范围 se.getSession().getServletContext().setAttribute("onlineNumber", onlineNumber); } /** * 当有用户退出时,销毁session对象,在线人数减1 */ public void sessionDestroyed(HttpSessionEvent se) { // 在线人数减1 onlineNumber--; // 将在线人数存到sessio作用域中 // se.getSession().setAttribute("onlineNumber", onlineNumber); se.getSession().getServletContext().setAttribute("onlineNumber", onlineNumber); } }
<listener> <listener-class>com.demo.listener.OnloineListener</listener-class> </listener>