1. SpringMVC中的拦截器(Interceptor)

1.1. 作用

拦截器是运行在DispatcherServlet之后,在每个Controller之前的,且运行结果可以选择放行或拦截!

除此以外,拦截器还会运行在Controller之后,关于拦截器,在处理某一个请求时,最多有3次执行!只不过,通常关注最多的是第1次执行,即在Controller之前的那次!

1.2. 基本使用

需要自定义类,例如名为LoginInterceptor,实现HandlerInterceptor接口,重写3个抽象方法:

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()");
} }

拦截器需要在Spring的配置文件中进行配置后才可以生效!所以,需要添加配置:

<!-- 配置拦截器链 -->
<mvc:interceptors>
<!-- 配置第1个拦截器 -->
<mvc:interceptor>
<!-- 指定拦截路径,不在拦截路径之内的将不予处理,即拦截器根本就不运行 -->
<mvc:mapping path="/user/info.do"/>
<mvc:mapping path="/user/password.do"/>
<!-- 指定拦截器类 -->
<bean class="cn.tedu.spring.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

在拦截器类中,运行在Controller之前的是preHandle()方法,该方法返回true表示放行,返回false表示拦截!

对于登录拦截器而言,可以判断Session中的用户数据,如果数据存在,视为已登录,可直接放行,如果没有数据,则视为没登录,需要重定向,并拦截:

public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
System.out.println("LoginInterceptor.preHandle()");
// 判断Session中的数据,得知是否登录
HttpSession session = request.getSession();
if (session.getAttribute("username") == null) {
// Session中没有用户名,即:没有登录,则:重定向,并拦截
response.sendRedirect("../user/login.do");
// 返回false表示拦截
return false;
}
// 返回true表示放行
return true;
}

在配置拦截器时,根节点是<mvc:interceptors>,用于配置拦截器链,即任何一个SpringMVC项目,都可以有若干个拦截器,从而形成拦截器链,如果某个请求符合多个拦截器的拦截配置,则会依次被各拦截器进行处理,任何一个拦截,都会导致后续不再将请求交给Controller去处理!

<mvc:interceptors>(有s)节点子级,可以配置多个<mvc:interceptor>(没有s)子级节点,表示多个拦截器,拦截器链的执行顺序取决于这里各节点的配置先后顺序!

<mvc:interceptor>中,<bean>节点用于指定拦截器类;<mvc:mapping>节点,用于配置拦截的请求路径,每个拦截器可以配置多个该节点,并且,在配置时,支持使用星号*作为通配符,例如:<mvc:mapping path="/user/*"/>,为了避免拦截范围过大,可以通过<mvc:exclude-mapping />配置排除在外的请求路径,可以理解为白名单,该节点也可以配置多个:

<!-- 配置拦截器链 -->
<mvc:interceptors>
<!-- 配置第1个拦截器 -->
<mvc:interceptor>
<!-- 指定拦截路径,不在拦截路径之内的将不予处理,即拦截器根本就不运行 -->
<mvc:mapping path="/user/*" />
<!-- 指定白名单,列举的请求路径将不予处理,即拦截器根本就不运行 -->
<mvc:exclude-mapping path="/user/reg.do" />
<mvc:exclude-mapping path="/user/login.do" />
<mvc:exclude-mapping path="/user/handle_reg.do" />
<mvc:exclude-mapping path="/user/handle_login.do" />
<!-- 指定拦截器类 -->
<bean class="cn.tedu.spring.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

