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.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的值可以存储到硬盘上【钝化】,等下一次使用时,再从硬盘上提取出来【活化】。

  1. 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被活化了");
    }
   ...
}
  1. index2.jsp

    index2.jsp将Student2存入到session,然后正常关闭服务器,session 将被钝化。

<%
    //存入到session
    session.setAttribute("obj2",new Student2(1,"coco"));
%>
  1. index3.jsp

    重启服务器后,session将被活化,此时访问index3.jsp可以取到值。

<%--取值--%>
${obj2.name}
  • 配置钝化活化

配置让session在一定时间内钝化。

三种方式:

  1. 在tomcat的conf/context.xml里面配置;

对所有的运行在这个服务器的项目生效。

  1. 在conf/Catalina/localhost/context.xml配置;

对localhost生效。 localhost:8080

  1. 在自己的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

  1. 定义一个类,实现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被销毁了");
    }
}
  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的执行顺序

  1. 客户端发出请求,先经过过滤器,如果过滤器放行,那么才能到servlet;
  2. 如果有多个过滤器,请求过来时,会按照在web.xml中注册的映射顺序()依次通过,只要其中任意一个不放行,那么后面的过滤器及servlet都不会收到请求,服务器响应回去时也会依次倒着经过过滤器。

2.4Filter细节

  1. init方法中的FilterConfig参数可以用于获取Filter注册的名字及初始化参数,与ServletConfig类似;

  2. Filter的拦截路径,写法与servlet的一样:

    • 全路径匹配,以/开始

      如/LoginServlet

    • 目录匹配,以/开始,以*结束

      /demo01/*

    • 后缀名匹配,以*开始,以后缀名结束

      .jsp,.html,*.do等

  3. 拦截规则也可针对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;
	}
}
posted @ 2020-10-25 21:35  ALiWang1123  阅读(149)  评论(0编辑  收藏  举报