不要相信客户端, 所以做后端的人都应该铭记的事情。因为前端传过来的数据并不总是合法和有效的,所以后端是对访问资源的最后一道保护伞。之前我们在Spring中说到过AOP编程,AOP基础知识,它就可以在执行我们的方法之前进行一些预处理和验证来保护后端的资源。不难想到她的实现方式和本篇要说的过滤器的实现原理应该是相同的,都是通过Java的动态代理实现的(自己的理解)。
在Java Web的开发中,过滤器用于拦截请求,并对ServletRequest对象进行处理,我们可以想到的,它可以用来验证权限、加密和解密、Session检查、数据转换、防盗链等等。可以看出,它在web应用开发中是十分重要的。
一、 Filter配置使用
1、 配置方式
- @WebFilter(filterName="myfilter", urlPatterns="/myHttpServlet",
- initParams={
- @WebInitParam(name="server", value="www.baidu.com"),
- @WebInitParam(name="port", value="443")
- }
- )
- public class MyServletFilter implements Filter {}
- <filter>
- <filter-name>myServletFilter1</filter-name>
- <filter-class>com.javaservlet.servlet.filter.MyServletFilter1</filter-class>
- <init-param>
- <param-name>server</param-name>
- <param-value>www.baidu.com</param-value>
- </init-param>
- <init-param>
- <param-name>port</param-name>
- <param-value>443</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>myServletFilter1</filter-name>
- <url-pattern>/myHttpServlet</url-pattern>
- </filter-mapping>
2、 基本API
- javax.servlet.Filter:包含有Filter的生命周期方法,在servlet容器初始化时或者销毁时被调用;
- javax.servlet.FilterConfig:包含有关于Filter的配置信息,里边最重要就是它的初始化参数;
- javax.servlet.FilterChain:是servlet容器提供的来实现多个过滤器之间的调用;
- filterConfig.getInitParameter("server");
- Enumeration<String> enums = filterConfig.getInitParameterNames();
- while(enums.hasMoreElements()){
- String paramName = enums.nextElement();
- String paramValue = filterConfig.getInitParameter(paramName);
- System.out.println(paramName + "-" + paramValue);
- }
- filterConfig.getFilterName();
- ServletContext context = filterConfig.getServletContext();
最后是FilterChain类,个人感觉这是Filter里面最重要的类,只有它是和用户要访问的资源是直接相关的。
doFilter()方法才是我们实现具体的拦截器逻辑的地方,它可以修改请求的内容和属性,或者在响应中添加一个Http标头,完成自己的逻辑之后要调用FilterChain的doFilter()方法,将Servlet容器提供的请求实例和响应实例转发出去,为什么需要这个方法呢?简单说一个资源对应的过滤器可能不止一个,一个过滤器也会拦截多个请求,基于这种机制,所以一个资源实际上是对应一个过滤器链,每当一个过滤器处理结束后,就将请求转发给其他的过滤器,直到最后一个过滤器处理完成后,就会将请求和响应发给对应的Servlet。
注意:和Servlet类似,默认情况下servlet容器也是为每个过滤器类创建一个实例,也即是单实例,那么不可避免的就会遇到多线程的问题,所以这点要注意。
下面是完整的Filter实例:
- @WebFilter(filterName="myfilter", urlPatterns="/myHttpServlet",
- initParams={
- @WebInitParam(name="server", value="www.baidu.com"),
- @WebInitParam(name="port", value="443")
- }
- )
- public class MyServletFilter implements Filter {
- public void init(FilterConfig filterConfig) throws ServletException {
- System.out.println("myServletFiler init");
- filterConfig.getInitParameter("server");
- Enumeration<String> enums = filterConfig.getInitParameterNames();
- while(enums.hasMoreElements()){
- String paramName = enums.nextElement();
- String paramValue = filterConfig.getInitParameter(paramName);
- System.out.println(paramName + "-" + paramValue);
- }
- filterConfig.getFilterName();
- ServletContext context = filterConfig.getServletContext();
- }
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- System.out.println("filter service");
- chain.doFilter(request, response);
- }
- public void destroy() {
- System.out.println("myServletFiler destroy");
- }
- }
二、 Filter作用顺序
前面我们主要是集中在一个过滤器上,但是实际情况下,可能(因该是大多是)不止一个过滤器,所以过滤器之间顺序就很重要,这样才不会使业务逻辑混乱。这里我们做一个测试,主要有4个过滤器,两个配置使用@WebFilter配置,两个使用web.xml配置。在每个过滤器都通过打印来实现标识它们的初始化、销毁和进行过滤,同时它们都对同一资源进行过滤,如下:
- public class MyServletFilter1 implements Filter {
- public void init(FilterConfig filterConfig) throws ServletException {
- System.out.println("MyServletFilter1 init");
- }
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- System.out.println("MyServletFilter1 do filter");
- chain.doFilter(request, response);
- }
- public void destroy() {
- System.out.println("MyServletFilter1 destroy");
- }
- }
- public class MyServletFilter2 implements Filter {
- public void init(FilterConfig filterConfig) throws ServletException {
- System.out.println("MyServletFilter2 init");
- }
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- System.out.println("MyServletFilter2 do filter");
- chain.doFilter(request, response);
- }
- public void destroy() {
- System.out.println("MyServletFilter2 destroy");
- }
- }
- @WebFilter(filterName="myServletFilter3", urlPatterns="/myHttpServlet")
- public class MyServletFilter3 implements Filter {
- public void init(FilterConfig filterConfig) throws ServletException {
- System.out.println("MyServletFilter3 init");
- }
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- System.out.println("MyServletFilter3 do filter");
- chain.doFilter(request, response);
- }
- public void destroy() {
- System.out.println("MyServletFilter3 destroy");
- }
- }
- @WebFilter(filterName="myServletFilter4", urlPatterns="/myHttpServlet")
- public class MyServletFilter4 implements Filter {
- public void init(FilterConfig filterConfig) throws ServletException {
- System.out.println("MyServletFilter4 init");
- }
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- System.out.println("MyServletFilter4 do filter");
- chain.doFilter(request, response);
- }
- public void destroy() {
- System.out.println("MyServletFilter4 destroy");
- }
- }
MyServletFilter1和2都是在web.xml中进行配置的,3和4都是使用注解配置的。
- <filter>
- <filter-name>myServletFilter1</filter-name>
- <filter-class>com.javaservlet.servlet.filter.MyServletFilter1</filter-class>
- </filter>
- <filter>
- <filter-name>myServletFilter2</filter-name>
- <filter-class>com.javaservlet.servlet.filter.MyServletFilter2</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>myServletFilter2</filter-name>
- <url-pattern>/myHttpServlet</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>myServletFilter1</filter-name>
- <url-pattern>/myHttpServlet</url-pattern>
- </filter-mapping>
Filter的启动顺序:
在测试过程中的filter的启动顺序一直是2143,好像没有什么规律,但是可以发现,如果一个web应用程序中的过滤器是固定不变的,哪个这些过滤器的启动顺序就是不变的,除非有新的过滤器加入进来。
Filter的销毁顺序:
在测试过程中的filter的销毁顺序也一直是2143,如果一个web应用程序中的过滤器是固定不变的,哪个这些过滤器的启动顺序就是不变的,除非有新的过滤器加入进来;而且发现filter的销毁顺序和它们的启动顺序是一致的。
Filter的作用顺序:
在web.xml中指定过滤器的调用顺序,是由filterMapping元素决定的,哪个filter的filterMapping元素在前面则哪个filter首先被调用,而不是由filter元素决定的。
三、 Filter的dispatcher
- DispatcherType.REQUEST
- DispatcherType.FORWARD
- DispatcherType.INCLUDE
- DispatcherType.ERROR
- DispatcherType.ASYNC
Filter属性的默认值就是DispatcherType.REQUEST,由于它是一个数组属性,所以可以配置多个值。配置方法可以使用注解,也可以使用web.xml进行配置。
使用注解:
- @WebFilter(filterName="myfilter", urlPatterns="/myHttpServlet",
- initParams={
- @WebInitParam(name="server", value="www.baidu.com"),
- @WebInitParam(name="port", value="443")
- },
- dispatcherTypes={DispatcherType.REQUEST, DispatcherType.FORWARD}
- )
- public class MyServletFilter implements Filter {}
使用web.xml,如下:
- <filter-mapping>
- <filter-name>myServletFilter2</filter-name>
- <url-pattern>/myHttpServlet</url-pattern>
- <dispatcher>ERROR</dispatcher>
- <dispatcher>FORWARD</dispatcher>
- <dispatcher>REQUEST</dispatcher>
- </filter-mapping>
注意在web.xml中配置的时候,使用的部署描述符(web.xml)的版本要在2.3以上才会有这个属性。
配置该过滤器管理的资源当被直接调用或者通过转发调用时起作用。我们可以在servlet中进行调用,这里有两个servlet供使用:
下面的Servlet是被过滤器管理的:
- @WebServlet(name="myHttpServlet", urlPatterns="/myHttpServlet")
- public class MyHttpServlet extends HttpServlet {......}
- @WebServlet(name="myHttpServlet1", urlPatterns="/myHttpServlet1", loadOnStartup=1)
- public class MyHttpServlet1 extends HttpServlet {
- private static final long serialVersionUID = 6446908355902952649L;
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- System.out.println("MyHttpServlet1 request work");
- System.out.println("current thread :" + Thread.currentThread().getName());
- resp.getWriter().write("myHttpServlet1");
- req.getRequestDispatcher("/myHttpServlet").forward(req, resp);
- // req.getRequestDispatcher("/myHttpServlet").include(req, resp);
- }
- 当在上述的Filter中没有DispatcherType.FORWARD时,那么上边通过getRequestDispatcher()调用forward()来访问MyHttpServlet时不会调用过滤器方法,使用forward()方法只会返回最后一个被调用的资源;
- 当在上述的Filter中没有DispatcherType.INCLUDE时,那么上边通过getRequestDispatcher()调用include()来访问MyHttpServlet时不会调用过滤器方法,使用include()方法则会将所有经过的资源全部返回;
还有一个是对错误的声明式处理,是在web.xml中进行配置的,如下:
- <error-page>
- <error-code>404</error-code>
- <!-- error-code or exception-type can be chosen only one -->
- <!-- <exception-type>java.lang.NullPointerException</exception-type> -->
- <location>/myHttpServlet</location>
- </error-page>
相关文章: