Listener&Filter
Listener&Filter
1.Listener
监听器,用于监听某一个事件的发生。
监听器的内部机制:接口回调。
1.1接口回调
需求:
A类执行循环,当循环次数为5时,通知B。
-
传统做法
传递B的实例给A。
public class A {
public static void loop(){
for(int i=0;i<10;i++){
if(i==5){
B b=new B();
b.print();
}
}
}
}
public class B {
public void print(){
System.out.println("A循环了5次,通知到B");
}
}
public class Test1 {
public static void main(String[] args) {
A a=new A();
a.loop();
}
}
-
接口回调做法
传递一个接口PrintListener给A,B实现了该接口,因为A可能在B之前就写好了,此时A并不知道有B的存在,也就更无法直接接收B的实例了,所以可以利用接口回调解决。
public class A {
public static void loop(PrintListener printListener){
for(int i=0;i<10;i++){
if(i==5){
printListener.print();
}
}
}
}
public class B implements PrintListener{
public void print(){
System.out.println("A循环了5次,通知到B");
}
}
public interface PrintListener {
public void print();
}
public class Test1 {
public static void main(String[] args) {
A a=new A();
a.loop(new B());//A中的loop方法接收的是PrintListener,而这里可以传递PrintListener的实现类的实例对象,这是多态的体现。
}
}
1.2Web监听器
总共有8个,分为三种类型。
使用步骤都一致:
- 定义一个类,实现监听器接口
- 注册/配置监听器
1.2.1监听作用域的创建和销毁
作用域共有4个:page,request,session,application,监听作用域的创建和销毁是监听request,session,application三个作用域的创建和销毁。
- ServletRequestListener
监听request作用域对象的创建和销毁。
request对应类型为HttpServletRequest。
reqeust创建:访问服务器上的任意资源时,如html,jsp,servlet;
request销毁:服务器已经对这次请求做出了响应后。
@WebListener//注册监听器,相当于在web.xml中配置的那一段
public class MyServletRequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("request销毁了");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("request初始化了");
}
}
在web.xml中注册监听器:
<listener>
<listener-class>com.itheima.listener.MyServletRequestListener</listener-class>
</listener>
- ServletContextListener
监听application作用域对象的创建和销毁。
application对应类型为ServletContext。
ServletContext创建:服务器启动时;
ServletContext销毁:正常关闭服务器。
作用:完成初始化工作,执行自定义任务调度。
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("servletContext初始化了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("servletContext销毁了");
}
- HttpSessionListener
监听session作用域对象的创建和销毁。
session对应类型为HttpSession。
session创建:调用request.getSession(),jsp中默认创建了session;
session销毁:关闭服务器(包括正常和非正常);Session会话时间过期,默认是30分钟。
作用:可以统计在线人数。
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("session创建了");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("session销毁了");
}
}
1.2.2监听作用域属性状态变更
监听在servletContext,request,session作用域中属性的添加,替换,移除动作。
- ServletContextAttributeListener
@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("servletContext属性添加了");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("servletContext属性移除了");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("servletContext属性替换了");
}
}
<body>
<%
//添加name属性
application.setAttribute("name","zhangsan");
//替换name属性
application.setAttribute("name","wanger");
//移除name属性
application.removeAttribute("name");
%>
</body>
- ServletRequestAttributeListener
- HttpSessionAttributeListener
1.2.3监听HttpSession里存值的状态的变更
此类监听器无须注册。
- HttpSessionBindingListener
监听对象与session绑定和解除绑定的动作,此对象要实现HttpSessionBindingListener。
public class Student implements HttpSessionBindingListener {
private int id;
private String name;
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("Student对象被绑定到session了");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("Student对象从session中解除绑定了");
}
...
}
<%
session.setAttribute("obj1",new Student(1,"小王"));
session.removeAttribute("obj1");
%>
- HttpSessionActivationListener
用于监听现在session的值是钝化(序列化)还是活化(反序列化)的动作。
钝化:把内存中的数据存储到硬盘上;
活化:把硬盘中的数据读取到内存中。
钝化活化作用:session中的值可能会很多,并且可能很长一段时间不使用这个内存中的值,那么可以考虑把session的值可以存储到硬盘上【钝化】,等下一次使用时,再从硬盘上提取出来【活化】。
- Student2.java
public class Student2 implements HttpSessionActivationListener, Serializable {
private int id;
private String name;
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println("session被钝化了");
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println("session被活化了");
}
...
}
-
index2.jsp
index2.jsp将Student2存入到session,然后正常关闭服务器,session 将被钝化。
<%
//存入到session
session.setAttribute("obj2",new Student2(1,"coco"));
%>
-
index3.jsp
重启服务器后,session将被活化,此时访问index3.jsp可以取到值。
<%--取值--%>
${obj2.name}
- 配置钝化活化
配置让session在一定时间内钝化。
三种方式:
- 在tomcat的conf/context.xml里面配置;
对所有的运行在这个服务器的项目生效。
- 在conf/Catalina/localhost/context.xml配置;
对localhost生效。 localhost:8080
- 在自己的web工程项目中的 META-INF/context.xml配置;
只对当前的工程生效。
maxIdleSwap:1分钟不用就钝化
directory:钝化后的那个文件存放的目录位置。D:\tomcat\apache-tomcat-7.0.52\work\Catalina\localhost\ListenerDemo\itheima
//itheima为相对路径,存放到了D:\tomcat\apache-tomcat-7.0.52\work\Catalina\localhost\ListenerDemo\itheima
2.Filter
过滤器,其实就是对客户端发出来的请求进行过滤,起到拦截作用。
作用:过滤敏感词汇;统一设置编码;自动登录等。
2.1如何使用Filter
- 定义一个类,实现Filter(注意是javax.servlet.Filter)
@WebFilter(filterName = "FilterDemo1",urlPatterns = "/*")//可简写成下面这行
//@WebFilter("/*")
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器1初始化了");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("来到了过滤器1");
//放行,请求会到达下一个目标
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("过滤器1被销毁了");
}
}
- 注册过滤器
两种注册方式:在web.xml中注册,或在类上用注解注册,见上。
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.itheima.Filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.2Filter的生命周期
- 创建
在服务器启动时就创建。
- 销毁
在服务器关闭时就销毁。
2.3Filter的执行顺序
- 客户端发出请求,先经过过滤器,如果过滤器放行,那么才能到servlet;
- 如果有多个过滤器,请求过来时,会按照在web.xml中注册的映射顺序(
)依次通过,只要其中任意一个不放行,那么后面的过滤器及servlet都不会收到请求,服务器响应回去时也会依次倒着经过过滤器。
2.4Filter细节
-
init方法中的FilterConfig参数可以用于获取Filter注册的名字及初始化参数,与ServletConfig类似;
-
Filter的拦截路径
,写法与servlet的一样: -
全路径匹配,以/开始
如/LoginServlet
-
目录匹配,以/开始,以*结束
/demo01/*
-
后缀名匹配,以*开始,以后缀名结束
.jsp,.html,*.do等
-
-
拦截规则也可针对dispatcher设置
filter-mapping> <filter-name>FilterDemo1</filter-name> <dispatcher>REQUEST</dispatcher> <!--只要是请求过来都拦截,默认就是REQUEST--> <dispatcher>FORWARD</dispatcher> <!--只要是转发都拦截--> <dispatcher>ERROR</dispatcher> <!--只要是页面出错跳转时就拦截--> <dispatcher>INCLUDE</dispatcher> <!--包含页面时就拦截--> </filter-mapping>
2.5自动登录案例
访问login.jsp页面不拦截,访问index2.jsp拦截,依次判断session和cookie,实现自动登录。
- login.jsp
<form action="LoginServlet" method="post" >
账号:<input type="text" name="username"><br/>
密码:<input type="text" name="password"><br/>
<input type="checkbox" name="autoLogin">自动登录<br/>
<input type="submit" value="登录">
</form>
- LoginServlet
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UserDao userDao=new UserDaoImpl();
User user = userDao.loginCheck(username, password);
if(user!=null){
//判断是否勾选了自动登录
String autoLogin = request.getParameter("autoLogin");
if("on".equals(autoLogin)){
//勾选了自动登录
Cookie cookie=new Cookie("info",username+"#"+password);
cookie.setMaxAge(60*60*24*7);
//cookie.setPath("/StuSysMvc");
cookie.setPath(request.getContextPath());//当前应用名
//发送cookie到客户端
response.addCookie(cookie);
}
//将用户信息存储到session
request.getSession().setAttribute("user",user);
response.sendRedirect("index2.jsp");
}else{
response.sendRedirect("login.jsp");
}
}
}
- index2.jsp
<body>
<c:if test="${not empty sessionScope.user}">
欢迎来自${sessionScope.user.address}的${sessionScope.user.username}!!!
</c:if>
<c:if test="${empty sessionScope.user}">
您好,请<a href="login.jsp">登录</a>!
</c:if>
</body>
- AutoLoginFilter
@WebFilter("/index2.jsp")
public class AutoLoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
//1.先判断session是否失效,如果失效再判断cookie
User sessionUser = (User) request.getSession().getAttribute("user");
if (sessionUser == null) {
//2.如果session失效,判断cookie
Cookie[] cookies = request.getCookies();
Cookie autoLoginCookie = CookieUtils.findCookie(cookies, "info");
if (autoLoginCookie != null) {
//session失效了,但cookie不为空,所以要重新登录
String value = autoLoginCookie.getValue();
String[] split = value.split("#");
UserDao userDao = new UserDaoImpl();
User user = userDao.loginCheck(split[0], split[1]);
//重新登录成功后,再把session放回request
request.getSession().setAttribute("user", user);
}
}
//放行到index2.jsp,在index2.jsp判断是否登录
filterChain.doFilter(request, servletResponse);
}
@Override
public void destroy() {
}
}
2.6BeanUtils
依赖jar包:
对于这种情况,提交上来的参数很多,而这些参数又恰好能封装成一个JavaBean,可以使用BeanUtils简化代码。
- 传统做法:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");//只针对post请求有效
String username = request.getParameter("username");
String password=request.getParameter("password");
String address=request.getParameter("addresss");
String phone=request.getParameter("phone");
String birthday=request.getParameter("birthday");
String email=request.getParameter("email");
User user=new User();
user.setUsername(username);
user.setPassword(password);
user.setAddress(address);
user.setPhone(phone);
user.setEmail(email);
try {
user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse(birthday));
} catch (ParseException e) {
e.printStackTrace();
}
}
- BeanUtils做法
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
//注册自己的日期转换器,将String类型的birthday转换为Date类型
ConvertUtils.register(new MyDateConverter(),Date.class);
User user=new User();
Map<String, String[]> parameterMap = request.getParameterMap();
try {
BeanUtils.populate(user,parameterMap);
} catch (Exception e) {
e.printStackTrace();
}
}
自定义时间转换器MyDateConverter:
public class MyDateConverter implements Converter {
@Override
// 将value 转换 c 对应类型
// 存在Class参数目的编写通用转换器,如果转换目标类型是确定的,可以不使用c 参数
public Object convert(Class c, Object value) {
String strVal = (String) value;
// 将String转换为Date --- 需要使用日期格式化
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = dateFormat.parse(strVal);
return date;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}