SpringMVC笔记(6)-拦截器,异常解析器与配置类

拦截器

介绍

在SpringMVC中,拦截器主要用来拦截控制器方法的执行。它可以在控制器方法执行前后等位置执行相关的代码。

在前面我们还提到过过滤器filter的概念。过滤器指的是在浏览器和DispatcherServlet之间的过滤操作,浏览器发送的请求需要先经过过滤器操作,然后再由DispatcherServlet进行处理。

SpringMVC中拦截器通过实现接口HandlerInterceptor来实现,在其中一共有三个方法需要进行重写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}

实现了拦截器相关代码后,我们需要在SpringMVC中的配置文件中配置该拦截器,才能进行使用。具体如下:

1
2
3
4
<!--配置拦截器-->
<mvc:interceptors>
<bean class="com.syh.mvc.interceptor.MyInterceptor"/>
</mvc:interceptors>

拦截器的配置使用<mvc:interceptors>,在其中我们可以指定对应的bean,对应我们的拦截器类。当然除了使用bean标签指定对应类,也可以在外面定义一个Bean,然后在这里使用<ref>标签指定对应Bean。这种配置方式直接指定了对应的类。这种配置方式会对所有的请求进行拦截。

<mvc:interceptors>标签中,我们还可以使用<interceptor>标签,其中可以指定更加细节的配置,包括处理请求路径,需要排除的路径,使用的拦截器类等。

1
2
3
4
5
6
7
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/dontWantToSee"/>
<bean class="com.syh.mvc.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

