1. 统一异常处理
SpringMVC提供了统一处理Controller层抛出的异常的方法
1.1 统一异常处理案例
RestController的统一异常处理
package com.lan.controller;
import com.lan.exception.BusinessException;
import com.lan.exception.SystemException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
//注解@RestControllerAdvice用于标识当前类为REST风格对应的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
//注解@ExceptionHandler用于设置当前处理器类对应的异常类型
@ExceptionHandler(SystemException.class) // 处理SystemException
public Result doSystemException(SystemException ex){
//记录日志
//发送消息给运维
//发送邮件给开发人员,ex对象发送给开发人员
// ...
return new Result(ex.getCode(),null,ex.getMessage());
}
@ExceptionHandler(BusinessException.class) // 处理BusinessException
public Result doBusinessException(BusinessException ex){
// ...
return new Result(ex.getCode(),null,ex.getMessage());
}
//除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
@ExceptionHandler(Exception.class) // 处理其它的Exception
public Result doOtherException(Exception ex){
// ...
return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!");
}
}
1.2 常用注解
注解 | 作用 | 作用目标 |
@RestControllerAdvice | 为Controller类做增强,相当于@ControllerAdvice+@ResponseBody,@ExceptionHandler 方法默认带有 @ResponseBody 。 | 类 |
@ExceptionHandler | 设置指定类型异常的处理方案,Controller出现指定类型的异常后转入当前方法执行 | 方法 |
2. 统一结果封装
通过@RestControllerAdvice增强Controller的同时,实现ResponseBodyAdvice接口可以统一封装RestController的返回结果。
案例如下:
package com.lan.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@RestControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice<Object> {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 类型不是Result时使用增强
if(returnType.getParameterType() != Result.class) {
return true;
}
return false;
}
// body为原返回对象
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
try {
return objectMapper.writeValueAsString(new Result(0, body));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return body;
}
}
3. 拦截器interceptor
SpringMVC中的拦截器(interceptor)是一种动态拦截控制层的机制,用来做控制层增强。
3.1 拦截器Interceptor与过滤器Filter的区别
归属不同:Filter属于Serlvet技术,Interceptor属于SpringMVC技术。
拦截范围不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强。
3.2 拦截器案例
Servlet配置类
package com.lan.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
// return new Class[SpringConfig.class];
return new Class[0];
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"}; // 表示SpringMVC要处理的所有路径
}
//乱码处理的过滤器Filter
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
拦截器
package com.lan.controller.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
@Override
//原始方法调用前执行的内容
//返回值类型可以拦截控制的执行,true放行,false终止
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String contentType = request.getHeader("Content-Type");
HandlerMethod hm = (HandlerMethod)handler;
System.out.println("preHandle..."+contentType);
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容器扫描到):
package com.lan.config;
import com.itheima.controller.interceptor.ProjectInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 不需要SpringMVC处理的静态资源
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
//配置拦截器和拦截的所有路径
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
// 添加多个添加拦截器
// registry.addInterceptor(xxx).addPathPatterns("/xxx","/xxx/*");
}
}