Javaweb开发了解前端知识十二、Filter过滤器

1Filter

1.1、什么是Filter

1.2、举例说明Filter

1.3Filter的生命周期(与Servlet的生命周期比较)

1.4FilterConfig

1.5FilterChain 过滤器链

2.ThreadLocal的使用


1Filter

1.1、什么是Filter

1、首先Filter是一个接口。

2、Filterjava Web三大组件之一。   javaWeb三大组件分别是:Servlet小程序、Filter过滤器、Listener监听器

3、Filter是服务器专门用来过滤请求,拦截响应的。

 

 

Filter的常见作用:

1、检查用户访问权限。

2、设置请求响应编码,解决乱码问题。

 

 

1.2、举例说明Filter

需求:

现在在WebContent目录下有一个目录admin。这个目录是管理员操作的目录。这个目录里有jsp文件,有html文件,还有图片资源文件。现在我们要让这些资源都在用户登录才能被访问。那么我们要怎么实现这样的需求。

思路:

Session的局限:有人可能会想,我们可以在用户登录之后。把用户的信息保存在Session域对象中。然后在jsp页面里通过Session域对象获取用户的信息,如果用户信息存在,说明用户已登录。否则就重定向到登录页面。这个方案可行。可是html页面呢? html页面是没有Session域对象的。或者访问一张图片呢?如何在访问之前拦截呢?

解决方案:

这就需要我们使用Filter过滤器来进行请求的拦截。然后判断Session域对象中是否包含用户的信息。

现在我们以admin目录下user.jsp为例进行讲解。

 

1、首先,我们需要创建一个类来实现Filter接口,用来检查Session中是否包含用户信息。

2、实现Filter中的doFilter方法

3、然后到web.xml文件中去配置Filter的过滤信息。

4、然后重启服务器访问测试


登录页面:

<body>
		这是登录页面
		<form action="userServlet" method="post">
			用户名:<input name="username" /><br/>
			密 码:<input name="password" type="password" /><br/>
			<input type="submit" />
		</form>
	</body>

Filter过滤器

public class AdminFilter implements Filter {

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		// 需要强转后使用。
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		// 获取Session对象
		HttpSession session = httpRequest.getSession();
		// 从Session中获取用户登录的信息
		Object user = session.getAttribute("user");
		if (user == null) {
			// 说明用户还没有登录
			System.out.println("Filter中检查,用户没有登录。跳回登录页面");
			httpResponse.sendRedirect(httpRequest.getContextPath()
					+ "/login.html");
			return;
		} else {
			// 检查通过。放行
			chain.doFilter(request, response);
		}
	}

}

web.xml中的配置:

 <filter>
    <filter-name>AdminFilter</filter-name>
    <filter-class>com.tcent.filter.AdminFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>AdminFilter</filter-name>
    <url-pattern>/admin/*</url-pattern>
  </filter-mapping>

1.3Filter的生命周期(与Servlet的生命周期比较)

Servlet的生命周期

1.先执行构造方法

2.执行init方法做初始化操作

3.执行Service方法

4.销毁的时候调用destory方法

1)第一次访问的时候初始化 构造方法与init方法,且只进行一次

2)在访问servlet的时候调用Service方法

3Tomcat关闭Service被销毁的时候调用destory方法

Filter生命周期:

1、先执行Filter的构造方法

2、然后执行Filterinit方法

3、执行FilterdoFilter方法,每次访问资源,只要匹配过滤的地址,就会调用。

4、执行Filterdestroy方法

1Filter在工程启动的时候初始化。构造方法与init方法,且只进行一次

2)在访问过滤的时候调用doFilter方法

3Tomcat关闭Filter被销毁的时候调用destory方法

 

创建一个Filter2类。代码如下:

public class Filter2 implements Filter {

	public Filter2() {
		System.out.println("Filter2 构造 方法 被调用");
	}
	
	/**
	 * Filter初始化方法
	 */
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("Filter2 init 方法被调用。初始化……");
	}

	/**
	 * Filter的过滤方法
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("Filter2 doFilter 方法被调用  ");
		// 一定要调用此方法,否则用户访问的资源会访问不到。
		chain.doFilter(request, response);
	}

	/**
	 * Filter销毁的方法
	 */
	public void destroy() {
		System.out.println("Filter2 的destroy方法被调用……");
	}

}

