JavaWeb学习笔记(五)Filter、ThreadLocal

1.Filter

1.1 介绍

  1. Filter过滤器是JavaWeb的三大参数之一。三大组件分别是:Servlet程序,Listener监听器,Filter过滤器。
  2. Filter过滤器是JavaEE的规范,也就是接口。
  3. Filter过滤器的作用是拦截请求,过滤响应。
  4. 拦截器的常见应用场景:①权限检查②日记操作③事务管理…

1.2 Filter简单实现

1.2.1 场景引入

在这里插入图片描述

1.2.2 代码展示

1.AdminFilter

public class AdminFilter implements Filter {
    @Overide
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Overide
    //用于拦截请求,可以做权限检查
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpSession session = req.getSession();
        if(session.getAttribute("username") == null){
            servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
            return;
        }else {
            //让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Overide
    public void destroy() {
    
    }
}

2.web.xml中配置

<filter>
    <filter-name>AdminFilter</filter-name>
    <filter-class>com.test.filter.AdminFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>AdminFilter</filter-name>
    <url-parttern>/admin/*</url-parttern>
</filter-mapping>

filter-name:过滤器别名,一般为类名
filter-class:全类名
filter-mapping:配置过滤器的拦截路径
url-parttern:配置拦截路径        “/”对应http://ip:port/工程路径/        映射到IDEA的web目录

1.2.3 Filter实现步骤

  1. 编写一个类去实现Filter接口
  2. 实现过滤方法doFilter()
  3. 到web.xml中去配置Filter的拦截路径

1.3 Filter生命周期

Filter的生命周期包含以下几个方法

  1. 构造方法
  2. init初始化方法
  3. doFilter过滤方法
  4. destroy销毁方法

(1)第1、2步在Web工程启动就已执行,即Filter在工程启动时就创建
(2)第3步在每次拦截到请求就会执行
(3)第4步在停止Web工程时就会执行,即停止工程就会销毁Filter

1.4 FilterConfig类

1.4.1 介绍

FilterConfig类是Filter过滤器的配置文件。
Tomcat每次创建Filter类时,同时也会创建对应的FilterConfig类,包含了Filter配置文件的配置信息。

FilterConfig类的作用是获取filter过滤器的配置内容
1.获取Filter的名称即filter-name的内容                         FilterConfig.getFilterName()
2. 获取在web.xml中配置的init-param初始化参数           FilterConfig.getInitiParameter()
3. 获取ServletContext对象                                      FilterConfig.getServletContext();

1.5 FilterChain 过滤器链

1.5.1 多过滤器工作场景

FilterChain就是过滤器链,多个过滤器是如何工作的。

1.正常工作场景
在这里插入图片描述
2.去掉Filter2的doFilter()方法
在这里插入图片描述
没有doFilter方法,就不会继续执行请求的页面,会立即返回。
如删除Filter2的doFilter()方法,就会执行“Filter1的前置代码→Filter2的前置代码→Filter1的后置代码”。
如果删除Filter1的doFilter()方法,就会执行“Filter1的前置代码”。

3.多个Filter的执行顺序
在多个Filter过滤器执行过程中,它们执行的优先顺序是由在web.xml中从上到下配置的顺序决定的。

4.多个Filter过滤器执行的特点
(1)所有Filter和目标资源默认都执行在同一个线程中
(2)多个Filter共同执行的时候,它们都使用同一个Request对象

1.6 Filter的拦截路径

Filter过滤器有三种匹配方式。

  1. 精确配置
    <url-param>/target.jsp</url-param>
    以上配置的路径,请求地址必须为:http://ip:port/工程路径/target.jsp
  2. 目录配置
    <url-param>/admin/</url-param>
    以上配置的路径,请求地址必须为:http://ip:port/工程路径/admin/
  3. 后缀名配置
    <url-param>*.html</url-param>
    以上配置的路径,表示请求地址必须以.html结尾才会拦截到

Filter过滤器只关心请求的地址是否匹配,不关心请求资源是否存在。

1.7 使用Filter过滤器实现权限管理

1.7.1 使用Filter过滤器拦截/pages/manager/下的所有内容,实现全线检查

1.ManagerFilter

public class ManagerFilter implements Filter {
    @Overide
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Overide
    //用于拦截请求,可以做权限检查
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        Object obj = HttpSession session = req.getSession();
        if(obj == null){
            servletRequest.getRequestDispatcher("pages/user/login.jsp").forward(servletRequest, servletResponse);
            return;
        }else {
            //让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Overide
    public void destroy() {
    
    }
}

2.web.xml

<filter>
    <filter-name>ManagerFilter</filter-name>
    <filter-class>com.test.filter.ManagerFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>ManagerFilter</filter-name>
    <url-pattern>/pages/manager/*</filter-param>
    <url-pattern>/manager/bookServlet</filter-param>
</filter-mapping>

urlPattern可以有多个,如果要限制Servlet,就限制该Servlet所映射到的访问地址。

1.7.2 ThreadLocal的使用

  1. ThreadLocal的作用,它可以解决多线程的数据安全问题。

  2. ThreadLocal它可以给当前线程关联一个数据(可以是普通变量、对象、数组或集合)。

  3. ThreadLocal的特点:
    (1)ThreadLocal可以为当前线程关联一个数据。(可以像Map一样存取数据)
    (2)每个ThreadLocal对象只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal对象实例。
    (3)每个ThreadLocal对象实例定义的时候,一般都是static类型。
    (4)ThreadLocal中保存数据,在线程销毁后,会由JVM虚拟机自动释放。

  4. 代码实现

package com.test;

import java.util.Hashtable;
import java.util.Map;
import java.util.Random;

public class ThreadLocalTest {
//    public static Map<String,Object> data = new Hashtable<String, Object>();
    public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
    private static Random random = new Random();

    public static class Task implements Runnable {
        @Override
        public void run() {
            //在run方法中,随机生成一个变量(线程要关联的数据),以线程名为key保存到map中
            Integer i = random.nextInt(1000);
            String name = Thread.currentThread().getName();
            System.out.println("线程["+ name +"]生成的随机数是:" + i);
//            data.put(name,i);
            threadLocal.set(i);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new OrderService().createOrder();

            //run方法结束之前,以当前线程名获取出数据并打印,查看是否可以取出操作
//            Object o = data.get(name);
            Object o = threadLocal.get();
            System.out.println("线程["+ name +"]快结束时取出的关联的数据是:" + o);
        }
    }

    public static void main(String[] args) {
        for(int i = 0; i < 3; i++){
            new Thread(new Task()).start();
        }
    }
}

1.7.3 使用Filter和ThreadLocal组合管理事务

事务概念

1.如结算一个订单,需要经历保存订单→发起付款→减扣库存等环节,这些环节必须要执行全部执行,如果中间一个环节失败则会出问题,这种情况叫做事务。
2.JDBC的数据库事务管理

Connection conn = JdbcUtils.getConnection();
try {
    conn.setAutoCommit(false);      //设置为手动管理事务
    //执行一系列的jdbc操作
    conn.commit();                  //手动提交事务
} catch (Exception e) {
    conn.rollback();                //回滚事务
} finally{
    JdbcUtils.close(conn);          //关闭连接
}

(1)要确保所有事务要么都成功,要么都失败,就必须要使用数据库的事务。
(2)要确保所有操作都在一个事务内,就必须要确保所有操作都使用同一个Connection对象。
(3)我们可以使用ThreadLocal对象来确保所有操作都使用同一个Connection连接对象,那么操作的前提条件是所有操作都必须要在同一个线程内完成。

使用ThreadLocal管理事务

1.修改JdbcUtils

public class JdbcUtils{

    private static DruidDataSource dataSource;
    private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
    
    
    static{
        
        try{
            Properities properities = new Properities();
            //读取jdbc.properities配置文件
            InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properities"); 
            //从流中加载数据
            properities.load(inputStream);
            //创建数据库连接池
            dataSource = (DruidDataSource) new DruidDataSourceFactory.createDataSource(properities);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

     /**
     * 获取数据库连接池中的连接
     * @return
     */
    public static Connection getConnection(){
        Connection conn = conns.get();
        if(conn == null) {
            try{
                conn = dataSource.getConnection();
                conns.set(conn);              //保存到ThreadLocal对象中
                conn.setAutoCommit(false);    //设置为手动提交事务
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return conn;
    }
    
    /**
     * 提交事务并关闭连接
     */   
    public static void commitAndClose() {
        Connection conn = conns.get();
        if(conn != null) {            //如果不为空,说明之前使用过连接操作过数据库
            try {
                conn.commit();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    conn.close();         //关闭连接,释放资源
                } catch (Exception e) {
                    e.printStackTrace();
                } 
            }
        }
        conns.remove();       //一定要执行remove操作,否则就会出错(因为Tomcat服务器底层使用了线程池)
    }

    /**
     * 回滚事务并关闭连接
     */   
    public static void rollbackAndClose() {
        Connection conn = conns.get();
        if(conn != null) {            //如果不为空,说明之前使用过连接操作过数据库
            try {
                conn.rollback();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    conn.close();         //关闭连接,释放资源
                } catch (Exception e) {
                    e.printStackTrace();
                } 
            }
        }
        conns.remove();       //一定要执行remove操作,否则就会出错(因为Tomcat服务器底层使用了线程池)
    }
    
}

