Filter

一、Filter过滤器

1、Filter什么是过滤器

1) Filter过滤器是JavaWeb的三大组件之一;三大组件分别是Servlet程序、Listener监听器、Filter过滤器

2) Filter过滤器是JavaEE的规范,也就是接口

3) Filter过滤器作用是:拦截请求、过滤响应。

拦截请求常见的应用场景有:
	① 权限检查
	② 日记操作
	③ 事务管理
		... ...等

2、Filter操作

要求:在web工程下,有一个admin目录,这个admin目录下的所有资源(html页面、jpg图片、jsp文件等)都必须是用户登陆之后才允许访问。

用户登录才允许访问的实现思路 ①

根据之前学过的内容,可以知道,用户登录之后都会把用户登录的信息保存到Session域中。所以要检查用户是否登录,可以判断Session中是否包含有用户登录的信息即可!!!

        存在局限性,只能在jsp页面去写,html页面或者图片不能实现。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
        <%
            Object user = session.getAttribute("user");
            //如果等于null,说明还没登录
            if (user==null){
                request.getRequestDispatcher("/login.jsp").forward(request,response);
                return;
            }
        %>
        b.jsp文件
</body>
</html>

用户登录才允许访问的实现思路 ②

采用Filter过滤器可以来实现

过滤器的功能实现如下所示:
在这里插入图片描述

类似于Servlet

public class AdminFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    // doFilter方法,专门用于拦截请求(可以做权限检查),过滤响应
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        HttpSession session = httpServletRequest.getSession();

        Object user = session.getAttribute("user");
        //如果等于null,说明还没登录
        if (user==null){
            servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
            return;
        }else {
            //让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }

    @Override
    public void destroy() {

    }
}

在web.xml文件中的配置如下: - - 其中 /admin/* 表示拦截此路径下的所有。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--同Servlet类似-->

    <!--filter标签用于配置一个Filter过滤器-->
    <filter>
        <!--给filter起一个别名-->
        <filter-name>AdminFilter</filter-name>
        <!--配置filter的全类名-->
        <filter-class>filter.AdminFilter</filter-class>
    </filter>

    <!--filter-mapping配置Filter过滤器的拦截路径-->
    <filter-mapping>
        <!--filter-name表示当前的拦截路径给哪个filter使用-->
        <filter-name>AdminFilter</filter-name>
        <!--url-pattern配置拦截路径
            /           表示请求地址为:http://ip:port/工程路径/            映射到IDEA的web目录
            /admin/*    表示请求地址为:http://ip:port/工程路径/admin/*     拦截全部
        -->
        <url-pattern>/admin/*</url-pattern>
    </filter-mapping>

</web-app>

3、完整的用户登录

AdminFilter实现类中doFilter方法拦截请求如下: /admin/* 拦截此路径下的所有

public class AdminFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    // doFilter方法,专门用于拦截请求(可以做权限检查),过滤响应
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        HttpSession session = httpServletRequest.getSession();

        Object user = session.getAttribute("user");
        //如果等于null,说明还没登录
        if (user==null){
            servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
            return;
        }else {
            //让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }

    @Override
    public void destroy() {

    }
}

login.jsp代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    这是login登录界面<br/>
    <form action="http://localhost:8080/15_filter/loginServlet" method="get">
        用户名:<input type="txet" name="username"><br/>
        密  码:<input type="password" name="password"><br/>
        <input type="submit" name="提交">
    </form>
</body>
</html>

登录的LoginServlet代码如下:

public class LoginServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        response.setContentType("text/html;charset=UTF-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if ("wzg168".equals(username) && "123456".equals(password)){
            request.getSession().setAttribute("user",username);
            response.getWriter().write("登录 成功!");
        }else {
            request.getRequestDispatcher("/login.jsp").forward(request,response);
        }
    }
}

解释如下:

由于拦截了admin路径下的所有,因此在不登录的前提下是不可以访问路径下的文件的。

访问login,jsp页面,进行登录请求,跳转到LoginServlet程序,获取请求参数,并判断用户名和密码是否与要求的一致,不一致重新输入,一致就登录成功,并赋值给Session。

当用户登录之后就可以访问之前被拦截的文件了,例如:http://localhost:8080/15_filter/admin/a.html		http://localhost:8080/15_filter/admin/b.jsp

4、Filter的生命周期

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

① 构造器方法
② init初始化方法

第①、②步,在web工程启动的时候执行(Filter已经创建)

③ doFilter过滤方法

第③步,每次拦截到请求,就会执行

④ destroy销毁

第④步,停止web工程的时候,就会执行(停止web工程,也会销毁Filter过滤器)	
public class AdminFilter implements Filter {

    public AdminFilter() {
        System.out.println("1、Filter构造器方法AdminFilter()");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("2、Filter的init(FilterConfig filterConfig)初始化");
    }

    // doFilter方法,专门用于拦截请求(可以做权限检查),过滤响应
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("3、Filter的doFilter()过滤方法");

        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        HttpSession session = httpServletRequest.getSession();

        Object user = session.getAttribute("user");
        //如果等于null,说明还没登录
        if (user==null){
            servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
            return;
        }else {
            //让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }

    @Override
    public void destroy() {
        System.out.println("4、Filter的destroy()销毁方法");
    }
}

5、FilterConfig类

FilterConfig类见名知义,它是Filter过滤器的配置文件类;Tomcat每次创建Filter的时候,也会同时创建一个FilterConfig类,这里包含了Filter配置文件的配置信息。

FilterConfig类的作用是获取filter过滤器的配置内容

① 获取Filter的名称filter-name的内容
② 获取Filter在web.xml中配置的init-param初始化参数
③ 获取ServletContext对象

web.xml文件加入init初始化参数配置如下:

	<!--配置初始化参数-->
        <init-param>
            <param-name>username</param-name>
            <param-value>root</param-value>
        </init-param>

        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost3306/test</param-value>
        </init-param>

下面再init方法中进行演示

public class AdminFilter implements Filter {

    public AdminFilter() {
        System.out.println("1、Filter构造器方法AdminFilter()");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("2、Filter的init(FilterConfig filterConfig)初始化");
        // 1、获取Filter的名称filter-name的内容
        System.out.println("filter-name的值是:"+filterConfig.getFilterName());
        // 2、获取在web.xml中配置的init-param初始化参数
        System.out.println("初始化参数username的值是:"+filterConfig.getInitParameter("username"));
        System.out.println("初始化参数url的值是:"+filterConfig.getInitParameter("url"));
        // 3、获取ServletContext对象
        System.out.println(filterConfig.getServletContext());
    }

    // doFilter方法,专门用于拦截请求(可以做权限检查),过滤响应
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("3、Filter的doFilter()过滤方法");

        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        HttpSession session = httpServletRequest.getSession();

        Object user = session.getAttribute("user");
        //如果等于null,说明还没登录
        if (user==null){
            servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
            return;
        }else {
            //让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }

    @Override
    public void destroy() {
        System.out.println("4、Filter的destroy()销毁方法");
    }
}

6、FilterChain过滤器链

Filter 过滤器
Chain 链,链条
FilterChain 就是过滤器链(多个过滤器如何一起工作)

多个Filter过滤器的底层工作原理
在这里插入图片描述

重点

1 FilterChain.doFilter()方法的作用

① 执行下一个Filter过滤器(如果有其他Filter的前提下)
② 执行目标资源(已经没有Filter的前提下)

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

3 多个Filter过滤器执行的特点:

① 所有filter和目标资源默认都执行在同一个线程中
② 多个Filter共同执行的时候,它们都使用同一个Request对象

Filter1代码如下:

public class Filter1 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("Filter1 前置代码");

        filterChain.doFilter(servletRequest,servletResponse);

        System.out.println("Filter1 后置代码");
    }

    @Override
    public void destroy() {

    }
}

Filter2代码如下:

public class Filter1 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("Filter2 前置代码");

        filterChain.doFilter(servletRequest,servletResponse);

        System.out.println("Filter2 后置代码");
    }

    @Override
    public void destroy() {

    }
}

target.jsp代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
        <%
            System.out.println("target.jsp页面执行了");
        %>
</body>
</html>

web.xml配置文件

<filter>
        <filter-name>Filter1</filter-name>
        <filter-class>filter.Filter1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Filter1</filter-name>
        <url-pattern>/target.jsp</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>Filter2</filter-name>
        <filter-class>filter.Filter2</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Filter2</filter-name>
        <url-pattern>/target.jsp</url-pattern>
    </filter-mapping>

执行http://localhost:8080/15_filter/target.jsp显示结果如下

Filter1 前置代码
Filter2 前置代码
target.jsp页面执行了
Filter2 后置代码
Filter1 后置代码

情况1:如果过滤器2的filterChain.doFilter()及其后置的代码不存在
	结果如下:
		Filter1 前置代码
		Filter2 前置代码
		Filter1 后置代码
情况2:如果过滤器1的filterChain.doFilter()及其后置的代码不存在
	结果如下:
		Filter1 前置代码

7、Filter的拦截路径

在这里插入图片描述


二、ThreadLocal的使用

1、TheadLocal的介绍

该类提供了线程局部(thread-local)变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其get或set方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal实例通常是类中的private static字段,它们希望将状态与某一个线程(例如:用户ID或事务ID)相关联。

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

ThreadLocal的作用

可以解决多线程的数据安全问题

ThreadLocal的特点

① ThreadLocal可以为当前线程关联一个数据(它可以像Map一样存取数据,key为当前线程)	

② 每一个ThreadLocal对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal对象实例

③ 每个ThreadLocal对象实例定义的时候,一般都是static类型

④ ThreadLocal中保存数据,在线程销毁后,会由JVM虚拟自动释放。

测试OrderDao类代码如下

public class OrderDao {

    public void SaveOrder(){
        String name = Thread.currentThread().getName();
        //System.out.println("saveOrder当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.data.get(name));
        System.out.println("saveOrder当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.threadLocal.get());
    }

}

测试OrderService类代码如下

public class OrderService {

    public void createOrder(){
        String name = Thread.currentThread().getName();
        //System.out.println("OrderService当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.data.get(name));
        System.out.println("OrderService当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.threadLocal.get());
        new OrderDao().SaveOrder();
    }
}

ThreadLocalTest类代码如下:(与普通Map赋键值进行对比

public class ThreadLocalTest {

    // public static Map<String,Object> data=new Hashtable<>();
    public static ThreadLocal<Object> threadLocal=new ThreadLocal<>();
    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();
        }
    }
}

2、使用Filter和TheadLocal组合管理事务

1)使用ThreadLocal来确保所有dao操作都在同一个Connection连接对象中完成

原理分析图:
在这里插入图片描述

2)使用Filter过滤器统一给所有的Service方法都加上try-catch。来进行实现的管理

原理分析图:
在这里插入图片描述
3将所有异常都统一交给Tomcat,让Tomcat展示友好的错误信息页面

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

	<!--error-page标签配置,服务器出错之后,自动跳转的页面-->
    <error-page>
        <!--error-code是错误类型-->
        <error-code>500</error-code>
        <!--location标签表示,要跳转去的页面路径-->
        <location>/pages/error/error500.jsp</location>
    </error-page>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值