1.4FilterConfig

作用:FilterConfig类和ServletConfig类是一样的。可以获取Filterweb.xml文件中的配置信息,做初始化之用。

我们可以在web.xml文件中给Filter添加初始化参数。然后在init初始化方法中使用FilterConfig类获取到初始化的参数。

 

FilterConfig类,一般有三个作用:

1、获取Filterweb.xml文件中配置的名称

2、获取Filterweb.xml文件中配置的初始化参数

3、通过FilterConfig类获取ServletContext对象实例

 

1.4.1、修改Filter2web.xml中的配置信息

<!-- 配置Filter2 -->
	<filter>
		<!-- 给Filter2起一个名字 -->
		<filter-name>Filter2</filter-name>
		<!-- 是哪一个Filter类 -->
		<filter-class>com.tcent.filter.Filter2</filter-class>
		<!-- 配置初始化参数 -->
		<init-param>
			<!-- 初始化参数的名称 -->
			<param-name>username</param-name>
			<!-- 初始化参数的值 -->
			<param-value>root</param-value>
		</init-param>
	</filter>

1.4.2、修改Filter2init方法的代码如下:

/**
	 * Filter初始化方法
	 */
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("Filter2 init 方法被调用。初始化……");
		// 获取Filter的名称
		String filterName = filterConfig.getFilterName();
		System.out.println("Filter name ==>>> " + filterName);
		// 获取初始化参数。username的值
		String username = filterConfig.getInitParameter("username");
		System.out.println("username ==>> " + username);
		// 获取ServletContext的对象实例 
		ServletContext ctx = filterConfig.getServletContext();
		System.out.println(ctx);
	}

1.4.3、然后重启Tomcat服务器,控制台打印如下:


1.5FilterChain 过滤器链

FilterChain 过滤器链

Filter是过滤器

chain是链

FilterChain是过滤器链

 

FilterChain是整个Filter过滤器的调用者。FilterFilter之间的传递,或者Filter与请求资源之间的传递都靠FilterChain.doFilter方法。

一般Filter.doFilter中的代码分为三段。

第一段是FilterChain.doFilter之前的代码。一般用来做请求的拦截,检查用户访问的权限,访问日记的记录。参数编码的设置等等操作。

第二段是FilterChain.doFilter方法。此方法可以将代码的执行传递到下一个Filter中。或者是传递到用户最终访问的资源中。

第三段是FilterChain.doFilter之后的代码。主要用过做一些日记操作。我们很少会在第三段中做太多复杂的操作。

 

在每一个Filter类的doFilter方法中,一定要调用chain.doFilter方法,除非你想要阻止用户继续往下面访问。否则一定要调用FilterChaindoFilter方法。

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

 

图解:多个Filter过滤器的代码流转


Filter的拦截路径

--精确匹配

比如: /admin/a.jsp 它表示只有请求地址是:http://ip:port/工程名/admin/a.jsp的时候,Filter过滤器才会拦截。

 

--目录匹配

