现在聊一下javaweb三大核心(servlet 程序 Listener 监听器 Filter过滤器)之一的过滤器。
过滤器本身也是javaee中的一个接口,而其主要作用就是:拦截请求,过滤响应。
用一个不十分恰当的例子来说,过滤器像是净水器中过滤芯片,水过滤片的时候都会被拦住,然后将水中杂质给阻挡到出水口,而纯净的水可以通过。
所以网站很多时候通过过滤器进行权限检查比如最常见的购物网站,如果不登陆就无法打开购物车等。
这个就有一个问题,不是jsp支持编程脚本吗,我直接如下判断不就行了:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
String name= (String) session.getAttribute("username");
if (name!=null && name.equals("")){
request.getRequestDispatcher("/资源路径").forward(request,response);
}
%>
</body>
</html>
完美解决了问题了,这样干嘛还需要写一个过滤器,还需与实现过滤器接口,然后创建一个java文件呢。但是不要忽略一点,对于忘了url请求不单单是jsp,比如图片,还有pdf等格式,你如何在其内容中写java编程呢。
现在又有一个大胆的想法了,既然如此那我可以在servlet中判断一下不久可以了。还是这个例子似乎解决如下:
@WebServlet("/test")
public class test extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name= (String) req.getSession().getAttribute("username");
if (name!=null && name.equals("")){
// 通过service服务逻辑得到资源
}else{
req.getRequestDispatcher("/登录路径").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
虽然可以解决问题,但是有一个问题,那就是需要在每个servlet中写一遍是不是很麻烦,当然还有一种写法,就是写一个baseservlet,在这个方法里面写,然后其它的方法继承这个方法:
baseservlet类文件如下:
public class baseservlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// url 对方法进行重新命名,而不适用dopsost或者doget
String action = req.getParameter("action");
try {
String name = (String) req.getSession().getAttribute("username");
if (name != null && name.equals("")) {
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this, req, resp);
} else {
req.getRequestDispatcher("/登录路径").forward(req, resp);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
然后写一个继承类即可不过这样写也有问题,虽然可以解决问题,但是有时候发现这个过滤会有大量需求,那么为什么不提炼处理一个类,然后直接调用即可,没必要每次都手写一个,而且还有一个就是对于一些拦截的路径也可以使用类似正则表达式的方式拦截。
所以过滤器这个东西的意义就有了,比如在配置的时候:
<filter>
<filter-name>testFiltersfilter-name>
<filter-class>com.test.testFiltersfilter-class>
filter>
<filter-mapping>
<filter-name>testFiltersfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
先看一个资源:
然后写一个过滤器,然后配置:
package com.test;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public class testFilters implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest httprequest= (HttpServletRequest) request;
String name = (String) httprequest.getSession().getAttribute("username");
System.out.println(name);
if (name != null && !(name.equals(""))) {
chain.doFilter(request,response);
} else {
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write("请登录才行");
}
}
}
然后进行配置:
<filter>
<filter-name>testFiltersfilter-name>
<filter-class>com.test.testFiltersfilter-class>
filter>
<filter-mapping>
<filter-name>testFiltersfilter-name>
<url-pattern>/jpg/*url-pattern>
filter-mapping>
然后访问地址:http://localhost:8080/javaweb/jpg/gu.png(这是我的地址,自己使用自己的地址)
结果如下:
如果再写一个log.jsp,只会简单将其赋值一个session中的属性值而已
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%
// 我们直接通过脚本赋值给session属性
session.setAttribute("username","张三");
%>
然后先访问:http://localhost:8080/javaweb/test.jsp
然后在访问:http://localhost:8080/javaweb/jpg/gu.png
所以可以看出过滤器的作用了,其就是对请求的路径进行一次筛选,符合的就让进去,不符合的就拒绝。(感觉像火车站的是检票员)
其实和servlet很相似,现在说一下其生命周期:
先看一下过滤器有什么方法:
然后如下尝试:
public class testFilters implements Filter {
public testFilters(){
System.out.println("构造方法");
}
public void init(FilterConfig config) throws ServletException {
System.out.println("初始化了");
}
public void destroy() {
System.out.println("销毁了");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
System.out.println("过滤器启动了");
}
}
然后启动tomcat后:
然后访问(因为配置的还是前面演示的配置):http://localhost:8080/javaweb/jpg/gu.png 多访问几次
关闭tomcat:
所以总结:
这个看名字就知道,是过滤器的配置对象类,有点像是servletConfig对象相似,只不过一个是服务于servlet一个服务于filter。
而且FilterConfig也是在Filter创建的时候同时就会创建一个FilterConfig类的,这里包含了Filter配置文件的配置信息。
看官网文档:
而创建的时候,都是在filter中初始化init方法中得到Filterconfig:
public void init(FilterConfig config) throws ServletException {
}
对于FilterConfig的方法然后如下尝试:
web.xml
<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">
<context-param>
<param-name>contextnameparam-name>
<param-value>全局数据param-value>
context-param>
<filter>
<filter-name>testFiltersfilter-name>
<filter-class>com.test.testFiltersfilter-class>
<init-param>
<param-name>testparam-name>
<param-value>小三param-value>
init-param>
filter>
<filter-mapping>
<filter-name>testFiltersfilter-name>
<url-pattern>/jpg/*url-pattern>
filter-mapping>
web-app>
然后看一下过滤器:
public class testFilters implements Filter {
public void init(FilterConfig config) throws ServletException {
System.out.println("getFilterName() 初始化了"+config.getFilterName());
System.out.println("getServletContext()初始化了"+config.getServletContext().getInitParameter("contextname"));
System.out.println("getInitParameter() 初始化了"+config.getInitParameter("test"));
System.out.println("getInitParameterNames()初始化了"+config.getInitParameterNames());
}
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
}
}
其实看起和servletconfig很相似,也就是可以得到配置文件web.xml配置的信息。
其实有时候过滤器不是单独一个使用,而是可以套用的,为什么这样说呢?看下官网文档:
可以看出过滤器器首先会查看是否有下一个过滤器,如果没有再返回过滤器判断之后的资源。先不说这个而是做一个测试。
首先写两个过滤器:
testFilters.java
public class testFilters implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
System.out.println("销毁了");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
System.out.println("test_filter.......前");
chain.doFilter(request,response);
System.out.println("test_filter.......后");
}
}
testFilters1.java
public class testFilters1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("test_filter111111.......前");
chain.doFilter(request,response);
System.out.println("test_filter1111.......后");
}
@Override
public void destroy() {
}
}
不过先说一下在配置的web.xml :
先配置 testFilters在testFilters1前面。
<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">
<filter>
<filter-name>testFiltersfilter-name>
<filter-class>com.test.testFiltersfilter-class>
filter>
<filter-mapping>
<filter-name>testFiltersfilter-name>
<url-pattern>/jpg/*url-pattern>
filter-mapping>
<filter>
<filter-name>testFilters1filter-name>
<filter-class>com.test.testFilters1filter-class>
filter>
<filter-mapping>
<filter-name>testFilters1filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
结果如下:
如果先配置 testFilters1在testFilters前面。
<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">
<filter>
<filter-name>testFilters1filter-name>
<filter-class>com.test.testFilters1filter-class>
filter>
<filter-mapping>
<filter-name>testFilters1filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>testFiltersfilter-name>
<filter-class>com.test.testFiltersfilter-class>
filter>
<filter-mapping>
<filter-name>testFiltersfilter-name>
<url-pattern>/jpg/*url-pattern>
filter-mapping>
web-app>
其实可以看出,在使用多个过滤器的时候,谁在前是根据web.xml配置文件中
而根据 testFilters在testFilters1前面,画一个图:
所以总结:
同时还有一个补充点那就是:
精确匹配
<url-pattern>/jpg/gu.pngurl-pattern>
只会过滤http://localhost:8080/javaweb/jpg/gu.png,其它的地址不会过滤。
目标匹配
<url-pattern>/jpg/*url-pattern>
这个会过滤:以 http://localhost:8080/javaweb/jpg/ 开始的所有url ,其它的地址不会过滤。
后缀匹配
<url-pattern>*.dourl-pattern>
过滤以do结尾的请求地址,还有一点要注意那就是在* 前不要有/.