SpringMVC拦截器、乱码解决和异常处理

1. SpringMVC中的拦截器(Interceptor)

1.1. 基本概念

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

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

1.2. 开发拦截器

首先,所有的拦截器类都必须实现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>
		
		<mvc:interceptor>
			
			<mvc:mapping path="/main/index.do"/>
			
			<bean class="cn.tedu.spring.LoginInterceptor">bean>
		mvc:interceptor>
		
	mvc:interceptors>

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

	<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>

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


	
	
	
	

在使用*作为通配符时,需要注意,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/**"/>
		
		<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是不被处理的!添加到“例外”中的路径,在请求时,并不是拦截器直接放行,而是拦截器根本就不执行!

2. 请求参数的乱码解决方案

  • 乱码原因:
            计算机能够直接识别并处理的是二进制数据,每个二进数中的0或1占据存储空间中的1个二进制位(bit),每个二进制位不足以表达任何有效的信息含义,计算机中最基本的存储单位是字节(Byte),每个字节由8个二进制位组成。
            在实际使用时,除去最高位的符号位,还有7个二进制位可以表示内容,则每个字节可以表示128种不同的可能,早期的ASCII码就约定了字符与二进制数之间的对应关系,例如字母a对应的就是110 0001,但是,由于ASCII码都只占1个字节,只能128种不同的可能,所以,无法表示中文。
            由于中文的字符种类偏多,所以,至少需要2个字节才可以表示,当使用2个字节时,有效位数最多可高达15个!
            能够支持中文的编码格式有:UTF-8、GBK、GB2312……等等,不同的编码格式的编码规则是不相同的!即同一个汉字,使用不同的编码时,对应的二进制数的序列是不相同的!
            所以,乱码问题的根本原因在于使用的编码不统一!即存和取的时候使用了不同的编码!要解决这个问题的方案就是:始终使用同一种编码即可!
  • 设置编码的位置:
    需要设置编码的位置有:项目的源代码,前端页面,控制器,数据库连接,数据库。
  • 源代码中编码集的设置
    源代码中要使用过滤器设置编码集,因为过滤器执行在servlet之前,拦截器执行在servlet之后,一旦请求数据进入servlet,其中的请求参数已经被解码保存了,所以要在之前设置编码集,只能使用过滤器,在拦截器或者控制器中设置都没什么作用。

拦截器与过滤器的区别:
拦截器(Interceptor)是SpringMVC中的组件,过滤器(Filter)是JavaEE中的组件。
拦截器是运行在DispatcherServlet之后且在所有控制器(Controller)之前的组件,所以,仅当被DispatcherServlet接收的请求才可能被拦截器处理,例如DispatcherServlet配置的是*.do,那拦截器就只能处理某些*.do的请求,而例如*.html*.jpg都不在拦截器可处理的范围之内!过滤器是运行在所有的Servlet之前的组件,甚至可以处理所有的请求,是根据它在web.xml中配置的节点的值来决定的。
拦截器的配置更加灵活,可以有若干个拦截路径(黑名单),也可以有若干个例外路径(白名单),而过滤器只能配置1个过滤路径,如果使用了通配符,例如配置为*.do/*等,只能在过滤器类中编写代码添加例外。

	<filter>
		<filter-name>CharacterEncodingFilterfilter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
		<init-param>
			<param-name>encodingparam-name>
			<param-value>utf-8param-value>
		init-param>
	filter>
	
	<filter-mapping>
		<filter-name>CharacterEncodingFilterfilter-name>
		<url-pattern>/*url-pattern>
	filter-mapping>

3. 在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只能作用于当前控制器类!要让若干个类都能使用该异常处理,可以把这段代码抽取到超类,然后去继承它

你可能感兴趣的:(学习日志,springmvc,拦截器,乱码解决,异常处理)