“别小看任何人,越不起眼的人。每每会作些让人想不到的事。"你好我是梦阳辰,快来和我一块儿学习吧!css
Filter和Listener是Servlet规范中的两个高级特性,不一样于Servlet,他们不用于处理客户端请求。java
Filter用于对request,response对象进行修改。
Filter被称做过滤器,其基本功能就是对Servlet容器调用Servlet的过程进行拦截,从而在Servlet进行响应处理先后实现一些特殊的功能。这就比如现实中的污水净化设备,它能够看做一个过滤器,专门用于过滤污水杂质。web
当浏览器访问服务器中的目标资源时,会被Filter拦截,在Filter中进行预处理操做,而后再将请求转发给目标资源。设计模式
当服务器接收到这个请求后会对其进行响应,在服务器处理响应的过程当中,也须要先将响应结果发送给过滤器,在过滤器中对响应结果进行处理后,才会发送给客户端。数组
过滤器的做用:
通常完成通用性的操做。
好比:登陆验证,判断用户是否登陆;统一编码处理,敏感字符处理等。浏览器
其实Filter过滤器就是实现了javax.servlet.Filter接口的类,在javax.servlet.Filter定义了三个方法。服务器
Listener用于对context,session,request事件进行监听。session
步骤:
1.定义一个类,实现接口javax.servlet.Filter。app
2.复写方法异步
3.配置拦截路径
3.1.web.xml配置
3.2.注解配置
javax.servlet.Filter定义了三个方法
@WebFilter("/*")//拦截全部资源public class FilterTest1 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { //初始化过滤器,在Web程序加载的时候调用,配置初始化参数 } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.print("我是过滤器!"); //是否放行,即转发到请求资源 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { //用于释放被Filter打开的资源,当Web服务器卸载Filter对象以前被调用 }}
web.xml配置
取消注解配置,使用web.xml配置。
<filter> <filter-name>FilterTest1</filter-name> <filter-class>filter.FilterTest1</filter-class> </filter> <filter-mapping> <filter-name>FilterTest1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
过滤器的流程:
@WebFilter("/*")public class FilterTest2 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //用于拦截用户的请求,若是和当前过滤器的拦截路径匹配,此方法会被调用 //对request对象请求消息加强 System.out.println("我被执行了request!"); chain.doFilter(req, resp); //对response对象的响应消息加强 System.out.println("我被又执行了response!"); } public void init(FilterConfig config) throws ServletException { }}
拦截路径的配置:
具体资源路径:/index.jsp 只有访问index.jsp资源时,过滤器才会被执行。
2.拦截目录:/user/* 访问/uer下的全部资源时,过滤器都会被执行。
3.后缀名拦截:*.jsp 访问全部后缀名为jsp资源时,过滤器执行。
4.拦截全部资源:/*
拦截方式的配置:
资源被访问的方式:
请求转发过滤器不会被执行。
1.注解配置,能够配置多个值
设置dispatcherTypes属性
REQUEST:默认值,浏览器直接请求资源 FORWARD:转发访问资源 INCLUDE:包含访问资源 ERROR:错误跳转资源 ASYNC:异步访问资源
2.web.xml注释
//浏览器直接请求资源时,会被执行,转发不会@WebFilter(value = "/*",dispatcherTypes = DispatcherType.REQUEST)public class FilterTest3 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("我被执行啦!"); chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException { }}
@WebFilter(value = "/*", dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
<filter> <filter-name>FilterTest1</filter-name> <filter-class>filter.FilterTest1</filter-class> </filter> <filter-mapping> <filter-name>FilterTest1</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping>
过滤器链(多个过滤器):
执行顺序
先执行过滤器1,再执行过滤器2,回来先执行过滤器2,再执行过滤器1。
怎么判断过滤器谁在前面:
1.注解配置
按照类名的字符串比较规则,较小的先执行。
如:AFilter,BFilter
AFilter就先执行。
2.web.xml配置:
<filter-mapping>
谁定义在上面,谁就先执行。
案例一:登陆验证
1.访问某些资源,验证其是否登陆
2.若是登陆了,则直接放行。
3.若是没有登陆,则跳转到登陆页面,提示,“你还没有登陆,请先登陆”。
/** * 登陆验证的过滤器 */@WebFilter("/*")//除了登陆相关资源外(如login.jsp)public class FilterTest4 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //1.强转 HttpServletRequest request = (HttpServletRequest)req; //2.获取资源请求路径 String uri = request.getRequestURI(); //排除登陆相关资源,css/js/fonts等 if(uri.contains("/login.jsp")||uri.contains("/loginServlet")||uri.contains("/css/")||uri.contains("/js/")){ //用户就是像登陆,放行 chain.doFilter(req, resp); }else{ //不包含,须要验证用户是否登陆 //3.从获取session中获取user Object user = request.getSession().getAttribute("user"); if(user!=null){ //登陆了,放行 chain.doFilter(req, resp); }else{ //没有登陆,跳转到登陆页面 request.setAttribute("login_msg","你为登陆,请先登陆!"); request.getRequestDispatcher("/login.jsp"); } } } public void init(FilterConfig config) throws ServletException { }}
案例二:敏感词汇的过滤
分析:
案例须要对request对象进行加强。
那如进行加强呢?
加强对象的功能
设计模式:一些通用的解决固定问题的方式。
装饰模式:
代理模式:
概念:
1.真实对象:被代理的对象。
2.代理对象
3.代理模式:代理对象代理真实对象,达到加强真实对象的目的。
实现方式:
1.静态代理
在一个类文件描述代理模式。
2.动态代理
在内存中造成代理类(在内存中动态生成)。
注:请先看如下动态代理相关知识再看案例二的实现
案例二实现:
1.对requset对象进行加强,加强获取参数相关方法。
2.放行,传递代理对象。
代码实现:
/** * 敏感词汇过滤器 */@WebFilter("/*")public class FilterSensitiveWords implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //建立代理对象,加强getParameter方法 ServletRequest proxy_req=(ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //加强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); } private List<String> list = new ArrayList<>();//敏感词汇 public void init(FilterConfig config) throws ServletException { try { //1.加载配置文件(获取文件的真实路径) ServletContext servletContext = config.getServletContext(); String realPath = servletContext.getRealPath("/WEB-INF/classes/SensitiveWords.txt"); //2.读取文件 BufferedReader br = new BufferedReader(new FileReader(realPath)); //3.将文件的每一行加载到list中 String line =null; while ((line = br.readLine())!=null){ list.add(line); } br.close(); System.out.println(list); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}
测试的servlet:
@WebServlet("/ServletSensitiveWordsTest")public class ServletSensitiveWordsTest 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 { doPost(request,response); }}
结果:
重点:注意路径问题:
SensitiveWords在src源文件目录下。
//1.加载配置文件(获取文件的真实路径)
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath
("/WEB-INF/classes/SensitiveWords.txt");
实现步骤:
1.代理对象和真实对象实现相同的接口。
2.代理对象= Proxy.newProxyInstance();
3.使用代理对象调用方法。
4.加强方法。
加强方式:
1.加强参数列表。
2.加强返回值类型。
3.加强方法体执行逻辑。
练习理解:
package proxy;public interface SaleComputer { public String sale(double money); public void show();}
/** * 真实类 */public class ASUS implements SaleComputer{ @Override public String sale(double money) { System.out.println("花了"+money+"拍下一台电脑!"); return "ASUS"; } @Override public void show() { System.out.println("展现电脑!"); }}
public class ProxyTest { public static void main(String[] args) { //建立真实对象 ASUS asus1 = new ASUS(); //动态代理加强ASUS对象 /* 三个参数: 1.类加载器:真实对象.getClass().getClassLoader() 2.接口数组:真实对象.getClass().getInterfaces() 3.处理器:new InvocationHandler() */ SaleComputer proxy_asus=(SaleComputer) Proxy.newProxyInstance(asus1.getClass().getClassLoader(), asus1.getClass().getInterfaces(), new InvocationHandler() { /** * 代理逻辑编写的方法:代理对象调用的全部方法都会触发该方法执行 * @param proxy 代理对象 * @param method 代理对象调用的方法被封装成对象 * @param args 代理对象调用方法时,传递的实际参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*System.out.println("该方法执行了..."); System.out.println(method.getName());*/ //使用真实对象调用该方法 /* Object obj = method.invoke(asus1,args);*/ //加强参数;判断是否时sale方法 if(method.getName().equals("sale")){ //加强参数 double money = (double)args[0]; money*=0.8; //使用真实对象调用该方法 String obj = (String) method.invoke(asus1,money); //加强返回值类型 return obj+"_鼠标垫"; }else { Object obj = method.invoke(asus1,args); return obj; } } }); //3.调用方法 String computer = proxy_asus.sale(9000); System.out.println(computer); }}
概念:web的三大组件之一。
事件监听机制
程序开发中,常常须要对某些事件进行监听,如鼠标单击事件,监听键盘按下事件等,此时就须要监听器。
事件:用户的一个操做,如点击按钮…
事件源:产生事件的对象。
监听器:负责监听发生在事件源上的事件。
注册监听:将事件,事件源,监听器绑定在一块儿。当事件源上发生某个事件后,执行监听器代码。
ServletContextListener:监听ServletContext对象的建立和销毁。
//Servlet对象被销毁前会调用此方法void contextDestroyed(ServletContextEvent sce) //ServletContext对象被建立后会调用该方法。void contextInitialized(ServletContextEvent sce)
步骤:
1.定义一个类实现ServletContextListener接口。
2.复写方法。
3.配置
1.web.xml
<listener> <listener-class>listener/ServletContextListenerTest1</listener-class> </listener>
指定初始化参数:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/applicationContext.xml</param-value> </context-param>
2.注解配置
@WebListener
@WebListenerpublic class ServletContextListenerTest1 implements ServletContextListener { //监听ServletContext建立,ServletContext对象服务器启动自动建立 @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(); } } //在服务器关闭后,ServletContext对象被销毁。当服务器正常关闭后该方法被调用 @Override public void contextDestroyed(ServletContextEvent sce) { }}
一切事没法追求完美,惟有追求尽力而为。这样心无压力,出来的结果反而会更好。