本文最后更新于:2022-10-20T21:29:50+08:00
拦截器
介绍
在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> <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 protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; }
@Override protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMVCConfig.class}; }
@Override 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 public class SpringMVCConfig implements WebMvcConfigurer {
@Override 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); 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默认为方法的方法名。