SpringMVC 5:自定义处理器拦截器

※. 处理器拦截器

    SpringMVC的处理器拦截器类似于Servlet 开发中的过滤器Filter,
    用于对处理器进行预处理和后处理。
    1) 常见应用场景
        1、日志记录
        2、权限检查
        3、性能监控
        4、通用行为 例如读取用户cookie
        5、OpenSessionInView 例如在Hibernate中,在进入处理器前打开Session,在完成后关闭Session。
        等
    2) 拦截器接口
      

  public interface HandlerInterceptor {
            boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;

            void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;

            void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
        }

        preHandle方法(相当于前置通知)
            预处理回调方法,实现处理器的预处理,第三个参数为的处理器(本次请求要访问的那个Controller)
            返回值:true表示继续流程(如调用下一个拦截器或处理器)
                    false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应
        
        postHandle方法
            后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理,modelAndView也可能为null。    
        
        afterCompletion方法
            整个请求处理完毕回调方法,即在视图渲染完毕时回调

1.控制器

package com.briup.web.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class FiveController 
	implements Controller{

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		ModelAndView mv=
					new ModelAndView();
		System.out.println("FiveController.....");
		mv.setViewName("hello");
		return mv;
	}

}


2.构建拦截器

package com.briup.web.Interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class defineInterceptor 
	extends HandlerInterceptorAdapter{
	/*执行处理器之前的操作,
	 * 相当于前置通知
	 */
	public  boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("before....defineInterceptor1");
		//true就是放行,原来执行那个
		//controller接着执行那个controller
		//false终止原来的请求
		return true;
	}
	/*
	 * Controller控制器执行之后,
	 * 视图渲染之前做的操作
	 */
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle....defineInterceptor1");
	}
	/*
	 * 在视图解析器渲染视图之后的操作
	 */
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion....defineInterceptor1");
	}
}




3.spring.xml文件配置


	<bean name="myInterceptor" 
	class="com.briup.web.Interceptor.defineInterceptor"></bean>
	<!-- 一旦自定义了拦截器,
	映射器就不能省略 -->
	<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
		<!-- 配置拦截器
		,在映射器中配置的拦截器,对
		所有的controller处理器生效
		interceptors List<Object>
		 -->
		<property name="interceptors">
			<array>
				<ref bean="myInterceptor"/>
			</array>
		</property>
	</bean>
	<!-- 适配器,默认,控制器实现了接口controller
	里面有重写方法的时候配置SimpleControllerHandlerAdapter -->
	<bean class="com.briup.web.adapter.DefineAdapter"></bean>



<bean name="/five" 
	class="com.briup.web.controller.FiveController"></bean>


    
    3) 拦截器适配器
        有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor 接口的话,
        三个方法必须实现,不管你需不需要,此时spring 提供了一个HandlerInterceptorAdapter 适配器(适配器模式),允许我们只实现需要的回调方法。
        在HandlerInterceptorAdapter中,对HandlerInterceptor 接口中的三个方法都进行了空实现,其中preHandle方法的返回值,默认是true

拦截器:

package com.briup.web.Interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class defineInterceptor1 
	extends HandlerInterceptorAdapter{
	public defineInterceptor1() {
		System.out.println("defineInterceptor1 create");
	}
	/*
	 * 执行controller|handler
	 * 中的方法之前做的操作
	 * true 放行
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("before...defineInterceptor2");
		return true;
	}
	/*
	 * 控制器controller中方法执行之后
	 * 视图渲染之前做的操作
	 */
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle.....defineInterceptor2");
	}
	/*
	 * 视图渲染之后做的操作
	 */
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion...defineInterceptor2");
	}
}


spring.xml映射文件


