这篇文章会记录一些SpringBoot中常用的知识/技巧。
这些知识或技巧并不高深,但如果没有接触过,工作进度常常会受阻,明明一个很简单的问题,却花了许久才解决。
本文不定期更新。
自定义拦截器(拦截HTTP请求)
使用场景:拦截http请求,在请求前后自定义处理内容。
核心类:HandlerInterceptor,WebMvcConfigurer
HandlerInterceptor,包含三个方法:
- preHandle 目标方法执行前执行
- postHandle 目标方法执行后执行
- afterCompletion 请求完成时执行
核心步骤: 第一步, 自定义拦截处理逻辑:实现HandlerInterceptor接口。
public class MyHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 自定义逻辑
return true;
}
}
第二步,注册拦截器,使其能够作用于controller方法
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
HandlerInterceptor handlerInterceptor = new MyHandlerInterceptor();
// 注册拦截器
InterceptorRegistration simLoginRegistration = registry.addInterceptor(handlerInterceptor);
// 设置优先级
simLoginRegistration.order(10);
}
}
自此,在请求接口时spring mvc通过该拦截器,能够自动拦截该接口。
获取Spring容器对象
使用场景:从Spring容器中获取Bean
获取Bean对象方式:
- BeanFactoryAware接口
- ApplicationContextAware接口
BeanFactoryAware
@Service
public class BeanUtils implements BeanFactoryAware {
private static BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public static <T> T getBean(String name) {
return (T) BeanUtils.beanFactory.getBean(name);
}
}
ApplicationContextAware
@Service
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
public static <T> T getBean(String name) {
return (T) SpringUtils.applicationContext.getBean(name);
}
}
自定义接口参数类型转换
使用场景:接口中接收参数的实体对象中,有个字段的类型是Date,但是实际传参的是字符串类型:2021-01-03 10:20:15
核心 : 自定义注解 + HandlerMethodArgumentResolver + WebMvcConfigurer
第一步,自定义注解
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DateParam {
public String name() default "";
}
第二步,实现**HandlerMethodArgumentResolver接口**
import com.pinduoduo.teochew.common.anno.DateParam;
import com.pinpinxiaozhan.service.base.utils.DateTimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.util.Date;
@Slf4j
public class MyHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(DateParam.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
DateParam dateParam = parameter.getParameterAnnotation(DateParam.class);
String name = dateParam.name();
if ("".equals(name)) {
name = parameter.getParameter().getName();
}
String tmp = webRequest.getParameter(name);
Date res = DateTimeUtils.parse(tmp, DateTimeUtils.SHORT_DISPLAY_PATTERN);
log.info("resolveArgument tmp {} res {}", tmp, DateTimeUtils.format(res, DateTimeUtils.SHORT_DISPLAY_PATTERN));
return res;
}
}
第三步,注册自定义的解析器
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new MyHandlerMethodArgumentResolver());
}
}
第四步,通过注解使用自定义解析器
@GetMapping(value = "/test")
@ApiOperation("测试")
public BaseResponse<Boolean> test(@DateParam(name = "date") Date request) {
String userName = UserBasicInfoContext.getUserBasicInfo().getUserName();
log.info("PushController bindUserAndClient request is {} userName is {}", JsonUtils.toJson(request), userName);
return BaseResponse.ofSuccess(true);
}
自定义异常处理
使用场景:自定义controller全局异常处理,用于优化返回给前端的错误信息。
核心:@RestControllerAdvice + @ExceptionHandler
@RestController
@Slf4j
@Order(0)
public class MyControllerAdvice {
@Autowired(required = false)
private HttpServletRequest req;
@ExceptionHandler(BusinessException.class)
@ResponseStatus(value = HttpStatus.OK)
public BaseResponse<Object> handleBusinessException(BusinessException exception) {
req.setAttribute(CatConstants.CAT_STATE, exception.getClass().getName());
log.error("Handling business exception", exception);
return BaseResponse
.fail()
.errorCode(exception.getCode())
.errorMsg(exception.getMessage())
.build();
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public BaseResponse<Object> handleValidException(Exception e, @Autowired HttpServletRequest request) {
ConstraintViolationException mae = (ConstraintViolationException) e;
request.setAttribute(CatConstants.CAT_STATE, mae.getClass().getName());
String errorMsg = mae.getConstraintViolations().isEmpty() ?
"invalid params" : Lists.newArrayList(mae.getConstraintViolations()).get(0).getMessage();
log.warn("handleValidException request not valid {}", errorMsg);
return BaseResponse.ofFail(ErrorCode.BAD_PARAMS.getCode(), errorMsg);
}
}