• 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() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    三个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();
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    //开启事务
        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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    ③建立一个连接工具类,保证每一次事务的完整实现都是处在一个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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

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

    2.3 ThreadLocal介绍

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

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

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

    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是没有初始化的,那么第一次往其中添加数据时,会去初始化
         }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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();
         }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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) {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

        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容器创建失败");
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

  • 相关阅读:
    Scala网络编程:代理设置与Curl库应用实例
    智谱AI版Sora开源!首个可商用,在线可玩,5小时GitHub狂揽3.7K星
    Spring Boot 中是使用 JDK Proxy 动态代理还是 CGLib ?
    华为面试100题:java开发工程师(中)
    prim算法的实现
    Spring事务源码解读
    【C语言】指针的进阶(二)—— 回调函数的讲解以及qsort函数的使用方式
    CentOSPython使用openpyxl、pandas读取excel报错
    一种用于干式脑电图的高密度256通道电极帽
    常用半导体器件
  • 原文地址:https://blog.csdn.net/weixin_44606952/article/details/125510214