1 过滤器概述
oa项目还有什么缺陷?
目前oa项目的功能不多,Servlet也不多,但若功能很多,假如还有EmpServlet、OrderServlet等等,每一个Servlet都是处理自己相关的业务。但它们有相同的地方,在执行之前都要判断用户是否登录了,如果用户登录可以继续操作,如果没有需要登录。这段处理是固定的,大家都一样,怎么解决这个问题??可以使用Filter过滤器。
Filter过滤器是什么,有什么用,执行原理是什么?
- Filter是过滤器
- 可以在Servlet这个目标程序执行之前添加代码,也可以在执行之后添加代码,之前之后都可以过滤
- 一般情况下都是在过滤器当中编写公共代码。
一个过滤器怎么写?
第一步:编写一个Java类实现一个接口:jakarta.servlet.Filter。并且实现这个接口中的所有方法。
- init方法:在Filter对象第一次被创建之后调用,并且只调用一次
- doFilter方法:只要用户发送一次请求,则执行一次,发送N此执行N次,在这个方法中编写过滤规则。
- destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次。
第二步:在web.xml文件对FIlter进行配置,和Servlet类似,或者使用注解进行配置。
注意:
- Servlet对象默认情况下,在服务器启动的时候是不会创建对象的。
- Filter对象默认情况下,在服务器启动的时候会创建对象
- Servlet是单例的
小小演示一波:
随便写两个Servlet,然后写个Filter,Filter如下:
package com.itzw.javaweb.servlet;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("*.do")
public class Filter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init方法执行了。。");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("doFilter执行了。。");
//执行下一个过滤器,如果下一个不是过滤器则执行目标程序servlet
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("doFilter结束了。。");
}
@Override
public void destroy() {
System.out.println("destroy方法执行了。。");
}
}
注意要想执行Servlet,在doFilter方法中要写上chain.doFilter,它会接着执行下面的程序。而在注解中,可以进行模糊匹配比如在Servlet的注解可以在后面加个“.do”,就可以使用“*/”进行模糊匹配或者像之前一样“/dept/*”,或者可以匹配所有路径“/*”,根据需求来。
在web.xml文件中进行配置的时候,Filter的执行顺序是什么?
- 依靠filter-mapping标签的配置位置,越靠上优先级越高。
- 过滤器的调用顺序遵循栈数据结构
使用注解@WebFilter的时候,执行顺序是怎样的?
- 执行顺序是比较Filter的类名
- 比如Filter1和Filter2,优先执行Filter1
Filter的声明周期:
- 和Servlet的声明周期一致
- 唯一的区别是:Filter默认情况下,在服务器启动阶段就实例化,Servlet不会。
Filter过滤器这里有一个设计模式:
- 责任链设计模式
- 过滤器的最大优点:在编译阶段不会确定调用顺序,因为顺序是web.xml文件中的配置决定的,我们可以随时更改,不需要改代码。
- 配置格式如下:
-
<!--Filter1--> <filter> <filter-name>filter1</filter-name> <filter-class>com.itzw.javaweb.servlet.Filter1</filter-class> </filter> <filter-mapping> <filter-name>filter1</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <!--Filter2--> <filter> <filter-name>filter2</filter-name> <filter-class>com.itzw.javaweb.servlet.Filter2</filter-class> </filter> <filter-mapping> <filter-name>filter2</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>
只要将Filter2中的filter-mapping放到上面就可以改变顺序
2 使用过滤器改造oa项目
本次我们对是否能访问资源进行过滤,也就是当访问一个资源时是否需要登录,因为这种情况很多,一个网站的资源数量相当多,每次都要我们自己判断太麻烦,不如放在过滤器:
package com.itzw.oa.web.action;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
public class LoginCheckFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//获取请求路径
String servletPath = request.getServletPath();
HttpSession session = request.getSession(false);
if ("/index.jsp".equals(servletPath) || "/welcome".equals(servletPath) ||
"/user/login".equals(servletPath) || "/user/exit".equals(servletPath) ||
"/error.jsp".equals(servletPath) ||
session != null && session.getAttribute("username") != null){
//有这个用户才能进行下面的操作
chain.doFilter(request,response);
}else {
//没有这个用户,跳转到登录界面
response.sendRedirect(request.getContextPath() + "/index.jsp");
}
}
@Override
public void destroy() {
}
}
本次的过滤路径写的是:“/*”,也就是全部路径,注意,并不是全部路径都要过滤,比如登录界面,错误界面,欢迎页面等等。