一. Filter过滤器
1.1 基本概念
·Filter本意为”过滤“的含义,是JavaWeb的三大组件之一,三大组件为:Servlet、Filter、 Listener
·过滤器是向 Web 应用程序的请求和响应处理添加功能的 Web 服务组件。
·过滤器相当于浏览器与Web资源之间的一道过滤网,在访问资源之前通过一系列的过滤器对请求进行修改、判断以及拦截等,也可以对响应进行修改、判断以及拦截等
·可以理解为:
它主要用于对HttpServletRequest用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理
1.2 实现原理
1) 在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。
2) 在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
3) Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源进行拦截后,Web服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,doFilter方法中有一个filterChain对象, 用于继续传递给下一个filter,
在传递之前我们可以定义过滤请求的功能, 在传递之后, 我们可以定义过滤响应的功能
1.3 如何使用
·采用三步走策略使用filter
1) 开发后台资源 静态资源(html,css … …)或者动态资源(Servlet,Jsp)
2) 开发Filter, 自定义一个类实现javax.servlet.Filter接口
3) 在web.xml中配置Filter拦截哪些资源
servlet
public class TestFilterServlet extends HttpServlet { __ @Override protected void service(HttpServletRequest req, HttpServletResponse resp_) _throws ServletException, IOException { __ System.out.println(“TestFilterServlet 中的service方法执行”); resp.getWriter().println(“Hello!”); } __} |
---|
filter
public class MyFilter1 implements Filter { __ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain_) _throws IOException, ServletException { __ // 过滤请求 _System.**_out.println(“MyFiter1在请求到达servlet之前的代码处理”); _// 请求向后传递 _filterChain.doFilter(servletRequest,servletResponse); _// 过滤响应 _System.out.println(“myFilter1在响应回到浏览器之前的代码处理”); } __}** |
---|
web.xml
<_servlet> _ <_servlet-name>_testFilterServlet</_servlet-name> _ <_servlet-class>_com.bjsxt.servlet.TestFilterServlet</_servlet-class> <_servlet-mapping_> _ <_servlet-name>_testFilterServlet</_servlet-name> _ <_url-pattern>_/testFilterServlet</_url-pattern> _ </_servlet-mapping>_ <_filter> _ <_filter-name>_myFilter1</_filter-name> _ <_filter-class>_com.bjsxt.filter.MyFilter1</_filter-class> _</_filter>_ <_filter-mapping> _ <_filter-name>_myFilter1</_filter-name> _ <_url-pattern>_/*</_url-pattern> _ </_filter-mapping>_ |
---|
总结
1) 在doFilter方法中, 我们可以通过filterChain.doFilter()方法控制请求是否继续向后传递
2) 在doFilter方法中, 我们同样可以使用HttpRequest处理请求, 使用HttpResponse对象作出响应
1.4 过滤器的生命周期
同servlet对象一样,Filter对象的创建也是交给web服务器完成的,在web服务器创建和使用及最后销毁filter时,会调用filter对应的方法
初始化方法:
public void init(FilterConfig filterConfig);
和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
拦截请求方法
public void doFilter
这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。
销毁方法:
public void destroy();
Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源
代码演示
public class MyFilter1 implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println(“MyFilter1初始化方法”); } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { _System.**_out.println(“MyFiter1在请求到达servlet之前的代码处理”); _filterChain.doFilter(servletRequest,servletResponse); _System.out.println(“MyFilter1在响应回到浏览器之前的代码处理”); } public void destroy() { System.out.println(“MyFilter1销毁方法”**); } } |
---|
1.5 过滤器链的使用
1.5.1 基本概念
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链
1.5.2 使用过滤器链的好处
使用过滤器链的好处是我们可以将不同的过滤功能分散到多个过滤器中,分工明确,避免一个过滤器做太多的业务处理,降低了代码的耦合度,这体现了单一职责的设计原则,应用了责任链的代码设计模式
1.5.3 过滤器链执行顺序
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源
代码演示
public class MyFilter12implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println(“MyFilter2初始化方法”); } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { _System.**_out.println(“MyFiter2在请求到达servlet之前的代码处理”); _filterChain.doFilter(servletRequest,servletResponse); _System.out.println(“MyFilter2在响应回到浏览器之前的代码处理”); } public void destroy() { System.out.println(“MyFilter2销毁方法”**); } } |
---|
web.xml
<_servlet> _ <_servlet-name>_testFilterServlet</_servlet-name> _ <_servlet-class>_com.bjsxt.servlet.TestFilterServlet</_servlet-class> <_servlet-mapping_> _ <_servlet-name>_testFilterServlet</_servlet-name> _ <_url-pattern>_/testFilterServlet</_url-pattern> <_filter_> _ <_filter-name>_myFilter1</_filter-name> _ <_filter-class>_com.bjsxt.filter.MyFilter1</_filter-class> _ <_filter-mapping> _ <_filter-name>_myFilter1</_filter-name> _ <_url-pattern>_/*</_url-pattern> <_filter_> _ <_filter-name>_myFilter2</_filter-name> _ <_filter-class>_com.bjsxt.filter.MyFilter2</_filter-class> _ <_filter-mapping> _ <_filter-name>_myFilter2</_filter-name> _ <_url-pattern>_/*</_url-pattern> _ </_filter-mapping>_ |
---|
1.5.4 过滤器的初始化参数
同servlet一样, filter也可以通过web.xml进行初始化配置,在初始化是,将参数封装进入FilterConfig并在调用init方法时作为实参传入,我们可以在init方法中获取参数
web.xml
<_filter> _ <_filter-name>_myFilter1</_filter-name> _ <_filter-class>_com.bjsxt.filter.MyFilter1</_filter-class>_ <_init-param> _ <_param-name>_test</_param-name> _ <_param-value>_utf-8</_param-value> _ <_filter-mapping> _ <_filter-name>_myFilter1</_filter-name> _ <_url-pattern>_/*</_url-pattern> _ </_filter-mapping>_ |
---|
myFilter1
public class MyFilter1 implements Filter { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain_) _throws IOException, ServletException { __ // 过滤请求 _System.**_out.println(“MyFiter1在请求到达servlet之前的代码处理”); _// 请求向后传递 _filterChain.doFilter(servletRequest,servletResponse); _// 过滤响应 _System.out.println(“MyFilter1在响应回到浏览器之前的代码处理”); } __ //过滤器初始化时执行 public void init(FilterConfig filterConfig) _throws ServletException { **//获取初始化参数 String testInitParam = filterConfig.getInitParameter**(“testInitParam”); System._out.println(testInitParam); _} **//过滤器销毁时执行 public void destroy**() {} _}** |
---|
1.6 过滤器案例开发
1.6.1 案例开发之解决post乱码问题
filter可以帮助我们在请求到达servlet之前处理好请求参数乱码问题
CharacterEncodingFilter
public class CharacterEncodingFilter implements Filter { __ String charset = null; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain_) _throws IOException, ServletException { __ servletRequest.setCharacterEncoding(_charset)_; // 设置请求编码格式 servletResponse.setCharacterEncoding(_charset)_; // 设置响应编码格式 servletResponse.setContentType(“text/html;charset=”+charset); // 请求向后传递 _filterChain.doFilter**(servletRequest,servletResponse); } **//过滤器初始化时执行 @Override public void init**(FilterConfig filterConfig) throws ServletException { charset = filterConfig.getInitParameter(“charset”); System._out.println(_charset); } **//过滤器销毁时执行 @Override public void destroy**() {} }_** |
---|
web.xml
<_filter> _ <_filter-name>_characterEncodingFilter</_filter-name> _ <_filter-class>_com.bjsxt.filter.CharacterEncodingFilter</_filter-class> <_init-param_> <_param-name_>_charset</_param-name> _ <_param-value>_utf-8</_param-value> <_filter-mapping_> <_filter-name_>_characterEncodingFilter</_filter-name> _ <_url-pattern>_/*</_url-pattern> _</_filter-mapping>_ |
---|
1.6.2 案例开发之登录验证
需求:
访问queryServlet | show.jsp, 校验是否登录, 如果没有登录, 跳转到登录页面进行登录, 只有登录的用户可以访问queryServlet | show.jsp
login.jsp
<_h2>_登录页面</_h2> <_form action="loginServlet" method="post"_> <_input type="text" name="username"_><_br_> <_input type="password" name="password"_><_br_> <_input type="submit"_><_br_> <_br_> <_p style=”color: red”_><_c:if test=”${!empty msg}”>${msg}</_c:if>_ |
---|
show.jsp
<_h2>_数据展示页面</_h2> _<_h3>_欢迎 _<_c:if test=”${!empty username}”>${username}</_c:if> __<_c:if test=”${!empty list}”> _ <_c:forEach items=”${list}” var=”user”> __ ${user} <_br> _ <_a href=”logoutServlet”>_退出登录</_a>_ |
---|
loginServlet
/登录的Servlet/public class LoginServlet extends HttpServlet { __ @Override protected void service(HttpServletRequest req, HttpServletResponse resp_) _throws ServletException, IOException { //1.获取用户名, 密码 _String username = req.getParameter**(“username”); String password = req.getParameter(“password”); **//2.获取session HttpSession session = req.getSession**()**; //3.模拟数据库查询判断 **if(“zs”.equals(username) && “111”.equals(password)){ **//4.登录成功, 存储用户的标志 session.setAttribute**(“username”, username); session.setAttribute(“msg”, “”)**; //5.重定向到queryServlet 重定向需要 /项目名/资源名 resp.sendRedirect**(“/servlet_demo/queryServlet”); }else{ **//4.登录失败, 存储错误信息 session.setAttribute**(“msg”, “用户名或密码有误”)**; //5.重定向到login.jsp resp.sendRedirect**(“/servlet_demo/login.jsp”); } } }_** |
---|
queryServlet
//… |
---|
logoutServlet
//… |
---|
checkLoginFilter
public class CheckLoginFilter implements Filter { __ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain_) _throws IOException, ServletException { __ //强转, 需要HttpServletRequest中的方法 _HttpServletRequest request = **(HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) **servletResponse; //获取session HttpSession session = request.getSession**()**; //获取统一资源标识符 String uri = request.getRequestURI**()**; //包含登录页面, 登录的servlet放行 **if(uri.contains(“loginServlet”) || uri.contains(“login.jsp”)){ filterChain.doFilter(request, response); return; } **//获取session中的用户登录的标志(有存储的username, 代表用户已经登录) Object username = session.getAttribute**(“username”); if(username != null){ **//登录 放行 filterChain.doFilter**(request, response); }else { **//没登录 跳转到登录的页面 response.sendRedirect**(request.getContextPath()+“/login.jsp”); } } }_** |
---|
characterEncodingFilter
//… |
---|
web.xml
<_url-pattern>_/loginServlet</_url-pattern>_ <_url-pattern>_/queryServlet</_url-pattern>_ <_filter> _ <_filter-name>_characterEncodingFilter</_filter-name> _ <_filter-class>_com.bjsxt.filter.CharacterEncodingFilter</_filter-class> <_init-param_> <_param-name_>_charset</_param-name> _ <_param-value>_utf-8</_param-value> <_filter-mapping_> <_filter-name_>_characterEncodingFilter</_filter-name> _ <_url-pattern>_/*</_url-pattern> _</_filter-mapping>_ <_filter> _ <_filter-name>_checkLoginFilter</_filter-name> _ <_filter-class>_com.bjsxt.filter.CheckLoginFilter</_filter-class> <_filter-mapping_> _ <_filter-name>_checkLoginFilter</_filter-name> _ <_url-pattern>_/*</_url-pattern> _</_filter-mapping>_ |
---|
二. Listener监听器
2.1 基本概念
·Servlet中定义的一种特殊的组件,用来监听Servlet容器产生的事件并进行相应的处理
·容器产生的事件分类如下:
1) 生命周期相关的事件
2) 属性状态相关的事件
3) 存值状态相关的事件
2.2 基本分类
2.4 监听器详解
2.4.1 ServletRequestListener监听器
·在ServletRequest创建和关闭时都会通知ServletRequestListener监听器
·常用方法如下:
2.4.2 ServletRequestAttributeListener监听器
·添加、删除或者替换一个属性的时候,通知 ServletRequestAttributeListener监听器
·常用方法如下:
代码实现
Servlet1
@WebServlet(“/servlet1”_)_public class Servlet1 extends HttpServlet { __ @Override protected void service(HttpServletRequest req, HttpServletResponse resp_) _throws ServletException, IOException { __ System.out.println(“service方法执行了”); req.setAttribute(“msg”,“aaa”); req.setAttribute(“msg”,“bbb”); req.removeAttribute(“msg”); } __} |
---|
TestRequestListener
public class TestRequestListener implements ServletRequestListener, ServletRequestAttributeListener { __ @Override public void requestDestroyed(ServletRequestEvent servletRequestEvent) { __ /request域被销毁的方法 任何一个req域的销毁都会触发该方法 请求 响应完成一次 request就会销毁 / _System.**_out.println(“请求被销毁”); } __ @Override public void requestInitialized(ServletRequestEvent servletRequestEvent) { __ _/request域的初始化方法 任何一个req域的初始化都会触发该方法 发生一次请求 request域就会被创建一次 / _System.out.println(“请求被创建”); } __ @Override public void attributeAdded(ServletRequestAttributeEvent srae) { __ _/向任何一个request域中放入数据会触发的方法/ _System.out.println(“request域中添加了数据”); } __ @Override public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) { __ _/从任何一个request域中移除数据会触发的方法/ _System.out.println(“request域中移除了数据”); } __ @Override public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) { __ _/任何一个request域中的任何数据被修改的时候会触发该方法/ _System.out.println(“request域中修改了数据”); } __}** |
---|
web.xml
<_listener> _ <_listener-class>_com.bjsxt.listener.TestRequestListener</_listener-class> _</_listener>_ |
---|
2.4.3 HttpSessionListener监听器
·当一个HttpSession刚被创建或者失效(invalidate)的时候,将会通知HttpSessionListener监听器
·常用方法如下:
2.4.4 HttpSessionAttributeListener监听器
·HttpSession中添加、删除或者替换一个属性的时候,将会通知HttpSessionAttributeListener监听器
·常用方法如下:
代码案例
Servlet2
@WebServlet(“/servlet2”_)_public class Servlet2 extends HttpServlet { __ @Override protected void service(HttpServletRequest req, HttpServletResponse resp_) _throws ServletException, IOException { __ HttpSession session = req.getSession(); session.setAttribute(“msg”, “sesion域添加了数据”); session.setAttribute(“msg”, “sesion域修改了数据”); session.removeAttribute(“msg”); session.invalidate(); } __} |
---|
TestSessionLostener
@WebListener public class TestSessionListener implements HttpSessionListener, HttpSessionAttributeListener { __ @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { __ System.out.println(“session对象被创建”); } __ @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { __ System.out.println(“session对象被销毁”); } __ @Override public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) { __ System.out.println(“session域新增数据”); } __ @Override public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) { __ System.out.println(“session域修改数据”); } __ @Override public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) { __ System.out.println(“session域移除数据”); } __} |
---|
2.4.5 HttpSessionBindingListener监听器
·HttpSession中的对象绑定和解除绑定时,通知HttpSessionListener监听器
·常用方法如下:
代码实现
User
public class User implements Serializable, HttpSessionBindingListener { _ _private int id; private String name; private String password; private int age; //get|set|toString … @Override public void valueBound(HttpSessionBindingEvent event) { __ System.out.println(“session中绑定了User对象”); } __ @Override public void valueUnbound(HttpSessionBindingEvent event) { __ System.out.println(“session中解除绑定了User对象”); } } |
---|
Servlet3
@WebServlet(“/servlet3”_)_public class Servlet3 extends HttpServlet { __ @Override protected void service(HttpServletRequest req, HttpServletResponse resp_) _throws ServletException, IOException { __ HttpSession session = req.getSession(); User user = new User(1,“zs”,“111”,18); session.setAttribute(“user”, user); session.invalidate(); } __} |
---|
2.4.6 HttpSessionActivationListener监听器
·当有session数值的钝化和活化操作时,通知HttpSessionActivationListener监听器
·常用方法如下:
配置文件:
1) 在web目录中新建META-INF目录
2) META-INF目录中新建context.xml文件
<?_xml version=”1.0” encoding=”gbk”?> <_Context_> <_Manager className="org.apache.catalina.session.PersistentManager"_> _ <_Store className=”org.apache.catalina.session.FileStore” directory=”d:/session”/> _ |
---|
代码实现
Servlet4
@WebServlet(“/servlet4”_)_public class Servlet4 extends HttpServlet { __ @Override protected void service(HttpServletRequest req, HttpServletResponse resp_) _throws ServletException, IOException { __ HttpSession session = req.getSession(); User user = new User(1,“zs”,“111”,18); session.setAttribute(“user”, user); } __} |
---|
User
public class User implements Serializable, HttpSessionBindingListener, HttpSessionActivationListener { _ _private int id; private String name; private String password; private int age; //get|set|toString … @Override public void sessionWillPassivate(HttpSessionEvent se) { __ System.out.println(“把内存中session域中的User持久化到本地磁盘”); } __ @Override public void sessionDidActivate(HttpSessionEvent se) { __ System.out.println(“本次磁盘的User对象读取到内存中的session域”); } } |
---|
2.4.6 ServletContextListener监听器
·在ServletContext创建和关闭时都会通知ServletContextListener监听器
·常用方法如下:
2.4.7 ServletContextAttributeListener监听器
·向ServletContext添加、删除或者替换一个属性的时候,将会通知 ServletContextAttributesListener监听器
·常用方法如下:
代码实现
Servlet5
@WebServlet(“/servlet5”_)_public class Servlet5 extends HttpServlet { protected void service(HttpServletRequest req, HttpServletResponse resp_) _throws ServletException, IOException { __ ServletContext application = req.getSession().getServletContext(); application.setAttribute(“msg”, “application域添加数据”); application.setAttribute(“msg”, “application域修改数据”); application.removeAttribute(“msg”); } __} |
---|
TestServletContextListener
@WebListenerpublic class TestServletContextListener implements ServletContextAttributeListener, ServletContextListener { __ @Override public void contextInitialized(ServletContextEvent servletContextEvent) { __ System.out.println(“application域被创建”); } __ @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { __ System.out.println(“application域被销毁”); } __ @Override public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) { __ System.out.println(“application域中添加数据”); } __ @Override public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) { __ System.out.println(“application域中移除数据”); } __ @Override public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) { __ System.out.println(“application域修改数据了”); } __} |
---|
3.1 使用监听器生成请求记录日志
需求: 记录每次请求中如下的信息并存储进入日志文件
发送请求浏览器的IP
请求的资源 URI
请求发生的时间
代码实现
@WebListenerpublic class RequestLogListener implements ServletRequestListener { __ @Override public void requestInitialized(ServletRequestEvent sre) { __ System.out.println(“request 被创建”); //需要使用HttpServletRequest中的方法 _HttpServletRequest request = **(HttpServletRequest) sre.getServletRequest(); String remoteHost = request.getRemoteHost(); String uri = request.getRequestURI(); String str = new Date().toLocaleString(); System._out.println(“发送请求的远程ip为: “+remoteHost+“ 访问的资源为: “+uri+“ 访问的时间为: “+str); FileOutputStream fileOutputStream = null; PrintWriter printWriter = null; try { __ _//获取文件输出流 _fileOutputStream = new FileOutputStream(“/Users/yc/log.txt”, true); printWriter = new PrintWriter(fileOutputStream); _//写入内容 _printWriter.println(“发送请求的远程ip为: “+remoteHost+“ 访问的资源为: “+uri+“ 访问的时间为: “+str); } _catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { printWriter.close(); fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } _}** |
---|
3.2 使用监听器实现实时在线人数统计
需求:
1当任何一个账户处于登录状态时,在线统计总数+1,离线时-1
2通过session监听器实现计数,但是在线人数要保存在Application域中
代码实现
OnlineListener
@WebListenerpublic class OnlineListener implements HttpSessionListener, HttpSessionAttributeListener { __ //用户登录成功, 会在session中存储用户的信息 @Override public void attributeAdded(HttpSessionBindingEvent se) { __ HttpSession session = se.getSession(); //统计在线人数, 整个项目共享的数据 使用application _ServletContext application = session.getServletContext**(); Integer online = (Integer) application.getAttribute(“online”); System._out.println(“omline: “+online); if(online == null){ __ online = 1; }_else { online++; } application.setAttribute(“online”, online); } **//用户退出会销毁session @Override public void sessionDestroyed**(HttpSessionEvent se) { ServletContext application = se.getSession().getServletContext(); Integer online = (Integer) application.getAttribute(“online”); if (online == null){ online = 0; }else { online—; } application.setAttribute(“online”, online); } }_** |
---|
show.jsp
<_h2>_数据展示页面</_h2> _<_h3>_当前在线人数: ${online}</_h3> _<_h3>_欢迎 _<_c:if test=”${!empty username}”>${username}</_c:if> __<_c:if test=”${!empty list}”> _ <_c:forEach items=”${list}” var=”user”> __ ${user} <_br> <_br_> <_a href="logoutServlet"_>_退出登录</_a>_ |
---|