### 1. 使用 Session
通常, 会在 Session 中存放:
1. 客户端 (用户) 的身份标识, 通常是用户的 id;
2. 使用频率非常高的数据, 例如显示在页面中的用户名, 头像等;
3. 其它的不便于使用其它存储方案来存取或传递的数据.
关于 Session 的使用, 和 `ModelMap` 几乎一样, 即在处理请求的方法中添加 `HttpSession` 参数, 并在方法体中操作该参数对象即可.
### 2. 拦截器: Interceptor
Spring MVC 中的拦截器 (Interceptor) 与 Java EE 中的过滤器 (Filter) 比较相似, 可以对某些请求尝试拦截, 由开发者自行编写拦截的逻辑, 使得某些请求可以执行, 而某些请求将不允许执行, 实现统一管理的效果.
在使用时, 必须先自定义拦截器类, 实现 `HandlerInterceptor` 接口, 然后在 Spring 的配置文件中进行配置.
当实现 `HandlerInterceptor` 接口后, 需要重写 3 个未实现的方法, 其中,`public boolean preHandle()` 方法是起到拦截作用的, 在运行在控制器之前的, 该方法的返回值是 boolean 类型的, 表示是否放行, 即返回 true 则放行, 返回 false 则拦截! 一旦拦截, 控制器方法将不会被执行, 并且拦截器中剩下的 2 个方法也不会被执行, 如果通过浏览器进行访问, 界面将显示一片空白!
注意: 即使执行重定向语法, 如果拦截器 `return true;`, 依然会执行控制器中的方法和拦截器中另 2 个方法, 则没有拦截效果, 所以, 当符合拦截条件时, 应该 `return false;`.
关于拦截器的配置大致如下:
- <!-- 拦截器链 -->
- <mvc:interceptors>
- <!-- 第 1 个拦截器 -->
- <mvc:interceptor>
- <!-- 拦截路径 -->
- <mvc:mapping path="/user/index.do"/>
- <!-- 拦截器类 -->
- <bean class="cn.tedu.spring.interceptor.LoginInterceptor"></bean>
- </mvc:interceptor>
- <!-- 第 2 个拦截器 -->
- <!-- 第 3 个拦截器 -->
- <!-- 第 N 个拦截器 -->
- </mvc:interceptors>
即: SpringMVC 是支持 ** 拦截器链 ** 的, 在同一个项目中, 允许存在多个拦截器, 形成拦截器链, 多个拦截器的执行先后顺序取决于配置的先后顺序.
在配置每一个拦截器的 `<mvc:interceptor>` 节点中,`<mvc:mapping>` 节点用于配置需要拦截的路径, 该节点可以存在若干个, 例如:
- <mvc:interceptor>
- <!-- 拦截路径 -->
- <mvc:mapping path="/user/index.do"/>
- <mvc:mapping path="/user/logout.do"/>
- <!-- 拦截器类 -->
- <bean class="cn.tedu.spring.interceptor.LoginInterceptor"></bean>
- </mvc:interceptor>
并且, 在配置路径时, 是支持通配符的, 例如:
<mvc:mapping path="/user/*"/>
即: 例如 `/user/reg.do`,`/user/login.do`,`/user/handle_reg.do` 等这些路径都在拦截范围之内!
但是, 需要注意的是: 1 个星号表示的通配符只能匹配 1 层路径, 例如 `/user/*` 不可以匹配到 `/user/news/list.do` 这样的路径! 如果要匹配若干层路径, 可以使用 2 个星号, 例如配置为 `/user/**`.
除此以外, 在配置时, 还可以添加 `<mvc:exclude-mapping>` 节点, 以配置例外, 例如:
- <!-- 拦截路径 -->
- <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"/>
关于 `<mvc:exclude-mapping>` 的配置方式, 与 `<mvc:mapping>` 相同, 也可以使用通配符.
以上 `<mvc:mapping>` 也可以理解为 ** 拦截名单 **, 而 `<mvc:exclude-mapping>` 就是 ** 白名单 **.
以上配置是必须有先后顺序的,`<mvc:mapping>` 必须在最前, 其次是 `<mvc:exclude-mapping>`, 最后是拦截器 `<bean>`.
仅在拦截范围之内的, 才会触发拦截器执行(无论最终是拦截还是放行), 如果某路径不在拦截范围之内(包含被添加到例外的), 将根本就不触发拦截器的执行.
### 3. SpringMVC 项目的乱码解决方案
整个 SpringMVC 框架默认使用的编码都是 ISO-8859-1, 是不支持中文的, 所以, 在 `DispatcherServlet` 接收到请求的那一刻起, 数据的编码就已经是 ISO-8859-1, 为了修改编码, 只能通过过滤器 (Filter) 来设置, 在 SpringMVC 中也定义好了 `CharacterEncodingFilter`, 用于设置字符编码, 所以, 当使用 SpringMVC 时, 应该在 `web.xml` 中配置该过滤器, 并为这个过滤器类的 `encoding` 属性设置编码值:
- <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>
- ### 4. SpringMVC 处理异常
控制器是向客户端进行响应的组件, 如果在控制器中的代码运行时出现异常, 应该进行处理, 如果不处理异常, 则会按照默认的方式处理, 有几处问题:
1. 对于没有计算机开发相关基础的用户而言, 体验很差(界面上显示的错误完全看不懂);
2. 对于掌握了计算机开发相关技术的用户而言, 可能从中获取当前项目的某些实现细节(异常的跟踪信息中会显示某些类, 方法的名称等), 导致项目的部分内容外泄;
3. 其它问题.
所以, 在控制器中, 应该对可能存在的异常进行处理! 注意: 此处的 "处理" 不包括使用 `throw` 抛出, 而是通过 `try...catch` 类似的方式捕获并在 `catch` 代码块中进行处理, 例如:
- @RequestMapping("null.do")
- public String showNull(
- String username, ModelMap modelMap) {
- try {
- username.length();
- } catch (NullPointerException e) {
- String message = "您的操作有误, 未提交必要的参数, 请 < a href=input.do > 重新提交</a>!";
- modelMap.addAttribute("msg", message);
- return "error";
- }
- return null;
- }
由于异常出现的频率可能较高, 或者, 在多个不同的请求中都可能出现, 那么, 在每个方法中都进行处理, 是不现实的, 也不便于代码的管理!
在 SpringMVC 中, 提供了 2 种统一处理异常的做法:
**1. 通过 SimpleMappingExceptionResolver**
在 Spring 的配置文件中, 对 `SimpleMappingExceptionResolver` 进行配置, 确定异常与转发的页面的映射即可:
- <!-- 处理异常 -->
- <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
- <property name="exceptionMappings">
- <props>
- <prop key="java.lang.NullPointerException">error</prop>
- <prop key="java.lang.StringIndexOutOfBoundsException">oob</prop>
- <prop key="java.lang.RuntimeException">runtime</prop>
- </props>
- </property>
- </bean>
使用这种做法时, 如果出现异常, 框架将自动转发到对应的页面, 开发者难以对异常出现的原因作出针对性的处理! 所以, 这种做法只适合比较粗糙的, 大概的处理异常!
同时, 这种做法固定使用转发来显示错误提示页面, 无法更改为其它方式的响应!
**2. 使用 @ExceptionHandler**
在控制器类中, 可以自定义处理异常的方法, 并在方法之前添加 `@ExceptionHandler` 注解, 该方法的设计规则与处理请求的方法大致相同, 区别在于参数不可以随便写, 必须包含 1 个异常类型的参数,
- @ExceptionHandler
- public String handleException(Exception e) {
- if (e instanceof NullPointerException) {
- return "error";
- } else if (e instanceof StringIndexOutOfBoundsException) {
- return "oob";
- }
- return "exception";
- }
> 使用 `@ExceptionHandler` 之前, 必须保证在 Spring 配置文件中已经配置了注解驱动, 即 `<mvc:annotation-driven />`.
在使用 `@ExceptionHandler` 注解时, 可以在注解中添加属性的配置, 以确定所处理的异常的范围, 例如:
@ExceptionHandler(IndexOutOfBoundsException.class)
经过以上配置, 仅 `IndexOutOfBoundsException` 及其子孙类异常会被接下来的方法进行处理, 而其它异常的出现, 并不会导致对应的方法被执行!
在控制器中, 允许同时存在多个处理异常的方法!
处理异常的方法仅能作用于当前控制器类中的请求! 如果某个处理异常的方法的代码希望被通用, 可以将这个处理异常的方法写在控制器类的基类中.
以上两种统一处理异常的方式是可以同时存在的! 且第 2 种方式的优先级高于第 1 种方式.
** 附: 常见异常 **
- Throwable
- Error
- OutOfMemoryError
- Exception
- SqlException
- IOException
- FileNotFoundException
- RuntimeException
- NullPointException
- ClassCastException
- ArithmeticException
- IndexOutOfBoundsException
- ArrayIndexOutOfBoundsException
- ### [附] 拦截器和过滤器的区别
过滤器 (Filter) 是 Java EE 体系中的, 而拦截器 (Interceptor) 是 SpringMVC 中的;
过滤器是在所有的 Servlet 之前执行的, 而拦截器的初次执行是在 DispatcherServlet 之后, 在 Controller 之前执行的;
过滤器的过滤范围只能在 Web.xml 中通过 < url-pattern > 这 1 个节点来配置, 而拦截器可以配置多个拦截路径, 且可以添加例外, 配置更加灵活;
所有的请求都可以被过滤器进行处理, 却只有交由 DispatcherServlet 分发的请求才可能被拦截器处理!
### [附] 字符编码问题 / 出错时的乱码问题
在不考虑某些编码不支持中文的情况下, 使用了支持中文的编码, 仍会出现乱码的原因只有 1 个: 存和取的时候使用了不同的编码.
所以, 解决方案就是: 整个项目涉及的所有位置全部使用相同的编码!
通常, 需要指定编码的位置有:
1. 项目的源代码, 例如某个 String 类型数据的值;
2. 显示界面的组件所使用的编码, 例如 html/JSP 使用的编码;
3. 数据存储位置使用的编码, 例如数据库中的编码;
4. 数据传输过程中使用的编码, 例如请求, 响应, 或数据库连接的 URL.
来源: https://www.cnblogs.com/topzhao/p/10305715.html