跳至主要內容

SpringMVC 基础(三)

Yaien Blog原创大约 5 分钟SpringMVC基础

SpringMVC 基础(三)

SpringMVC 基础笔记系列

SpringMVC中的拦截器(Interceptor)

基本概念

在SpringMVC中的拦截器可以是运行在控制器(Controller)之前的组件,可以设置拦截器应用于哪些请求路径,当发生这些请求时,拦截器会自动执行,在执行过程中,可以对请求相关数据进行判断,选择阻止继续向后执行,或选择放行。

注意:拦截器是一个若干种请求都会经历的执行过程,但是,并不一定需要阻止继续运行,只要是若干种请求都需要做相同的事情,也许每种请求的处理过程都是选择放行,也可以使用拦截器。

开发拦截器

首先,所有的拦截器类都必须实现HandlerInterceptor接口,可以自定义LoginInterceptor

	public class LoginInterceptor implements HandlerInterceptor {
	
		public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
				throws Exception {
			System.out.println("LoginInterceptor.preHandle()");
			return false;
		}
	
		public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
				ModelAndView modelAndView) throws Exception {
			System.out.println("LoginInterceptor.postHandle()");
		}
	
		public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
				throws Exception {
			System.out.println("LoginInterceptor.afterCompletion()");
		}
	
	}

在拦截器的3个方法中,只有preHandle()方法是运行在控制器(Controller)之前的,另2个方法是运行在控制器之后的,所以,只有preHandle()具有真正意义的“拦截”功能,该方法的返回值是boolean
类型的,当返回true时表示放行,返回false时将阻止继续向后执行,即控制器并不会被执行:

	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("LoginInterceptor.preHandle()");
		// 获取Session
		HttpSession session = request.getSession();
		// 检查Session中是否有登录信息
		if (session.getAttribute("uid") == null) {
			// 没有登录信息,重定向到登录页
			response.sendRedirect("../user/login.do");
			// 执行拦截
			return false;
		} 
		// 放行
		return true;
	}

注意:即使已经决定了重定向,还是需要return false;否则处理流程会继续向执行,控制器中的方法还是会被调用,达不到阻止运行的效果!

所有的拦截器都需要在Spring的配置文件中进行配置,在SpringMVC框架中,允许使用若干个拦截器,形成拦截器链,即某个请求可能需要经过多个拦截器,仅当每个拦截器都放行时,才会执行控制器中的方法!在配置文件中,配置的先后顺序决定了多个拦截器的执行顺序:

<mvc:interceptors>
    <!-- 配置第1个拦截器 -->
    <mvc:interceptor>
        <!-- 拦截路径 -->
        <mvc:mapping path="/main/index.do"/>
        <!-- 拦截器类 -->
        <bean class="cn.tedu.spring.LoginInterceptor"></bean>
    </mvc:interceptor>
    <!-- 配置第2个拦截器 -->
</mvc:interceptors>

在配置每个拦截器时,允许使用若干个<mvc:mapping>节点以配置若干个拦截路径,例如:

<mvc:interceptor>
    <!-- 拦截路径 -->
    <mvc:mapping path="/main/index.do"/>
    <mvc:mapping path="/user/password.do"/>
    <mvc:mapping path="/user/info.do"/>
    <mvc:mapping path="/user/handle_password.do"/>
    <mvc:mapping path="/user/handle_info.do"/>
    <!-- 拦截器类 -->
    <bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>

在配置路径,还可以使用*作为通配符,例如:

<mvc:interceptor>
    <!-- 拦截路径 -->
    <mvc:mapping path="/main/*"/>
    <!-- 拦截器类 -->
    <bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>

在使用*作为通配符时,需要注意,1个星号只能匹配1层路径,例如/main/*可以匹配上/main/index.do,也可以匹配/main/hello.do,但是,不可以匹配上/main/a/index.do

如果一定要匹配若干层路径,必须使用2个星号,例如配置为/main/**,可以匹配上/main/index.do,也可以匹配/main/a/hello.do,甚至可以匹配/main/a/b/c/d/hello.do
,即无视路径中后续的层级。

所以,可以配置为:

<mvc:interceptor>
    <!-- 拦截路径 -->
    <mvc:mapping path="/main/**"/>
    <mvc:mapping path="/user/**"/>
    <!-- 拦截器类 -->
    <bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>

如果通配符匹配的路径过多,需要从中去除某些请求路径,还可以添加例外:

<mvc:interceptor>
    <!-- 拦截路径:黑名单 -->
    <mvc:mapping path="/main/**"/>
    <mvc:mapping path="/user/**"/>
    <!-- 例外路径:白名单 -->
    <mvc:exclude-mapping path="/user/reg.do"/>
    <mvc:exclude-mapping path="/user/handle_reg.do"/>
    <mvc:exclude-mapping path="/user/login.do"/>
    <mvc:exclude-mapping path="/user/handle_login.do"/>
    <!-- 拦截器类 -->
    <bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>

也就是说,凡/user/下的路径都会经过该拦截器,但是,/user/login.do是不被处理的!添加到“例外”中的路径,在请求时,并不是拦截器直接放行,而是拦截器根本就不执行!

请求参数的乱码解决方案

<node>
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</node>

在SpringMVC中统一处理异常

在Java中,异常的体系结构:

Throwable
		Error
			OutOfMemoryError
		Exception
			SQLException
			IOException
				FileNotFoundException
			RuntimeException
				NullPointerException
				ClassCastException
				NumberFormatException
				IndexOutOfBoundsException
					ArrayIndexOutOfBoundsException
					StringIndexOutOfBoundsException

Exception中,除了RuntimeException及其子孙类异常,其它的异常都是必须通过语法进行处理的!处理方式可以是try...catch进行捕获并处理,也可以是在方法的签名中添加throws声明抛出。

其实,处理异常的本质应该是:给予用户某些提示,告之操作失败的原因,避免用户再次提交错误的数据,另外,也可能在处理过程中,对已经发生的错误尽可能的补救,例如关闭某些已经打开的流对象。

某一种异常在项目的多个请求处理过程中,都可能出现,在SpringMVC中,提供了统一处理异常的做法:将@ExceptionHandler添加在自定义的用于处理异常的方法之前,该自定义方法应该:

  1. 使用public权限;

  2. 返回值的设计原则与处理请求的方法的相同;

  3. 方法名称可以自由定义;

  4. 方法中必须添加异常类型的参数,另外还可以添加例如HttpServletRequest等参数,但不可以添加其它参数。

例如:

	@ExceptionHandler({IndexOutOfBoundsException.class, NullPointerException.class})
	public String handleException(Throwable ex, HttpServletRequest request) {
		String errorMessage = null;
		if (ex instanceof NullPointerException) {
			errorMessage = "错误:请提交用户名!";
		} else if (ex instanceof IndexOutOfBoundsException) {
			errorMessage = "错误:使用的索引超出了界限!";
		}
		request.setAttribute("msg", errorMessage);
		return "error";
	}

@ExceptionHandler注解中,可以配置需要处理的异常的种类,当配置后,仅当指定的异常出现时,才会调用匹配的方法进行处理,而其它异常是不予处理的!如果没有配置需要处理哪些异常,则任何异常出现都会进行处理!

在处理时,@ExceptionHandler只能作用于当前控制器类!

上次编辑于:
贡献者: yanggl