Servlet、Filter、Listener被称为JavaWeb的三大组件,Filter需要重点掌握,Listener了解即可。
过滤器,我们可以联想下生活中的净水器、空气净化器、土匪,以土匪为例,映射到Web场景如下图所示。
web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能;
过滤器的作用:
一般用于完成通用的操作,如:登录验证、统一编码处理、敏感字符的过滤等。
2 Filter过滤器快速入门快速入门步骤:
1)定义一个类,实现接口Filter;2)覆写方法;3)配置拦截路径; web.xml: 注解:【举例】:访问indes.jsp,观察FilterDemo1类中的打印信息,若不执行filterChain.doFilter,就不能访问到该资源了。
代码语言:javascript复制@WebFilter("/*") //访问所有资源之前都会执行该过滤器public class FilterDemo1 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("FilterDemo1被执行了。。。");//放行filterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {}}3 过滤器使用细节3.1 web.xml配置代码语言:javascript复制demo1cn.test.web.filter.FilterDemo1demo1/* 3.2 过滤器执行流程在index.jsp打印输出一段信息,新建demo2,观察打印先后顺序:过滤器->放行后的资源->回来执行过滤器放行代码后的代码。
代码语言:javascript复制@WebFilter("/*")public class FilterDemo2 implements Filter {public void destroy() {}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {//对request对象的请求消息增强System.out.println("filterDemo2 ...");//放行chain.doFilter(req, resp);//对response对象的响应消息增强System.out.println("filterDemo2 back...");}public void init(FilterConfig config) throws ServletException {}}3.3 过滤器生命周期方法1)init:服务器启动后会创建Filter对象,然后调用init方法,只执行一次,用于加载资源;2)doFilter:每一次请求被拦截资源时,会执行,执行多次;3)destroy:服务器关闭后,Filter对象被销毁,若服务器正常关闭,则会执行destroy方法,只执行一次,用于释放资源。代码语言:javascript复制@WebFilter("/*")public class FilterDemo3 implements Filter {//服务器关闭后,Filter对象被销毁,若服务器正常关闭,则会执行destroy方法,只执行一次,用于释放资源public void destroy() {System.out.println("destroy...");}//每一次请求被拦截资源时,会执行,执行多次public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {System.out.println("doFilter...");chain.doFilter(req, resp);}//服务器启动后会创建Filter对象,然后调用init方法,只执行一次,用于加载资源public void init(FilterConfig config) throws ServletException {System.out.println("init...");}}3.4 过滤器配置详解1)拦截路径的配置
具体的资源路径:/index.jsp 只有访问index.jsp资源时,过滤器才会被执行;拦截目录:/user/* 访问user下的所有资源时,过滤器都会被执行;后缀名拦截: *.jsp 访问所有后缀名为jsp的资源时过滤器都会被执行;拦截所有资源:/* 访问所有资源时过滤器都会被执行。新建两个Servlet用来演示,Filter代码如下:
代码语言:javascript复制//@WebFilter("/index.jsp") //1.具体的资源路径,只有访问index.jsp资源时,过滤器才会被执行;//@WebFilter("/user/*") //2.拦截目录,访问user下的所有资源时,过滤器都会被执行;@WebFilter("*.jsp") //3.后缀名拦截,访问所有后缀名为jsp的资源时过滤器都会被执行;public class FilterDemo4 implements Filter {public void destroy() {}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {System.out.println("demo4...");chain.doFilter(req, resp);}public void init(FilterConfig config) throws ServletException {}}Servlet代码如下:
代码语言:javascript复制@WebServlet("/user/findAllServlet")public class ServletDemo1 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("findAllServlet...");}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}}代码语言:javascript复制@WebServlet("/user/updateServlet")public class ServletDemo2 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("updateServlet...");}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}}2)拦截方式的配置:资源被访问的方式
注解配置:设置dispatcherTypes属性,有5种取值,分别代表不同的资源被访问方式:1、REQUEST:默认值,浏览器直接请求资源;
2、FORWARD:转发访问资源;
3、INCLUDE:包含访问资源(了解);
4、ERROR:错误跳转(了解);
5、ASYNC:异步访问资源(了解);
【举例】:访问ServletDemo2时转发到index.jsp,在FilterDemo5中注解配置不同的拦截方式,观察效果:
代码语言:javascript复制//@WebFilter(value = "/index.jsp",dispatcherTypes = DispatcherType.REQUEST) //浏览器直接请求资源时该过滤器会被执行//@WebFilter(value = "/index.jsp",dispatcherTypes = DispatcherType.FORWARD) //只有转发访问资源时该过滤器会被执行@WebFilter(value = "/index.jsp",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST}) //浏览器直接发送请求或转发访问资源时该过滤器会被执行public class FilterDemo5 implements Filter {public void destroy() {}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {System.out.println("FilterDemo5...");chain.doFilter(req, resp);}public void init(FilterConfig config) throws ServletException {}}代码语言:javascript复制@WebServlet("/user/updateServlet")public class ServletDemo2 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("updateServlet...");//转发到index.jsprequest.getRequestDispatcher("/index.jsp").forward(request,response);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}}web.xml配置:设置标签即可3.5 过滤器链(配置多个过滤器)这里要注意过滤器的执行顺序问题:如果有两个过滤器,分别为过滤器1、过滤器2:
过滤器1-》过滤器2-》资源执行-》过滤器2-》过滤器1
【举例】:写两个过滤器,在doFilter中打印信息,访问index.jsp时观察打印顺序是否和上面的一样:
代码语言:javascript复制@WebFilter("/*")public class FilterDemo6 implements Filter {public void destroy() {}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {System.out.println("FilterDemo6 执行了");chain.doFilter(req, resp);System.out.println("FilterDemo6 回来了");}public void init(FilterConfig config) throws ServletException {}}代码语言:javascript复制@WebFilter("/*")public class FilterDemo7 implements Filter {public void destroy() {}public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {System.out.println("FilterDemo7 执行了");chain.doFilter(req, resp);System.out.println("FilterDemo7 回来了");}public void init(FilterConfig config) throws ServletException {}}以上,我们不难想到一个问题,为什么FilterDemo6先于FilterDemo7呢???
【过滤器先后顺序问题】:
注解配置:按照类名的字符串比较规则比较,值小的先执行,如AFilter、BFilter,前者先执行;web.xml配置:谁在前面,谁先执行;4 Filter案例实战4.1 案例1:登录验证【需求】:
1)访问userinfo中的index.jsp资源,验证其是否登录;2)若已登录,则放行;3)若没有登录,则跳转到登录页面,提示“您尚未登录,请先登录”。【分析】:
【代码实现】:
代码语言:javascript复制/** * 登录验证的过滤器 */@WebFilter("/*")public class LoginFilter implements Filter {public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {System.out.println(req);//0.强制转换HttpServletRequest request = (HttpServletRequest) req;//1.获取资源请求路径String uri = request.getRequestURI();//2.判断是否包含登录相关资源路径,要注意排除掉 css/js/图片/验证码等资源if(uri.contains("/login.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/fonts/") || uri.contains("/checkCodeServlet") ){//包含,用户就是想登录。放行chain.doFilter(req, resp);}else{//不包含,需要验证用户是否登录//3.从获取session中获取userObject user = request.getSession().getAttribute("user");if(user != null){//登录了。放行chain.doFilter(req, resp);}else{//没有登录。跳转登录页面request.setAttribute("login_msg","您尚未登录,请登录");request.getRequestDispatcher("/login.jsp").forward(request,resp);}}// chain.doFilter(req, resp);}public void init(FilterConfig config) throws ServletException {}public void destroy() {}}4.2 案例2:过滤敏感词汇【需求】:
1)对userinfo案例录入的数据进行敏感词汇过滤;2)敏感词汇参考《敏感词汇.txt》3)若是敏感词,则替换为***【分析】:
1)主要是需要对request对象进行增强,增强获取参数的相关方法;2)放行,传递代理对象。增强对象的功能,可以采用设计模式来完成,所谓的设计模式,就是一些通用的解决固定问题的方式(有23种)。
回到我们的问题,有两种设计模式可用:装饰模式、代理模式,我们使用代理模式解决我们的问题。
【代码实现】:
1)SensitiveWordsFilter敏感词汇过滤器:
代码语言:javascript复制/** * 敏感词汇过滤器 */@WebFilter("/*")public class SensitiveWordsFilter implements Filter {private List list = new ArrayList();//敏感词汇集合public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {//1.创建代理对象,增强getParameter方法ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//增强getParameter方法//判断是否是getParameter方法if(method.getName().equals("getParameter")){//增强返回值,获取返回值String value = (String) method.invoke(req,args);if(value != null){for (String str : list) {if(value.contains(str)){value = value.replaceAll(str,"***");}}}return value;}return method.invoke(req,args);}});//2.放行chain.doFilter(proxy_req, resp);}public void init(FilterConfig config) throws ServletException {try{//1.获取文件真实路径ServletContext servletContext = config.getServletContext();String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt"); //注意:src下的资源写法//2.读取文件BufferedReader br = new BufferedReader(new FileReader(realPath)); //默认是GBK的//3.将文件的每一行数据添加到listString line = null;while((line = br.readLine())!=null){list.add(line);}br.close();System.out.println(list);}catch (Exception e){e.printStackTrace();}}public void destroy() {}}2)写一个测试Servlet,在浏览器中访问它,查看后台打印信息满足预期。
代码语言:javascript复制@WebServlet("/testServlet")public class TestServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String name = request.getParameter("name");String msg = request.getParameter("msg");System.out.println(name+":"+msg);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}}———————————————————————————————————————