<bean name="myInterceptor" 
	class="com.briup.web.Interceptor.defineInterceptor"></bean>
	<!-- 一旦自定义了拦截器,
	映射器就不能省略 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> 
		<!-- 配置拦截器
		,在映射器中配置的拦截器,对
		所有的controller处理器生效
		interceptors List<Object>
		
		配置多个拦截器的时候
		顺序在先的拦截器先执行前置方法,
		当执行完控制器
		中controller方法之后先执行的拦截器后执行-->
		
		<property name="interceptors">
			<array>
				<bean class="com.briup.web.Interceptor.defineInterceptor1"></bean>
				<ref bean="myInterceptor"/>
			</array>
		</property>
	</bean> 


    
    4) 测试一个拦截器
        拦截器代码:
      

         public class MyInterceptor1 extends HandlerInterceptorAdapter{
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
                System.out.println("MyInterceptor1 preHandle");
                return true;
            }
            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                    ModelAndView modelAndView) throws Exception {
                System.out.println("MyInterceptor1 postHandle");
            }
            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                    throws Exception {
                System.out.println("MyInterceptor1 afterCompletion");
            }
        }

        配置文件:
        <bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>

        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
            <property name="interceptors">
                <list>
                    <ref bean="handlerInterceptor1"/>
                </list>
            </property>
        </bean>        

        访问一个测试的Controller查看结果:
        MyInterceptor1 preHandle
        TestController执行
        MyInterceptor1 postHandle
        MyInterceptor1 afterCompletion

    5) 测试俩个拦截器
        俩个拦截器的代码和上面类似,只是每个输出的内容不同。配置多个拦截器的时候
        顺序在先的拦截器先执行前置方法,当执行完控制器中controller方法之后先执行的拦截器后执行。
        配置文件:
      

        <bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
        <bean name="handlerInterceptor2" class="com.briup.web.interceptor.MyInterceptor1"/>

        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
            <property name="interceptors">
                <list>
                    <ref bean="handlerInterceptor1"/>
                    <ref bean="handlerInterceptor2"/>
                </list>
            </property>
        </bean>

        访问一个测试的Controller查看结果:
        MyInterceptor1 preHandle
        MyInterceptor2 preHandle
        TestController执行
        MyInterceptor2 postHandle
        MyInterceptor1 postHandle
        MyInterceptor2 afterCompletion
        MyInterceptor1 afterCompletion


        注意:<list>标签中引用拦截器的顺序会影响结果输出的顺序
    
    6) 如果Controller等采用的注解配置,那么拦截器需要mvc标签进行配置
        注意:每个<mvc:interceptor>只能配置一个拦截器
      

        <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <ref bean="handlerInterceptor1"/>
            </mvc:interceptor>
        </mvc:interceptors>

        例如1: 注意/*和/**的区别
        <mvc:interceptors>
            <!-- 下面所有的mvc映射路径都会被这个拦截器拦截 -->
            <bean class="com.briup.web.interceptor.MyInterceptor1" />

            <mvc:interceptor>
                <mapping path="/**"/>
                <exclude-mapping path="/admin/**"/>
                <bean class="com.briup.web.interceptor.MyInterceptor2" />
            </mvc:interceptor>
            <mvc:interceptor>
                <mapping path="/secure/*"/>
                <bean class="com.briup.web.interceptor.MyInterceptor3" />
            </mvc:interceptor>
        </mvc:interceptors>
    
例:spring配置文件:

        <!-- 给指定的请求设置拦截器 -->
	<mvc:interceptors>
		<mvc:interceptor>
			<!-- path对资源请求的限定 -->
			
			<mvc:mapping path="/four"/>
			<!-- 不加入拦截器 -->
			<mvc:exclude-mapping path="/hello.do"/>
			<!-- 指的是拦截器 -->
			<bean class="com.briup.web.Interceptor.defineInterceptor1"></bean>
		</mvc:interceptor>
		<mvc:interceptor>
			<mvc:mapping path="/second"/>
			<ref bean="myInterceptor"/>
		</mvc:interceptor>
		<mvc:interceptor>
	    <!-- 	/* 	/后面直接是资源名称(后面不能在出现斜杠)
					 /first    /first/test
					http://localhost:8888/jd1812_MVC/first/test
			/** 资源名称中允许出现多个斜杠 -->
			<mvc:mapping path="/**"/>
			<bean class="com.briup.web.Interceptor.defineInterceptor1"></bean>
		</mvc:interceptor>
	</mvc:interceptors> 

 

    7) 拦截器是单例
        因此不管多少用户请求多少次都只有一个拦截器实现,即线程不安全。
        所以在必要时可以在拦截器中使用ThreadLocal,它是和线程绑定,一个线程一个ThreadLocal,
        A 线程的ThreadLocal只能看到A线程的ThreadLocal,不能看到B线程的ThreadLocal。
        


    8) 记录执行Controller所用时间
      

           public class TimeInterceptor extends HandlerInterceptorAdapter{
            //拦截器是单例,不是线程安全的,所以这里使用ThreadLocal
            private ThreadLocal<Long> local = new ThreadLocal<>();
            
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
                long start = System.currentTimeMillis();
                local.set(start);
                return true;
            }
            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                    throws Exception {
                long end = System.currentTimeMillis();
                System.out.println("共耗时:"+(end-local.get()));
            }
        }
例:
拦截器:

package com.briup.web.Interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
/*
 * 拦截器单列,线程不安全
 * 多个请求同时访问相同控制器中的方法
 * 的时候,该方法只有一个拦截器
 */
