项目第7天—Filter&Listener
项目中遇到的问题?
一、Web项目三大组件
Servlet , Filter , Listener HTML开发的三剑客 —(HTML,CSS,JS)
二、Filter—过滤器
1、概念
2、案例
服务器中的资源—》 图片,css,js,音频视频,html,jsp 等都是资源
第一步:编写一个Filter 通过实现Filter接口
package com.qfedu.filter;import javax.servlet.*;import java.io.IOException;public class Demo01Filter implements Filter {// init 方法 ==-》 Filter创建的时候调用@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("我被创建了....");}// 当程序被拦截器拦截之后,会执行doFilter里面的代码@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("这是我的第一个过滤器");// 获取到资源之后,根据条件选择是否放行filterChain.doFilter(servletRequest,servletResponse);}// filter即将回收的时候调用@Overridepublic void destroy() {System.out.println("我即将被回收");}}
这个里面有三个方法:
init —> 该Filter被创建的时候调用,只调用一次
destory—> 该Filter对象被销毁的时候调用,也只调用一次
doFilter —> 核心方法,过滤器没拦截一次请求,就执行一次方法。
不管是做什么操作,记得放行:
filterChain.doFilter(servletRequest,servletResponse);
第二步: 配置Filter
1) 在web.xml中编写xml
<filter><filter-name>filter01</filter-name><filter-class>com.qfedu.filter.Demo01Filter</filter-class></filter><filter-mapping><filter-name>filter01</filter-name><url-pattern>/*</url-pattern></filter-mapping>
2、注解的方案: @WebFilter(“/*”)
执行顺序:
浏览器 —> 过滤器去拦截 —> 获取资源 —> 又到 过滤器这里—> 过滤器放行—>浏览器展示资源
问题:如果有两个 过滤器,都满足拦截条件,先执行哪个,后执行哪个?
1、如果你是注解配置的方式编写的过滤器:执行顺序按照类名进行比较
AFilter BFilter A先执行,B后执行
A10Filter A2Filter 先执行A10Filter ,后执行A2Filter.
2、web.xml方式编写的配置
定义在最前面的,先执行,定义在后面的后执行
一个项目中,要么都使用xml,要么都使用注解,所以注解和xml混着写的方式一般不存在。
拦截的规则有如下几种写法:
1、拦截具体的路径 /index.jsp 只拦截index.jsp
2、拦截目录: /customer/ 只拦截以customer开头的二级访问路径
3、拦截以某种后缀名结尾的请求 .do .action .jsp
4、拦截所有请求 /*
在过滤器中,还可以设置拦截被访问的方式 DispatcherType 【了解】
DispatcherType
REQUEST: 默认值,拦截浏览器直接请求的资源,类似于重定向
FORWARD: 内部转发 只拦截内部转发的资源 INCLUDE: 一个资源被包含在另一个资源中,发出的请求
ERROR: 错误跳转的资源
* ASYNC: 异步访问的资源_
3、实战
通过过滤器,完善只有登录才能操作系统中的资源
package com.qfedu.filter;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;@WebFilter({"/customer/*"})public class LoginFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}// ServletRequest 是 HttpServletRequest的父类@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("进入到登录过滤器");HttpServletRequest req = ((HttpServletRequest) servletRequest);// 先判断该用户有没有登录,如果没有登录,直接跳转到登录页面HttpSession session =req.getSession();Object obj = session.getAttribute("USERNAME");if(obj!=null){// 如果判断登录过了,直接放行filterChain.doFilter(servletRequest,servletResponse);}else{// 没有登录过,跳转到登录页面((HttpServletResponse)servletResponse).sendRedirect(req.getContextPath()+"/login.jsp");}}@Overridepublic void destroy() {}}
字符集的一个设置:
我们有很多页面,都需要设定字符集,防止中文乱码 编写一个字符集过滤器,为指定的servlet设置字符集,以后就不用我们设置了。
三、Listener—> 监听器
类似于窃听器,按钮上加事件等此类性质的情况。
Java Web监听器很多中,最常用的,以后能用的上的,就这一种:ServletContextListener
监听ServetContext的诞生的。
如果ServletContext对象被创建了,就会触发ServletContextListener中的代码。
ServletContext对象有什么用,它创建不创建跟我的代码有什么关系?
ServletContext对象是在tomcat已启动就被创建的,被tomcat所创建。
换句话说:当一个项目已启动就会触发ServletContextListerner.
我们可以将项目启动时需要加载的数据或者资源放入到该ServletContextListener中。
static {
}
static 静态代码块是一加载某个类,就去执行某些代码。
项目中的真实案例:
群发短信功能—-> 每一个手机号码都需要进行一个红名单的校验
红名单:此类手机号码千万不要给它发垃圾短信。
红名单的数据在你们公司的数据库中,大约有100多万条。
发短信要校验红名单。
项目一启动,就开始查询红名单数据,将数据存放在内存中,将来拿到一个手机号去校验是否是红名单数据就非常快。
代码演示:
package com.qfedu.listener;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;@WebListenerpublic class ContextLoaderListener implements ServletContextListener {//监听ServletContext被创建就执行如下代码@Overridepublic void contextInitialized(ServletContextEvent servletContextEvent) {System.out.println("servletContext被创建了");}//监听ServletContext被销毁,触发如下代码@Overridepublic void contextDestroyed(ServletContextEvent servletContextEvent) {System.out.println("servletContext被销毁了");}}
Listener编写完之后也有两种配置方案
xml
<listener><listener-class>com.qfedu.listener.ContextLoaderListener</listener-class></listener>
注解方案: @WebListener
监听器什么时候执行,不是由我们决定的,而是监听器本身监听的对象决定,监听器监听的对象发生改变了,监听器中的代码就自动执行了。一个对象,同一个动作一般只有一个监听器。我们学习的是ServletContextListener!
四、总结三大web组件
1、web.xml中都可以写什么内容?
项目中的web.xml其实是tomcat服务器中web.xml的子集。它可以配置servlet,Filter,Listener
welcome-file-list 设置欢迎页,还可以设置一些参数 context_param。
2、各个组件之间的执行顺序说一下?
web.xml中加载顺序:
filter如果是多个,加载顺序怎样。
servlet很多,加载顺序如何?load-on-startup 有关系。
3、三大组件共同的特点
编写完代码之后,必须进行配置,配置都分为两种 xml 和 注解
五、总结二阶段内容
1、HTML\CSS\JS 【熟悉并且会修改一些样式】
2、MySQL 【一定要非常熟悉】
3、Servlet \ Filter \ Listener 【后面基本都被替换了,是web的三大基石】
Servlet—->SpringMVC
Filter —-> Struts2
4、JSP — 网页模板技术
5、JdbcTemplate 它是指众多操作数据库的小框架而已(工具包)
6、JQuery/Ajax/JSON —一直用下去。
二阶段的目标:可以独立完成一些小的功能,能够排错。
企业看重的—编程经验(开发经验)不等于工作年限
代码技巧(实现技巧)、考虑问题是否全面、排错能力强、独立思考,见识广,各种类型的项目都见过,参与过,职场经验丰富。
六、验证码功能
在公司中如果你能将一个问题差分为多个小问题,你就可以做管理(项目经理,技术总监)
拆分:
1、在页面上修改样式,可以展示验证码
<p style="position: relative;"><input class="ipt" style="width:150px;" type="text" name="yzm" id="yzm" placeholder="输入验证码" /> <img id="imgCode" src="${pageContext.request.contextPath}/validateCode/create" style="margin-top: 5px;" height="40px" width="100px" onclick="changeImg()"></p>
页面上还要给验证码图片加上点击事件:
再要我们的url后面添加一个参数随机值,因为相同的url不断的请求,浏览器不会去后台执行代码的,所以搞一个随机值,不断的更改值,骗过浏览器去后台执行代码。
function changeImg(){console.log("进来了.....");$("#imgCode")[0].src="${pageContext.request.contextPath}/validateCode/create?data="+Math.random();}
2、在前端或者后台生成验证码,展示在页面上
借助一个工具类 ValidateCode.jar,需要先导入jar包
package com.qfedu.servlet;import cn.dsna.util.images.ValidateCode;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/validateCode/*")public class ValidateCodeServlet extends BaseServlet {public void create(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 第一个参数 图片的宽度// 2 图片高度// 3 图片上包含多少个字母// 4 生成干扰线的数量ValidateCode validateCode = new ValidateCode(100,40,4,20);validateCode.createCode();//生成验证码图片// 每次生成的验证码都要放入session一个,因为我们将来登录的时候要校验用户的验证码是否正确String code = validateCode.getCode();req.getSession().setAttribute("VALIDATECODE",code);validateCode.write(resp.getOutputStream());//通过流的方式传递给页面}}
3、提交表单时验证码一定要是正确的。
需要提前在生成验证码的时候就在session中放入验证码的值,便于以后登录时校验
String code = validateCode.getCode();req.getSession().setAttribute("VALIDATECODE",code);
4、登录校验:
HttpSession session = req.getSession();String validatecode = (String)session.getAttribute("VALIDATECODE");String yzm = req.getParameter("yzm");if(!validatecode.equals(yzm)){resp.getWriter().write("2");}else{String username = req.getParameter("username");String password = req.getParameter("password");String isJiZhu = req.getParameter("isJiZhu");System.out.println(username+","+password+","+isJiZhu);boolean result = adminService.validateLogin(username,password);if(result){Cookie usernameCookie = new Cookie("username", username);Cookie passwordCookie = new Cookie("password", password);if(isJiZhu.equals("true")){// 设置7天过期usernameCookie.setMaxAge(7*3600*24);passwordCookie.setMaxAge(7*3600*24);}else{usernameCookie.setMaxAge(0);passwordCookie.setMaxAge(0);}resp.addCookie(usernameCookie);resp.addCookie(passwordCookie);// 将用户名保存在session中便于页面获取数据session.setAttribute("USERNAME",username);resp.getWriter().write("1");}else{resp.getWriter().write("0");}}
这是只编写验证码的一种方式而已,还有很多种。
