JavaWeb中篇Filter-Transaction-Listener笔记

说明:

本篇文章主要记录的是filter(过滤器)、事务管理、监听器Listener的介绍和使用。

1. 过滤器Filter的介绍和使用

1.1 过滤器Filter概述

文字描述

    Filter也属于Servlet规范,中文名称就是过滤器,通俗的说它的作用就是在项目启动的时候,首先会进行拦截,将需要执行的内容先执行完毕之后,放行(可以继续访问服务),在经过一系列的执行之后又会返回回来,执行过滤器中放行后设置的程序

图片描述

在这里插入图片描述

说明:

①Filter开发步骤新建类实现Filter接口,然后实现其中的三个方法:initdoFilterdestroy
②配置Filter,可以用注解@WebFilter,也可以使用xml文件 <filter> <filter-mapping>
③Filter在配置时,和servlet一样,也可以配置通配符,例如 @WebFilter(“*.do”)表示拦截所有以.do结尾的请求
④过滤器链【上图所示
    1)执行的顺序依次是: A B C demo03 C2 B2 A2
    2)如果采取的是注解的方式进行配置,那么过滤器链的拦截顺序是按照全类名的先后顺序排序的
    3)如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序

1.2 过滤器使用实例

首现创建三个过滤器

Filter01

@WebFilter("*.do")
public class Filter01 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("A");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("A2");
    }

    @Override
    public void destroy() {

    }
}

Filter02

@WebFilter("*.do")
public class Filter02 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("B");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);

        System.out.println("B2");
    }

    @Override
    public void destroy() {

    }
}

Filter03

@WebFilter("*.do")
public class Filter03 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("C");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("C2");
    }

    @Override
    public void destroy() {

    }
}

三个Filter形成一个过滤器链
    执行的结果图(在默认的情况下按照名称进行过滤,但是在配置文件XML设置时按照配置的先后顺序进行过滤)
在这里插入图片描述

2. 事务管理

2.1 事务概述

    事务,这个名词不是新鲜的知识点了,在学习数据库的时候就学习过事务,事务的含义是在执行的一件事的完整过程。例如:最经典的案例就是:转账,一个发钱,一个收钱。不可能是一个发钱没人收钱,不符合要求,另一种一个收钱,没人发钱也不行。所有两个过程只要有一个不能执行成功,就算事情没有成功,需要进行回滚操作【也就是恢复到刚开始的状态】

在这里插入图片描述
    通过上图可以了解到:事务管理不能以DAO层的单精度方法为单位,而应该以业务层的方法为单位。

    在实际的实现中我们可以在访问Servlet前,建立一个Filtter,建立过滤器之后,就可以实现建立一个连接开启事务,然后放行,在执行完毕所有的事务之后,就可以提交事务,只要在执行的过程中出现错误就会被catch捕捉到,然后执行事务的回滚
    实际开发中进行事务管理的方法
在这里插入图片描述

2.2 事务管理的实现

实现所需的项目仍然以说过后台存储管理系统为例:

实现过程简述:首先设置拦截器,来到事务管理,之后掉用新建链接的方法(通过ThreadLocal实现),修改BaseDAO中的内容

实现步骤:
①建立拦截器(建立一个类实现Filter接口),执行doFilter方法