public class TimeInterceptor 
	extends HandlerInterceptorAdapter{
	/*
	 * ThreadLocal 单多线程环境
	 * 操作相同的变量的时候
	 * ThreadLocal和每一个线程绑定的
	 * 变量,能保证线程的安全
	 * 注意:ThreadLocal内部只能存储
	 * 一个值
	 */
	private ThreadLocal<Long> timer=
					new ThreadLocal<>();
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		long start_date=
				System.currentTimeMillis();
		timer.set(start_date);
		return true;
	}
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		long end_date=
				System.currentTimeMillis();
		long start_date=timer.get();
		System.out.println("总时间:"
					+(end_date-start_date));
	}
}



spring配置:

        <mvc:interceptors> 
                <mvc:interceptor>
			<mvc:mapping path="/hello.do"/>
			<bean class="com.briup.web.Interceptor.TimeInterceptor"></bean>
		</mvc:interceptor> 
	</mvc:interceptors> 

拓展:利用ThreadLocal对象达到不同层传参。


    9) 登录检查
      

           public class LoginInterceptor extends HandlerInterceptorAdapter{
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
                //请求到登录页面放行
                if(request.getServletPath().startsWith("/login")) {
                    return true;
                }

                //如果用户已经登录放行
                if(request.getSession().getAttribute("username") != null) {
                    return true;
                }

                //重定向到登录页面
                response.sendRedirect(request.getContextPath() + "/login");

                return false;
            }
        }

    
    注意:推荐能使用servlet规范中的过滤器Filter实现的功能就用Filter实现,因为HandlerInteceptor只有在SpringWebMVC环境下才能使用,因此Filter是最通用的、最先应该使用的。

例:

访问登录页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%
	String path = request.getContextPath();
	String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
	%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<base href="<%=basePath %>">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
	登陆页面
</body>
</html>


控制器Controller:

package com.briup.web.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class LoginController 
	implements Controller{

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		ModelAndView mv=
					new ModelAndView();
		mv.setViewName("login");
		return mv;
	}

}


拦截器:

package com.briup.web.Interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;


public class LoginInterceptor 
	extends HandlerInterceptorAdapter{
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//通过request获取请求的资源名称
//		System.out.println(
//				request.getServletPath()+"***");
		//通过request获取项目的名字
//		System.out.println(
//				request.getContextPath()+"&&&");
		if(request.getServletPath().startsWith("/login")){
			return true;
		}
		if(request.getSession().getAttribute("user")!=null){
			return true;
		}
		/*
		 * 直接进入登陆页面
		 */
		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp")
			.forward(request, response);
		//      /jd1812_MVC/login
//		response.sendRedirect(
//				request.getContextPath()+"/login");
		return false;
	}
}





spring配置:

<mvc:interceptor>
	   <!-- 	/* 	/后面直接是资源名称(后面不能在出现斜杠)
					 /first    /first/test
					http://localhost:8888/jd1812_MVC/first/test
			/** 资源名称中允许出现多个斜杠  -->
	       <mvc:mapping path="/**"/>
			<bean class="com.briup.web.Interceptor.LoginInterceptor"></bean>
		</mvc:interceptor>

	<bean name="/login" class="com.briup.web.controller.LoginController"></bean>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值