2.修改各个Dao
(1)把finally中的关闭连接删除掉
(2)catch到异常必须抛出,否则service拿不到,无法回滚

3.修改Servlet(调用Service的地方)
try{
①执行service方法
②commitAndClose()提交事务
}catch(Exception e){
①rollbackAncClose()回滚事务
}

使用Filter过滤器统一给所有Service方法都加上try-catch

在这里插入图片描述
1.TransactionFilter

public class TransactionFilter implements Filter {
    @Overide
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Overide
    //用于拦截请求,可以做权限检查
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
             filterChain.doFilter(servletRequest,servletResponse);
             JdbcUtils.commitAndClose();
        }catch (Exception e) {
            JdbcUtils.rollbackAncClose();
            e.printStackTrance();
        }
    }

    @Overide
    public void destroy() {
    
    }
}

2.web.xml

<filter>
    <filter-name>TransactionFilter</filter-name>
    <filter-class>com.test.filter.TransactionFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>TransactionFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
使用Tomcat统一管理异常,展示错误信息页面

1.在web.xml中可以通过错误页面配置来进行管理

<error-page>
    <error-code>500</erroe-code>
    <location>/pages/error/error500.jsp</location>
</error-page>

error-page:配置服务器出错之后自动跳转的页面
error-code:错误类型
location:要跳转到的页面路径

2.在Filter中要再把异常抛出给Tomcat服务器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值