这里的/**表示匹配所有请求,而/*则表示仅匹配单层目录。

HandlerInterceptor

我们的拦截器需要实现HandlerInterceptor接口,其中的接口方法有一共有三个:

  • public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):在控制器方法执行之前执行preHandle,返回值为一个布尔类型的值,表示是否放行。如果返回true,则表示放行该请求,如果返回false,则表示拦截该请求。
  • public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):在控制器方法执行之后执行postHandle方法。
  • public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex):在处理完视图和模型数据以及渲染视图完毕之后,执行afterCompletion方法。

拦截器的执行顺序

单个拦截器的执行顺序为:preHandler() -> postHandle() -> 控制器方法 -> afterCompletion()。而如果配置了多个拦截器,则拦截器的执行顺序与其在SpringMVC配置文件中的配置顺序有关。

如果每个拦截器的preHandle()方法都返回true,那么每个拦截器的方法都会执行,preHandle()方法按照配置的顺序执行,而postHandle()和afterCompletion()方法会按照配置顺序的逆序执行。

如果其中有一个拦截器的preHandle()方法返回了false,则后续的所有拦截器方法都不会执行,控制器方法也不会执行。而对于前面的拦截器,它们的preHandle()方法仍然是顺序执行,postHandle()方法都不执行,但是afterCompletion()方法会逆序执行。

异常处理器

SpringMVC中的异常处理器用于处理控制器方法执行过程中所出现的异常,对应的接口为HandlerExceptionResolver。通过异常处理器,我们可以在出现异常的时候跳转到相应页面,提高用户体验。

对于该接口,在SpringMVC中提供了两个实现类,分别是DefaultHandlerExceptionResolver以及SimpleMappingExceptionResolver,其中默认的异常处理器即SpringMVC中默认使用的,而SimpleMapplingExceptionResolver则是自定义的异常处理器,我们可以利用注解配置来使用。在SpringMVC配置文件中配置如下内容:

1
2
3
4
5
6
7
8
9
10
11
<!--配置异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--指定不同异常相应的跳转页面-->
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--将异常信息在请求域中共享,对应键值对的key名称为这里value的值-->
<property name="exceptionAttribute" value="ex"/>
</bean>

其中我们可以在exceptionMappings属性中指定不同异常对应的跳转页面,这里的error为视图的名称。我们也可以利用exceptionAttribute属性将异常信息在请求域中共享,并指定对应的key名称。

我们也可以创建自己的类用作异常处理:

1
2
3
4
5
6
7
8
@ControllerAdvice
public class ErrorController {
@ExceptionHandler(ArithmeticException.class)
public String handlerArithmeticException(Model model, Exception ex) {
model.addAttribute("ex", ex);
return "error";
}
}

这里我们利用@ControllerAdvice注解将当前类标识为异常处理的组件,其中的方法用于处理不同的异常,并用@ExceptionHandler标识该方法需要处理的异常。在形参中,我们可以利用Exception类型来承接当前处理中出现的异常对象。

SpringMVC中的配置类

在SpringMVC中,我们用到的配置文件有项目配置文件web.xml,SpringMVC的配置文件springMVC.xml,后续还可能会用到Spring的配置文件spring.xml。我们可以使用配置文件,也可以用配置类来代替这些配置文件。

在Servlet 3.0环境中,容器会在类路径中查找实现了javax.servlet.ServletContainerInitializer接口的类,如果找到的话,就会利用这个类来配置Servlet容器。Spring中提供了这个接口的实现类,名称为SpringServletContainerInitializer。这个类会查找实现了WebApplicationInitializer的类,实际配置的任务交由它来完成。Spring 3.2中提供了该接口的一个基础实现,名称为AbstractAnnotationConfigDispatcherServletInitializer。因此,我们只需要实现自己的类来继承该类,容器会自动发现然后利用它来配置Servlet上下文。

我们实现的类对应配置文件web.xml。在web.xml中,我们会配置Spring的相关信息,配置SpringMVC的相关信息,配置前端控制器DispatcherServlet的映射规则url-pattern以及过滤器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
// 指定Spring的配置类
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}

@Override
// 指定SpringMVC的配置类
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMVCConfig.class};
}

@Override
// 指定DispatcherServlet的映射规则,即url-pattern
protected String[] getServletMappings() {
return new String[]{"/"};
}

@Override
// 添加过滤器
protected Filter[] getServletFilters() {
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceEncoding(true);
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();

return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
}
}

Spring的配置类SpringConfig对应Spring的配置文件。在SSM整合之后,Spring的配置信息书写在此类中。

SpringMVC的配置类SpringMVCConfig对应SpringMVC的配置文件。在SpringVC的配置文件中,我们一般会完成扫描组件、视图解析器、默认Servlet配置、MVC注解驱动、视图控制器、文件上传解析器、拦截器、异常解析器等功能,这些功能都可以在接口WebMvcConfigurer中找到相应的方法,我们的配置类也需要实现该接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@Configuration // 标识为配置类
@ComponentScan("com.syh.mvc") // 组件扫描
@EnableWebMvc // 开启MVC注解驱动
public class SpringMVCConfig implements WebMvcConfigurer {

@Override
// 使用默认的Servlet处理静态资源
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}

// 配置文件上传解析器
@Bean
public CommonsMultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}

@Override
// 配置视图控制器
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}

@Override
// 配置异常映射
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
// 设置异常映射
properties.setProperty("java.lang.ArithmeticException", "error");
exceptionResolver.setExceptionMappings(properties);
// 设置共享异常的Key
exceptionResolver.setExceptionAttribute("ex");
resolvers.add(exceptionResolver);
}

// 生成视图解析器,这里需要注入模板引擎,所以还需要配置模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);

return viewResolver;
}

// 生成模板引擎,这里需要注入模板解析器,所有还需要配置模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);

return templateEngine;
}

public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(webApplicationContext.getServletContext());
templateResolver.setPrefix("/WEB_INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);

return templateResolver;
}
}

这里我们使用到了@Bean注解。这个注解用来标识方法,则被标识的方法的返回值将被作为Bean管理,Bean的id默认为方法的方法名。


SpringMVC笔记(6)-拦截器,异常解析器与配置类
http://example.com/2022/10/20/SpringMVC笔记-6-拦截器-异常解析器与配置类/
作者
EverNorif
发布于
2022年10月20日
许可协议