Filter:过滤器
-
概念:
- web过滤器:拦截浏览器的请求,完成一些特殊功能。
- 过滤器作用:一般用于完成通过的操作。(例如帮助服务器检查一些字段是否携带,比如访问界面的时候需要带token,否则返回登录界面)
-
快速入门:
- 步骤:
- 定义一个类,实现接口Filter
- 复写方法
- 配置拦截路径(定义访问什么资源的时候,过滤器生效),两种配置方法
- Web.xml
- 注解 @WebFilter( )我们需要配置里面的urlPatten这个属性,这个属性等于value,所以可以省略不写。
- @WebFilter(“/*”) 代表访问所有资源的时候都会拦截
- 步骤:
-
过滤器细节:
-
web.xml配置
<filter> <filter-name>demo1</filter-name> <filter-class>com.emnets.java1115.Filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>demo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
过滤器的执行流程
-
doFilter方法中:req就是请求参数,
- 首先执行chain.doFilter(req,resp)方法之前的代码,可能会有一些对req参数的校验,如果通过就放行
- chain.doFilter(req,resp) 这个方法是放行方法,就是让服务器去处理请求
- 执行完放行后会回到chain.doFilter(req,resp)这个方法的下一行代码继续执行。
// 对request对象的请求消息增强 System.out.println("filterDemo2..."); // 放行 chain.doFilter(request, response); // 对response对象的响应消息增强 System.out.println("filterDemo2 executing!");
-
-
过滤器生命周期方法
- init方法:服务器启动后会创建Filter对象,然后调用init方法,服务器自动完成(仅执行一次)用于加载资源
- doFilter方法:每一次请求被拦截资源时会执行,方法内包含拦截的逻辑
- destroy方法:服务器关闭之后。Filter对象被销毁。如果服务器正常关闭,则会执行destroy(仅执行一次)释放资源
-
过滤器的配置详解(两种配置)
-
拦截路径配置:
- “/*”:拦截所有资源
- “/资源”:具体资源路径,只有访问资源时过滤器才会执行,使用较少。
- “/user/*”:目录拦截,访问user目录下的所有资源时,过滤器都会执行。
- “*.jsp”:后追拦截,访问所有后缀名为jsp的资源时,过滤器都会拦截。
-
拦截方式配置:资源访问的方式(直接请求、转发是两种不同的方式)
-
注解配置:设置dispatcherTypes的属性
- REQUEST:默认值,浏览器直接请求资源(重要)
- FORWARD:转发访问资源(重要)
- INCLUDE:包含访问资源
- ERROR:错误跳转资源
- ASYNC:异步访问资源
-
注解配置多个值:
-
@WebFilter(value = "/*",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
-
-
web.xml配置方式:
-
<filter> <filter-name>demo1</filter-name> <filter-class>com.emnets.java1115.Filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>demo1</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> </filter-mapping>
-
-
思考题:如果同时配置了请求和转发的过滤,那么一次请求触发的转发会触发几次过滤器
- 答案:2次 ,请求和转发分别都会触发过滤器
- 理解:虽然是一次请求,但是在服务器内部进行了跳转,每次切换资源都会触发过滤器
-
-
-
过滤器链(配置多个过滤器)
- 执行顺序 :如果有两个过滤器:f1 和 f2
- f1执行
- f2执行
- 资源执行
- f2回来执行
- f1回来执行
- 过滤器设计的先后顺序:
- 注解配置:按照类名的字符串比较规则比较,值小的先执行,
- 例如:AFilter 和 BFilter比较,A先执行
- web.xml配置:<filter-mapping> 谁定义在上面,谁先执行
- 注解配置:按照类名的字符串比较规则比较,值小的先执行,
- 执行顺序 :如果有两个过滤器:f1 和 f2
-
案例一:登录验证
- 步骤:
- 排除登录相关的资源,
- 是:直接放行
- 不是:判断是否已经登录
- 未登录:
- 已登录:
- 排除登录相关的资源,
@Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { // 0.强制转换 HttpServletRequest request = (HttpServletRequest)req; // 1.获取请求资源的请求路径 String requestURI = request.getRequestURI(); // 2.判断是否包含登录相关 // 2.1 同时还要注意排除各种css/js/图片/验证码等资源 if(requestURI.contains("/login.jsp")||requestURI.contains("/loginServlet") ||requestURI.contains("/css/*")||requestURI.contains("/js/*")||requestURI.contains("/checkCodeServlet")){ // 放行登录 chain.doFilter(req, resp); }else { // 3.session获取 Object user = request.getSession().getAttribute("user"); if(user!=null){ // 已经登录了 ,放行 chain.doFilter(req, resp); }else { // 跳转登录 request.setAttribute("login_msg","您尚未登录"); request.getRequestDispatcher("/login.jsp").forward(request,resp); } } }
- 步骤:
-
代理模式:
-
理解:在运行期间,对象中方法的动态拦截,在拦截前后执行功能操作,而原有对象不改变
-
概念:
- 真实对象:被代理的对象
- 代理对象:
- 代理模式:代理对象去代理真实对象,达到增强真是对象功能的目的
-
实现方式:(区别在于代理对象的生成方式)
- 静态代理:由一个类文件描述代理模式
- 动态代理:在内存中形成代理类(看不到一个真正的类文件,会在内存中动态生成)
- 实现步骤:
- 代理对象和真实对象实现相同的接口
- 代理对象 = Proxy.newProxyInstance( );进行代理对象的获取
- 使用代理对象调用方法。
- 增强方法
- 实现步骤:
- 动态代理代码:
public class ProxyTest { /** * * @param args */ public static void main(String[] args) { // 1.创建真实对象 Lenovo lenovo = new Lenovo(); /** * 2.动态代理增强lenovo对象 * 三个参数: * 1.类加载器 : 真实对象.getClass().getClassLoader() * 2.接口数组 : 真实对象.getClass().getInterfaces() * 3.处理器 : */ SaleComputer proxy_lenovo = (SaleComputer)Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() { /** * 代理逻辑编写的方法:代理对象调用的所有方法,都会触发该方法执行。 * 增强代码的逻辑就会在这个方法中执行 * @param proxy 代理对象,就是指proxy_lenovo这个对象,一般不用 * @param method 代理对象调用的方法会封装成对象,变成参数传递进来 * @param args 代理对象调用方法时,传递的实际参数,封装进args,这里面就会有8000这种参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("该方法执行了....."); System.out.println(method.getName()); return null; } }); // 3.调用方法 String computer = proxy_lenovo.sale(8000); System.out.println(computer); } }
-
invoke方法的写法
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 相当于使用真实对象调用该方法 Object obj = method.invoke(lenovo, args); // 思考:方法应该如何增强 // 这个返回值就是调用了方法后接收的返回值,会根据操作方法的不同而完全不同。 return obj; }
-
方法增强方式:
-
增强参数列表
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 首先判断方法是否是需要增强的方法 if(method.getName().equals("sale")){ // 1.增强参数 double money = (double) args[0]; money *= 0.85; Object obj = method.invoke(lenovo, money); return obj; }else { // 相当于使用真实对象调用该方法 Object obj = method.invoke(lenovo, args); // 这个返回值就是调用了方法后接收的返回值,会根据操作方法的不同而完全不同。 return obj; } }
-
增强返回值类型
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 首先判断方法是否是需要增强的方法 if(method.getName().equals("sale")){ // 1.增强返回值 double money = (double) args[0]; money *= 0.85; String obj = (String)method.invoke(lenovo, money); return obj+"_鼠标垫"; }else { // 相当于使用真实对象调用该方法 Object obj = method.invoke(lenovo, args); // 这个返回值就是调用了方法后接收的返回值,会根据操作方法的不同而完全不同。 return obj; } }
-
增强方法体执行逻辑
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 首先判断方法是否是需要增强的方法 if(method.getName().equals("sale")){ // 1.增强返回值 double money = (double) args[0]; money *= 0.85; System.out.println("专车接你...."); String obj = (String)method.invoke(lenovo, money); System.out.println("免费送货...."); return obj+"_鼠标垫"; }else { // 相当于使用真实对象调用该方法 Object obj = method.invoke(lenovo, args); // 这个返回值就是调用了方法后接收的返回值,会根据操作方法的不同而完全不同。 return obj; } }
-
-
-
案例二:过滤敏感词汇
- 需求:
- 对录入数据的敏感词汇进行过滤
- 敏感词汇为“笨蛋”、“坏蛋”
- 如果是敏感词汇,替换为“***”
- 分析:
- 对request对象的getParameter方法进行增强,增强获取参数相关方法
- 放行。传递代理对象
/** * 敏感词汇过滤器 */ @WebFilter("/*") public class SensitiveWordsFilter implements Filter { private List<String> list = new ArrayList<String>(); // 敏感词汇 public void init(FilterConfig config) throws ServletException { try { // 加载文件,获取文件真实路径 ServletContext servletContext = config.getServletContext(); String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt"); // 读取文件 BufferedReader br = new BufferedReader(new FileReader(realPath)); // 将文件的每一行数据添加到list中 String line = null; while ((line = br.readLine())!=null){ list.add(line); } br.close(); System.out.println(list); } catch (Exception e){ e.printStackTrace(); } } public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { // 1.创建代理对象,增强getParameter方法 ServletRequest proxy_req = (ServletRequest)Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("getParameter")){ // 增强返回值 // 获取返回值,对返回值进行过滤 String value = (String)method.invoke(request, args); if(value!=null){ for(String str:list){ if(value.contains(str)){ value = value.replaceAll(str,"***"); } // if } //for }// if return value; } return method.invoke(request,args); } }); // 2.放行,传递代理对象 chain.doFilter(proxy_req, response); } }
为了测试编写了一个servlet一起运行:
@WebServlet("/testServlet") public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override 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); } }
- 需求:
-
补充了对getParameterMap方法的增强:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { // 1.创建代理对象,增强getParameter方法 ServletRequest proxy_req = (ServletRequest)Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("getParameter")){ // 增强返回值 // 获取返回值,对返回值进行过滤 String value = (String)method.invoke(request, args); if(value!=null){ for(String str:list){ if(value.contains(str)){ value = value.replaceAll(str,"***"); } // if } //for }// if return value; }else if(method.getName().equals("getParameterMap")){ Map<String, String[]> parameterMap = (Map<String, String[]>)method.invoke(request,args); if(!parameterMap.isEmpty()){ // 遍历参数列表 for(String[] p_value: parameterMap.values()){ // 判断是否有子参数 if(p_value.length>0){ // 遍历子参数 for(int i=0;i<p_value.length;i++){ // 遍历敏感词汇 for(String str:list){ // 判断是否含有敏感词汇 if(p_value[i].contains(str)){ // 有则替换 p_value[i] = p_value[i].replaceAll(str,"***"); } } } } } } return parameterMap; } return method.invoke(request,args); } }); // 2.放行,传递代理对象 chain.doFilter(proxy_req, response); }
-
Listener:监听器
-
概念:web的三大组件之一。
- 事件监听机制:
- 事件 :一件事情
- 事件源 :事件发生的地方
- 监听器 :一个对象
- 注册监听:将事件、事件源、监听器绑定在一起。当事件源上发生某个事件后,执行监听器代码。
- 事件监听机制:
-
ServletContextListener:监听ServletContext对象的创建和销毁
- void contextDestory(ServletContextEvent sce): servletContext对象被销毁前会调用
- void contextInitialized(ServletContextEvent sce):servletContext创建之后会调用该方法
-
步骤:
-
定义一个类,实现servletContextListener接口
-
复写方法
-
配置
-
web.xml方式
<listener> <listener-class>com.emnets.java1115.listener.ContextLoaderListener</listener-class> </listener>
-
注解方式
@WebListener
-
-
-
代码:
public class ContextLoaderListener implements ServletContextListener { /** * 监听servletContext对象创建。ServletContext对象服务器启动后自动创建。 * @param sce */ @Override public void contextInitialized(ServletContextEvent sce) { // 1.获取servletContext对象 ServletContext servletContext = sce.getServletContext(); // 2.加载资源文件 String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation"); // 3.获取真实路径 String realPath = servletContext.getRealPath(contextConfigLocation); // 4.加载进内存 try { FileInputStream fis = new FileInputStream(realPath); System.out.println(fis); } catch (FileNotFoundException e) { e.printStackTrace(); } System.out.println("servletContext对象被创建了"); } /** * 服务器关闭后,ServletContext对象被销毁,当服务器正常关闭后该方法被调用 * @param sce */ @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContext对象被销毁了"); } }