注意:以上配置黑名单或白名单时,都可以使用星号*作为通配符,但是,它只能匹配1层路径(或者说只能通配任意资源)!例如配置的是/user/*,可以匹配/user/reg.do/user/login.do,却无法匹配到/user/a/reg.do/user/a/b/login.do这样的路径!如果需要匹配多层路径,可以使用2个星期,即**,例如配置/user/**则可以匹配以上任意路径!

2. 乱码

2.1. 关于乱码

在处理中文或非ASCII字符(需要使用输入法才可以输入的)时,如果存、取时,使用的字符编码不统一,就会出现乱码!

所以,出现乱码原因就是因为字符编码不统一,而解决问题的方案就是使用统一的编码

需要统一编码的位置有:项目源码、数据库、处理数据的服务端组件、数据传输过程、#显示界面

2.2. 解决控制器中接收请求参数的乱码

通常,在Java EE项目中,解决问题的方式是:

request.setCharacterEncoding("utf-8");

由于Controller是运行在DispatcherServlet之后的,在Controller内部再执行更改编码格式已经晚了,事实上SpringMVC框架在DispatcherServlet之前就存在CharacterEncodingFilter可以确定请求与响应的编码格式,所以,在SpringMVC中,无法通过ControllerInterceptor来解决请求和响应的乱码问题。

在SpringMVC框架的CharacterEncodingFilter中,把使用的字符编码设计为变量,可以在web.xml中添加配置加以应用,来统一设置编码:

<!-- 配置字符编码过滤器 -->
<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>

2.3. 拦截器与过滤器有什么区别

拦截器是Interceptor,过滤器是Filter;

拦截器是SpringMVC中的组件,过滤器是Java EE中的组件;

拦截器是配置在Spring的配置文件中的,过滤器是配置在web.xml中的;

拦截器是运行在DispatcherServlet之后、Controller之前的,且在Controller执行完后还会调用2个方法,而过滤器是运行在所有的Servlet之前的;

拦截器的配置非常灵活,可以配置多项黑名单,也可以配置多项白名单,过滤器的配置非常单一,只能配置1项过滤路径;

拦截器与过滤器也有很多相似之处,例如:都可拒绝掉某些访问,也可以选择放行;都可以形成链。

相比之下,在一个使用SpringMVC框架的项目中,拦截器会比过滤器要好用一些,但是,由于执行时间节点的原因,它并不能完全取代过滤器!

3. 异常

3.1. 基本概念

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

Throwable
Error
OutOfMemoryError
Exception
IOException
FileNotFoundException
SQLException
UnsupportedEncodingException
RuntimeException
NullPointerException
ClassCastException
ArithmeticException
IllegalArgumentException
IndexOutOfBoundsException
ArrayIndexOutOfBoundsException

在这些异常中,RuntimeException及其子孙类异常,在Java语法中并不要求必须处理!主要的原因有:这些异常出现的频率可能非常高,如果一定要处理,例如try...catch,则几乎所有的代码都需要放在try代码块中!并且,这些异常是可以杜绝的异常,通过严谨的编程,可以保证这些异常绝对不会出现!

处理异常有2种方式:使用try...catch处理异常,或者使用throw抛出异常对象,并且在方法的声明中使用throws语法声明抛出!

通常,异常都是必须处理的,如果没有处理异常,会导致异常不断向上抛出,最终,Java EE中的异常会由Tomcat来处理,会把跟踪日志显示在页面中!而这些跟踪日志是普通用户看不懂的,对于专业人士而言,还可能分析出项目中的一些内容,对于后续解决问题也没有任何帮助,所以,从开发原则上来说,必须处理异常!

3.2 处理异常-SimpleMappingExceptionResolver

RuntimeException是从语法上强制要求处理的,所以,每次调用了抛出异常的方法,就必须try...catch或继续声明抛出!

RuntimeException及其子孙类异常并不从语法上强制要求处理,但是,一旦出现,会项目的安全、用户体验等各方面都会产生负面影响!

SpringMVC提供了统一处理异常的方式:使用SimpleMappingExceptionResolver类,该类通过private Properties exceptionMappings;属性来配置异常种类与转发到的页面的对应关系,即每一种异常都可以有一个与之对应的页面,一旦项目中出现配置过的异常,就会自动转发到对应的页面来提示错误信息!

<!-- 配置统一处理异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.RuntimeException">runtime</prop>
<prop key="java.lang.NullPointerException">null</prop>
<prop key="java.lang.ArrayIndexOutOfBoundsException">index</prop>
</props>
</property>
</bean>

这种方式处理异常非常的简单,但是,也有一个比较大的问题:无法提示更详细的信息!例如出现ArrayIndexOutOfBoundsException时,其实,异常对象中还封装了详细的错误信息,即:越界值是多少。

3.3 处理异常-@ExceptionHandler

可以在控制器类中自定义一个处理异常的方法,在方法之前添加@ExceptionHandler,然后,凡是约定范围内的异常,都可以由该方法来决定如何处理:

@ExceptionHandler
public String handleException(Exception e) {
System.out.println(
"ExceptionController.handleException()"); if (e instanceof NullPointerException) {
return "null";
} else if (e instanceof ArrayIndexOutOfBoundsException) {
return "index";
} return "runtime";
}

在处理异常时,如果需要转发数据,可以将返回值修改为ModelAndView,或者,在处理异常的方法参数列表中添加HttpServletRequest,但是,却不可以使用ModelMap来封装转发的数据:

@ExceptionHandler
public String handleException(Exception e,
HttpServletRequest request) {
System.out.println(
"ExceptionController.handleException()"); if (e instanceof NullPointerException) {
return "null";
} else if (e instanceof ArrayIndexOutOfBoundsException) {
request.setAttribute("msg", e.getMessage());
return "index";
} return "runtime";
}

思考:是否可以在类中添加3个处理异常的方法,分别只处理NullPointerExceptionArrayIndexOutOfBoundsExceptionRuntimeException

答案:可以!允许使用多个不同的方法来处理异常!

思考:假设处理异常的方法在A控制器中,而B控制器中处理请求时出现异常,会被处理吗?

答案:不可以!通常,可以创建一个BaseController,作为当前项目的控制器类的基类,然后,把处理异常的代码编写在这个类中即可!

关于@ExceptionHandler,还可以用于限制其对应的方法处理的异常的种类!例如:

@ExceptionHandler(IndexOutOfBoundsException.class)

以上代码表示接下来的方法只处理IndexOutOfBoundsException及其子孙类异常,而其它的异常并不处理!

3.4 小结

处理异常的本质并不可以“撤消”异常,无法让已经出现的问题还原到没有问题!所以,处理异常的本质应该是尽量的补救已经出现的问题,并且,尝试通过提示信息等方式告之使用者,尽量执行正确的操作,避免后续再次出现同样的问题!

并且,对于开发者而言,应该处理每一个可能出现的异常,因为,如果没有处理,就会继续抛出,最终被Tomcat捕获,而Tomcat处理的方式是把异常的跟踪信息显示在页面上,是极为不合适的!

在SpringMVC中,处理异常有2种方式,第1种是通过SimpleMappingExceptionResolver设置异常与转发目标的对应关系,第2种是使用@ExceptionHandler为处理异常的方法进行注解,推荐使用第2种方式。

通常,会把处理异常的方法写在项目的控制器类的基类中,即写在BaseController中,在使用@ExceptionHandler时,可以明确的指定所处理的异常类型,例如:@ExceptionHandler(NullPointerException),且,在控制器类中,可以编写多个处理异常的方法!

关于处理异常的方法,应该是public方法,返回值类型与处理请求的方式相同,可以是StringModelAndView或其它允许的类型,方法的名称可以自定义,参数列表中必须包括Exception类型的参数,还允许存在HttpServletRequestHttpServletResponse,不允许使用ModelMap

SpringMVC中的拦截器、过滤器的区别、处理异常的更多相关文章

  1. 【SpringMVC学习11】SpringMVC中的拦截器

    Springmvc的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理.本文主要总结一下springmvc中拦截器是如何定义的,以及测试拦截器的执行情况和使用 ...

  2. (转)SpringMVC学习(十二)——SpringMVC中的拦截器

    http://blog.csdn.net/yerenyuan_pku/article/details/72567761 SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter, ...

  3. 9.springMVC中的拦截器

    springMVC中的拦截器大概大致可以分为以下几个步骤去学习: 1.自定义一个类实现HandlerInterceptor接口,这里要了解其中几个方法的作用 2.在springMVC的配置文件中添加拦 ...

  4. spring拦截器-过滤器的区别

    1.  理解 拦截器 :是在面向切面编程的时候,在你的 service 或者一个方法前调用一个方法,或者在方法后调用一个方法:比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业 ...

  5. SpringMvc中Interceptor拦截器用法

    SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理.比如通过它来进行权限验证,或者是来判断用户是否登陆等. 一. 使用场景 1 ...

  6. SpringMVC中的拦截器

    1. 自定义拦截器 实现HandlerInterceptor接口 拦截器一: package cn.rodge.ssm.interceptor;import javax.servlet.http.Ht ...

  7. springmvc中的拦截器interceptor用法

    1.配置拦截器 在springMVC.xml配置文件增加: 1 <mvc:interceptors> 2 <!-- 日志拦截器 --> 3 <mvc:intercepto ...

  8. springMvc中实现拦截器Interceptor以及添加静态资源映射

    这个代码写了很久了,多久呢?2018年12-20号写的.... 废话不多说,简化一下,作为笔记. 注: public class springmvcConfig extends WebMvcConfi ...

  9. Spring MVC中的拦截器/过滤器HandlerInterceptorAdapter的使用

    一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的 而在Spring中,基于Filter这种方式可以实现Bean预处理.后处理. 比如注入FilterRegistrationBean,然后 ...

随机推荐

  1. 【wireshark】插件开发(四):Lua插件Post-dissector和Listener

    1. Post-dissector post-dissector和dissector不同,它会在所有dissectors都执行过后再被执行,这也就post前缀的由来.post-dissector的构建 ...

  2. 在android应用程序中启动其他apk程序

    Android 开发有时需要在一个应用中启动另一个应用,比如Launcher加载所有的已安装的程序的列表,当点击图标时可以启动另一个应用. 一般我们知道了另一个应用的包名和MainActivity的名 ...

  3. FineReport9.0定义数据连接(创建与SQL Server 2016数据库的连接)

    1.下载并安装好FineReport9.0和SQL Server 2016 2.开始——>所有应用——>Microsoft SQL Server 2016——>SQL Server ...

  4. 【LeetCode】462. 最少移动次数使数组元素相等 II

    给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1. 您可以假设数组的长度最多为10000. 例如: 输入: [1,2,3] 输出: 2 说明: 只 ...

  5. Oracle 锁问题处理

    Oracle 锁问题处理 锁等待问题是一个常见的问题 查看持有锁的对象 查看事务正在执行的语句,与应用确认是否能够kill kill 对应的session

  6. ES6 箭头函数 this 指向

    ES6 箭头函数 this 指向 箭头函数有几个使用注意点: 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个 ...

  7. Android之Activity界面跳转--生命周期方法调用顺序

    这本是一个很基础的问题,很惭愧,很久没研究这一块了,已经忘得差不多了.前段时间面试,有面试官问过这个问题.虽然觉得没必要记,要用的时候写个Demo,打个Log就清楚了.但是今天顺手写了个Demo,也就 ...

  8. Python -- Gui编程 -- Tkinter的使用 -- 基本控件

    1.按钮 tkBtton.py import tkinter root = tkinter.Tk() btn1 = tkinter.Button(root, anchor=tkinter.E,\ te ...

  9. rails render

    Render結果 在根據request資訊做好資料處理之後,我們接下來就要回傳結果給用戶.事實上,就算你什麼都不處理,Action方法裡面空空如也,甚至不定義Action,Rails預設也還是會執行r ...

  10. bind9配置转发服务

    修改bind主配置文件 $ vi /etc/named.conf//// named.conf//// Provided by Red Hat bind package to configure th ...