@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        try {
            //开启事务
            TransactionManager.beginTrans();
            System.out.println("开启事务...");
            //放行
            filterChain.doFilter(servletRequest,servletResponse);
            //执行回来之后进行提交
            TransactionManager.commit();
            System.out.println("提交事务...");
        }catch (Exception e){
            e.printStackTrace();
            try {
                TransactionManager.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
    }

②建立事务管理类,在这里实现三个方法:开启事务,提交事务,和回滚事务

//开启事务
    public static void beginTrans() throws SQLException {
        //如果获取到连接,直接设置取消自动提交
        ConnUtil.getConn().setAutoCommit(false);
    }

    //提交事务
    public static void commit() throws SQLException {
        Connection conn = ConnUtil.getConn();
        conn.commit();
        ConnUtil.closeConn();
    }

    //回滚事务
    public static void rollback() throws SQLException {
        Connection conn = ConnUtil.getConn();
        conn.rollback();
        ConnUtil.closeConn();
    }

③建立一个连接工具类,保证每一次事务的完整实现都是处在一个Connection中,在这里需要注意使用建立连接的属性是ThreadLocal(本地线程),建立连接工具类可以让其他需要用到的直接调用方法即可,不用新建连接,保障了使用的是同一个连接

    //通过ThreadLocal的方法获取连接
    static ThreadLocal<Connection>  threadLocal = new ThreadLocal<>();
    //从BaseDAO中过来的
    public static Connection createConn(){
        try {
            //1.加载驱动
            Class.forName(DRIVER);
            //2.通过驱动管理器获取连接对象
            return DriverManager.getConnection(URL, USER, PWD);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return null ;
    }
    public static Connection getConn(){
        Connection conn = threadLocal.get();
        if (conn==null){
            //没有获取到连接的情况下,就调用创建连接的方法
            conn = createConn();
            threadLocal.set(conn);
        }
        //如果为空的话再获取一此连接
        return threadLocal.get();
    }

    public static void closeConn() throws SQLException {
        Connection conn = threadLocal.get();
        if (conn==null){
            //没有获取到连接的情况下,直接return
            return;
        }
        if (!conn.isClosed()){
            conn.close();
            threadLocal.set(null);
        }
    }

BaseDAO中也不再需要建立连接,做出以下修改:
在这里插入图片描述

2.3 ThreadLocal介绍

了解内容【只需要知道set和get方法,使用泛型来确定类型即可】

    ThreadLocal称之为本地线程 。 我们可以通过set方法在当前线程上存储数据、通过get方法在当前线程上获取数据,内部可以使用泛型,来实现同一个线程上都是用一个Connection。一共有两个方法:set()和get()方法即为:一个设置一个获取

ThreadLocal<Connection>  threadLocal = new ThreadLocal<>();

set方法源码分析:

public void set(T value) {
         Thread t = Thread.currentThread(); //获取当前的线程
         ThreadLocalMap map = getMap(t);    //每一个线程都维护各自的一个容器(ThreadLocalMap)
         if (map != null)
             map.set(this, value);          //这里的key对应的是ThreadLocal,因为我们的组件中需要传输(共享)的对象可能会有多个(不止Connection)
         else
             createMap(t, value);           //默认情况下map是没有初始化的,那么第一次往其中添加数据时,会去初始化
     }

get方法源码分析:

 public T get() {
         Thread t = Thread.currentThread(); //获取当前的线程
         ThreadLocalMap map = getMap(t);    //获取和这个线程(企业)相关的ThreadLocalMap(也就是工作纽带的集合)
         if (map != null) {
             ThreadLocalMap.Entry e = map.getEntry(this);   //this指的是ThreadLocal对象,通过它才能知道是哪一个工作纽带
             if (e != null) {
                 @SuppressWarnings("unchecked")
                 T result = (T)e.value;     //entry.value就可以获取到工具箱了
                 return result;
             }
         }
         return setInitialValue();
     }

3. 监听器

3.1 监听器的分类

名称说明
ServletContextListener监听ServletContext对象的创建和销毁的过程
HttpSessionListener监听HttpSession对象的创建和销毁的过程
ServletRequestListener监听ServletRequest对象的创建和销毁的过程
ServletContextAttributeListener监听ServletContext的保存作用域的改动(add,remove,replace)
HttpSessionAttributeListener监听HttpSession的保存作用域的改动(add,remove,replace)
ServletRequestAttributeListener监听ServletRequest的保存作用域的改动(add,remove,replace)
HttpSessionBindingListener监听某个对象在Session域中的创建与移除
HttpSessionActivationListener监听某个对象在Session域中的序列化和反序列化

3.2 监听器的简单使用

    使用到的是:ServletContextAttributeListener,用于实现在项目启动的时候就创建beanFactory(实现中央处理器中冗余和控制反转)

监听器:

@WebListener
public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext();
        ServletContext application = servletContextEvent.getServletContext();
        application.setAttribute("beanFactory",beanFactory);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

中央处理器做出的改变如下:

    public void init() throws ServletException {
        super.init();

//        beanFactory = new ClassPathXmlApplicationContext();
        //使用之后会在Tomcat启动的时候就实现IOC(控制反转)和依赖注入
        ServletContext application = getServletContext();
        Object beanFactoryObj = application.getAttribute("beanFactory");
        if (beanFactoryObj!=null){
            beanFactory = (BeanFactory) beanFactoryObj;
        }else {
            throw new RuntimeException("IOC容器创建失败");
        }

    }

简单总结就是:一个进行设置,一个进行获取(Set和Get)太晚了,就做这点说明吧…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮皮皮皮皮皮皮卡乒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值