比如: /admin/* 它表示只有请求地址是: http://ip:port/工程名/admin/* 的时候,Filter过滤器才会拦截。

 

--后缀名匹配

比如:

*.html 表示请求地址必须以html结尾,Filter才会拦截到。

*.jsp 表示请求地址必须以jsp结尾。filter才会拦截到。

*.action 表示请求地址必须以 action结尾。Filter才会拦截到。

*.do 表示请求地址必须以do结尾。Filter过滤器才会拦截到

*.abc 请求地址必须以abc结尾,Filter过滤器才会拦截到。

 

 

Filter只关心请求地址。不关闭资源是不是存在。

 

千万要注意:Filter类的doFilter方法中,除非你要拦截请求的资源,否则一定要调用FilterChain参数的doFilter方法让代码的执行传递到下一个Filter或访问的资源中

2.ThreadLocal的使用

1、它可以像map一样存取数据。key永远是当前线程对象。

2、一般情况ThreadLocal类实例的时候,都是static类型。

3、在ThreadLocal对象实例中保存的数据。只要线程销毁了。虚拟机JVM会自动的释放对应的数据。

 

 

注:Threadlocal可以像map一样存取数据。在一个线程中,不管代码调用层级有多少层。只要是在ThreadLocal同一个线程中都可以取出之前在ThreadLocal中保存的数

我们先来看一下。在线程里保存变量,然后在线程中取自己保存的变量的情况

 

1map来实现线程保存变量:

public class TestThreadLocal1 {

	// 定义一个整型
	private static Map<String, Integer> map = new HashMap<String, Integer>();
	// 随机数对象
	private static Random random = new Random(System.currentTimeMillis());

	static class MyTask implements Runnable {

		Map<String, Integer> map;

		public MyTask(Map<String, Integer> map) {
			super();
			this.map = map;
		}

		public void run() {
			System.out.println(Thread.currentThread().getName() + " -- begin");

			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 生成随机数,用于相加
			int num = random.nextInt(1000);
			//
			System.out.println(Thread.currentThread().getName() + "生成一个随机数:" + num);
			// 把随机数保存到map中
			map.put(Thread.currentThread().getName(), num);
			// 用线程做key获取自己的变量
			System.out.println("获取" + Thread.currentThread().getName() + " -- "
					+ map.get(Thread.currentThread().getName()));
			System.out.println(Thread.currentThread().getName() + " -- end");
		}
	}

	public static void main(String[] args) throws Exception {
		Thread t1 = new Thread(new MyTask(map));
		Thread t2 = new Thread(new MyTask(map));
		t1.start();
		t2.start();
	}

}

2)ThreadLocal实现线程保存变量

public class TestThreadLocal2 {

	// 定义一个整型
	private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
	// 随机数对象
	private static Random random = new Random(System.currentTimeMillis());

	static class MyTask implements Runnable {

		ThreadLocal<Integer> i;

		public MyTask(ThreadLocal<Integer> i) {
			this.i = i;
		}

		public void run() {
			System.out.println(Thread.currentThread().getName() + " -- begin");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 生成随机数,用于相加
			int num = random.nextInt(1000);
			//
			System.out.println(Thread.currentThread().getName() + "生成一个随机数:" + num);
			// 把随机数保存到map中
			i.set(num);
			// 用线程做key获取自己的变量
			System.out.println("获取" + Thread.currentThread().getName() + " -- " + i.get());
			System.out.println(Thread.currentThread().getName() + " -- end");
		}
	}

	public static void main(String[] args) throws Exception {
		Thread t1 = new Thread(new MyTask(threadLocal));
		Thread t2 = new Thread(new MyTask(threadLocal));
		t1.start();
		t2.start();
	}

}

经过上面两个小示例的代码,我们可以知道,,在多个线程里我们可以使用ThreadLocal保存线程自己需要的变量而不需要担心线程安全的问题所以我们只可以使用ThreadLocal来保存数据库的连接(与filter组合管理事务),这样在一次请求中。是一个线程处理所有的操作。使用FilterThreadLocal组合来控制事务,这样可以保正一个请求,使用相同的Connection对象。可以确保多个操作在一个连接的一个事务中完成。来达到。要么都成功 。要么都失败的效果。

 Javaweb开发了解前端知识十一、Cookie 和 Session





猜你喜欢

转载自blog.csdn.net/mxcsdn/article/details/80634818