目录

一、简介

二、@ControllerAdvice注解的处理获取

1.@ControllerAdvice注解

2.ControllerAdviceBean类

三、Request请求和Response响应对应接口切面

1.RequestBodyAdvice接口

2.ResponseBodyAdvice接口

四、Advice切面的读取和调用原理

1.RequestMappingHandlerAdapter类读取@ControllerAdvice注解类

2.RequestResponseBodyAdviceChain解析类

3.具体实现源码分析

3.1 使用HandlerMethodArgumentResolverComposite处理RequestBodyAdvice接口

3.2 HandlerMethodReturnValueHandlerComposite处理ResponseBodyAdvice接口

3.3 RequestResponseBodyMethodProcessor处理两种Advice切面接口


一、简介

@ControllerAdvice注解从名字上就可以看出来这个是针对Controller的切面增强处理注解,类似与@Controller和@RestController一样,@ControllerAdvice也有对应的@RestControllerAdvice注解用来返回序列化之后的对象。

@ControllerAdvice实际上也是一个Component,因为该注解同时也被@Component注解,搭配这个注解通常有一个注解和两个接口:

  • @ExceptionHandler注解:统一处理从Controller抛出的异常,在@ControllerAdvice中可以拥有多个@ExceptionHandler和实现方法,SpringMVC将会自动判断哪个异常优先处理;
  • RequestBodyAdvice接口:用来对Request请求方法体和对应方法参数进行处理,如果方法参数有多个,将会顺序调用到接口方法中来;
  • ResponseBodyAdvice接口:类似RequestBodyAdvice,只是处理的对象变成了Response响应,处理的对象包括请求体和返回类型。

另外需要注意的是,@ControllerAdvice注解是一定需要搭配其它的注解或者接口使用的,否则就算这个注解类被识别到,没有具体的处理方法,SpringMVC是不会进行任何操作的。

二、@ControllerAdvice注解的处理获取

1.@ControllerAdvice注解

在分析具体实现原理前先看到其源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {// 和下面的basePackages代表的是同一个意思,只针对某些包下的类@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};// 只处理某些类所在包路径Class<?>[] basePackageClasses() default {};// 处理某些类和这些类的子类Class<?>[] assignableTypes() default {};// 处理被某些注解过的类Class<? extends Annotation>[] annotations() default {};
}

2.ControllerAdviceBean类

这个类便是@ControllerAdvice注解对应的实体类,在这个类里面将会完成从bean工厂中获取所有被@ControllerAdvice注解过的bean,并读取注解属性将其全部返回的操作。部分关键源码如下:

public class ControllerAdviceBean implements Ordered {// 这个bean指的就是被@ControllerAdvice注解的类对象private final Object bean;// bean工厂,要从bean工厂中获取bean和判断bean的类型@Nullableprivate final BeanFactory beanFactory;// 这个类有兴趣的可以去看下,代码不多,大致作用便是存放并且判断注解的属性private final HandlerTypePredicate beanTypePredicate;private ControllerAdviceBean(Object bean, @Nullable BeanFactory beanFactory) {this.bean = bean;this.beanFactory = beanFactory;Class<?> beanType;// 设置bean和beanType等属性if (bean instanceof String) {String beanName = (String) bean;beanType = this.beanFactory.getType(beanName);this.order = initOrderFromBeanType(beanType);} else {beanType = bean.getClass();this.order = initOrderFromBean(bean);}// 从bean类型获取ControllerAdvice注解ControllerAdvice annotation = (beanType != null ?AnnotatedElementUtils.findMergedAnnotation(beanType, ControllerAdvice.class) : null);// 如果注解不为空则将注解的属性通过HandlerTypePredicate的构造器放入到// HandlerTypePredicate对象中,以便后续判断注解属性if (annotation != null) {this.beanTypePredicate = HandlerTypePredicate.builder().basePackage(annotation.basePackages()).basePackageClass(annotation.basePackageClasses()).assignableType(annotation.assignableTypes()).annotation(annotation.annotations()).build();} else {// 创建一个默认的HandlerTypePredicate对象this.beanTypePredicate = HandlerTypePredicate.forAnyHandlerType();}}public boolean isApplicableToBeanType(@Nullable Class<?> beanType) {// 这个便是外部调用判断注解属性是否满足要求的方法return this.beanTypePredicate.test(beanType);}public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {// 这个方法大致作用便是从bean工厂中获取所有被@ControllerAdvice注解过的// bean对象,因为ControllerAdvice中也有@Component注解,当获得bean之后// 再把这个bean使用ControllerAdviceBean的构造函数实例化return Arrays.stream(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)).filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null).map(name -> new ControllerAdviceBean(name, context)).collect(Collectors.toList());}
}

三、Request请求和Response响应对应接口切面

其大致流程如下:

前面说过Request请求和Response响应都会有一个对应的接口切面,接下来先看下其接口方法。

1.RequestBodyAdvice接口

其源码如下:

public interface RequestBodyAdvice {// 在调用本接口三个对方法体和参数进行处理的方法前都需要调用supports方法来// 判断是否支持该方法boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType);// 如果方法体不为空,则在读取方法体前先执行该方法HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;// 在已经读取了方法体的内容后调用的方法,可以直接对方法体进行操作Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType);// 如果方法体为空所进行的处理@NullableObject handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
}

2.ResponseBodyAdvice接口

这个接口相比于RequestBodyAdvice要简单很多,其源码如下:

public interface ResponseBodyAdvice<T> {// 在调用本接口对返回方法体进行处理的方法前都需要调用supports方法来// 判断是否支持该方法boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);// 接口只有一个操作方法,具体可以操作的对象有返回方法体、方法返回类型和选择的// 内容类型,如是Json或者文件等@NullableT beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response);
}

四、Advice切面的读取和调用原理

1.RequestMappingHandlerAdapter类读取@ControllerAdvice注解类

其读取@ControllerAdvice注解获取切面类源码如下:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {// 保存解析参数的解析器,RequestBodyAdvice接口将会在这里面完成处理@Nullableprivate HandlerMethodArgumentResolverComposite argumentResolvers;// 保存解析方法返回值的处理器,ResponseBodyAdvice接口将会在这里面完成处理@Nullableprivate HandlerMethodReturnValueHandlerComposite returnValueHandlers;// 具体保存RequestBodyAdvice和ResponseBodyAdvice接口实现类的集合对象private List<Object> requestResponseBodyAdvice = new ArrayList<>();@Overridepublic void afterPropertiesSet() {// 首先调用该方法去获取所有被@ControllerAdvice注解且符合是两个Advice// 切面实现类,@RequestMapping、@ModelAttribute和@InitBinder略过initControllerAdviceCache();// 如果参数解析器集合为空则获取默认的if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}...// 如果返回值处理器集合为空则获取默认的if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}private void initControllerAdviceCache() {// 如果spring上下文为空则无需进行下面的操作,因为都是基于bean工厂操作的if (getApplicationContext() == null) {return;}// 调用ControllerAdviceBean的方法,获取bean工厂中所有@ControllerAdvice// 注解bean对象List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());// 进行排序,排序的依据便是根据@Order等排序类来完成AnnotationAwareOrderComparator.sort(adviceBeans);List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();// 对获取到的@ControllerAdvice注解类进行判断for (ControllerAdviceBean adviceBean : adviceBeans) {Class<?> beanType = adviceBean.getBeanType();if (beanType == null) {throw new IllegalStateException();}...// 如果实现类的类型是RequestBodyAdvice和ResponseBodyAdvice接口的// 实现子类则将其添加到保存其对象的集合中if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {requestResponseBodyAdviceBeans.add(adviceBean);}}// 不为空则添加到保存两种切面集合中if (!requestResponseBodyAdviceBeans.isEmpty()) {this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);}}private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();// 等下只展示使用到了两种Advice切面的类...// 对Request处理的重要对象,基本上都是通过这个类完成处理的resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));// 这个类是用来处理@RequestPart注解的解析类resolvers.add(new RequestPartMethodArgumentResolvergetMessageConverters(), this.requestResponseBodyAdvice));...// 对参数和返回值是HttpEntity和RequestEntity类型进行处理的类resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.requestResponseBodyAdvice));...return resolvers;}private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {// 等下只展示使用到了两种Advice切面的类...// 对参数和返回值是HttpEntity和RequestEntity类型进行处理的类handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));...// 对Response处理的重要对象,基本上都是通过这个类完成处理的handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager,this.requestResponseBodyAdvice));...return handlers;}
}

2.RequestResponseBodyAdviceChain解析类

这个类的大致作用便是存储前面获得的requestResponseBodyAdvice集合对象,并提供只获取RequestBodyAdvice和ResponseBodyAdvice接口的方法,起到管理两类接口实现类的作用。其部分关键源码如下:

class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyAdvice<Object> {// 保存RequestBodyAdvice接口实现类的集合对象private final List<Object> requestBodyAdvice = new ArrayList<>(4);// 保存ResponseBodyAdvice接口实现类的集合对象private final List<Object> responseBodyAdvice = new ArrayList<>(4);public RequestResponseBodyAdviceChain(@Nullable List<Object> requestResponseBodyAdvice) {// 分别获取集合中的RequestBodyAdvice和ResponseBodyAdvice接口实现类// 并添加到对应的集合中,getAdviceByType方法则是对传入的集合遍历// 一一判断是不是类是不是集合元素的父类接口,在此略过实现方法this.requestBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, RequestBodyAdvice.class));this.responseBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, ResponseBodyAdvice.class));}private <A> List<A> getMatchingAdvice(MethodParameter parameter,Class<? extends A> adviceType) {// 这个是等下要分析几个方法都会都用的方法,因此先大致分析一遍// getAdvice方法是根据adviceType类型去分别获取requestBodyAdvice或者// responseBodyAdvice集合List<Object> availableAdvice = getAdvice(adviceType);// 如果切面集合为空则直接返回空集合if (CollectionUtils.isEmpty(availableAdvice)) {return Collections.emptyList();}List<A> result = new ArrayList<>(availableAdvice.size());for (Object advice : availableAdvice) {// 如果切面是被@ControllerAdvice注解的类if (advice instanceof ControllerAdviceBean) {ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;// 转换成ControllerAdviceBean类型去调用isApplicableToBeanType// 方法判断当前参数对象是否满足处理条件if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) {continue;}// 如果满足则获取切面beanadvice = adviceBean.resolveBean();}// 判断切面bean是否是要获取类的实现子类,是则添加if (adviceType.isAssignableFrom(advice.getClass())) {result.add((A) advice);}}// 返回结果集return result;}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {// RequestBodyAdvice几个方法的大致执行流程都差不多,因此只取一个分析// 获取满足条件的切面beanfor (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {// 一一调用切面bean的supports方法判断是否满足条件if (advice.supports(parameter, targetType, converterType)) {// 若满足则调用切面bean的相应方法request = advice.beforeBodyRead(request, parameter, targetType, converterType);}}return request;}@Override@Nullablepublic Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,Class<? extends HttpMessageConverter<?>> converterType,ServerHttpRequest request, ServerHttpResponse response) {// 调用processBody方法实现return processBody(body, returnType, contentType, converterType, request, response);}@Nullableprivate <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,Class<? extends HttpMessageConverter<?>> converterType,ServerHttpRequest request, ServerHttpResponse response) {// 和RequestBodyAdvice的执行流程差不多// 获取满足条件的切面beanfor (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {// 一一调用切面bean的supports方法判断是否满足条件if (advice.supports(returnType, converterType)) {// 若满足则调用切面bean的相应方法body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType, contentType,converterType, request, response);}}return body;}
}

3.具体实现源码分析

因为两个接口都是从ServletInvocableHandlerMethod类进去的,因此从这个类开始分析起:

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 在invokeForRequest这个方法里将会解析参数并调用实际的处理方法处理请求// RequestBodyAdvice接口便是在解析参数时被调用的Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 处理@ResponseStatus注解setResponseStatus(webRequest);// 处理返回方法值的,略过...try {// 调用handleReturnValue方法来处理方法返回值,在这里面将会调用// ResponseBodyAdvice接口方法this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);} catch (Exception ex) {throw ex;}}@Nullablepublic Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 解析参数Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);// 调用实际的bean方法处理请求return doInvoke(args);}protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 获取方法参数数组MethodParameter[] parameters = getMethodParameters();// 参数为空则直接返回if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {// 获得方法参数后开始遍历参数MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}// 判断resolvers是否支持该参数,如果不支持则抛出异常if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException();}try {// 支持处理该参数则调用解析器解析参数,RequestBodyAdvice将被处理args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);} catch (Exception ex) {throw ex;}}return args;}
}

3.1 使用HandlerMethodArgumentResolverComposite处理RequestBodyAdvice接口

直接从该类的resolveArgument方法开始分析,其源码如下:

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {// 封装的解析器集合private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();// 方法参数和对应解析器的缓存集合private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);@Override@Nullablepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception{// 根据参数类型获取具体的参数解析器将会遍历集合调用解析器的// supportsParameter方法来判断HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException();}// 调用具体的解析器解析参数,这里的resolver类型将是// RequestResponseBodyMethodProcessor类return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}@Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {// 先从缓存中获取HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {// 如果缓存为空则遍历解析器来判断哪个满足条件for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {// 调用解析器的supportsParameter方法判断参数是否支持if (methodArgumentResolver.supportsParameter(parameter)) {result = methodArgumentResolver;// 把参数类型和解析器的对应关系放到缓存中this.argumentResolverCache.put(parameter, result);break;}}}return result;}
}

3.2 HandlerMethodReturnValueHandlerComposite处理ResponseBodyAdvice接口

直接从handleReturnValue方法开始分析,其源码如下:

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {// 封装的返回参数处理器集合对象private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,NativeWebRequest webRequest) throws Exception {// 类似前面的参数解析器一样,这里先获取具体的处理器,支持没有缓存// 每次获取都需要从结果处理器集合中遍历获取HandlerMethodReturnValueHandler handler = selectHandler(returnValue,returnType);if (handler == null) {throw new IllegalArgumentException();}// 调用真正的处理器处理返回值handler.handleReturnValue(returnValue, returnType, mavContainer,webRequest);}@Nullableprivate HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {// 首先判断是否是异步的返回值boolean isAsyncValue = isAsyncReturnValue(value, returnType);for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {// 如果是异步的返回值和不是AsyncHandlerMethodReturnValueHandler类型// 则跳过本次循环if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {continue;}// 调用处理器的supportsReturnType方法判断是否支持返回值类型if (handler.supportsReturnType(returnType)) {return handler;}}return null;}
}

3.3 RequestResponseBodyMethodProcessor处理两种Advice切面接口

前面两步分析了,解析参数和处理返回值都会使用到RequestResponseBodyMethodProcessor对象,因此具体解析RequestBodyAdvice和ResponseBodyAdvice接口的逻辑将会在这里面完成。其部分关键源码如下:

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {/*** 这是处理RequestBodyAdvice接口的方法入口*/@Overridepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception{// 解析RequestBodyAdvice接口的方法入口parameter = parameter.nestedIfOptional();// 调用readWithMessageConverters读取解析方法参数和方法体Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());// 后面的略过,解析RequestBodyAdvice接口只在readWithMessageConverters// 方法中...return adaptArgumentIfNecessary(arg, parameter);}@Overrideprotected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {// 将HttpRequest封装成特定的Request对象,最终的request对象将会// 实现HttpInputMessage接口HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);// 将封装后的inputMessage对象传入readWithMessageConverters方法解析Object arg = readWithMessageConverters(inputMessage, parameter, paramType);if (arg == null && checkRequired(parameter)) {throw new HttpMessageNotReadableException();}return arg;}@Nullableprotected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {// 前面是获取判断contentType和参数class类型的逻辑,略过... EmptyBodyCheckingHttpInputMessage message;try {// 先使用EmptyBodyCheckingHttpInputMessage封装inputMessage对象message = new EmptyBodyCheckingHttpInputMessage(inputMessage);// 遍历messageConverters对象for (HttpMessageConverter<?> converter : this.messageConverters){// 先获取内容转换器的class对象类型Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();// 转换成GenericHttpMessageConverter类型GenericHttpMessageConverter<?> genericConverter =(converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);// 如果这个内容转换器不为空且可以转换当前文本类型if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :(targetClass != null && converter.canRead(targetClass, contentType))) {// 如果内容的方法体不为空if (message.hasBody()) {// 先执行beforeBodyRead方法HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);// 使用转换器将经过advice切面前置处理的内容转换成方法体body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));// 对经过转换器转正具体的方法体对象进行后置处理body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);} else {// 如果方法体为空则调用handleEmptyBody处理方法体为空的情况body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);}break;}}}// 对获得的body方法体进行特殊的判断return body;}// -------------------------方法分割线----------------------------/*** 这是处理ResponseBodyAdvice接口的方法入口*/@Overridepublic void handleReturnValue(@Nullable Object returnValue,MethodParameter returnType, ModelAndViewContainer mavContainer,NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// 使用ResponseBodyAdvice切面处理返回值的方法入口mavContainer.setRequestHandled(true);// 根据webRequest分别创建对应的inputMessage和outputMessage对象ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// 真正执行ResponseBodyAdvice切面增强接口方法的方法writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {// 前面是对body方法体的大致判断如String类型的判断和内容类型...if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();// 遍历内容转换器for (HttpMessageConverter<?> converter : this.messageConverters) {GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);// 如果转换器不为空且这种的内容类型可以转换if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {// 获取ResponseBodyAdvice切面调用其处理方法body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);// 后面略过...return;}}}...}
}

至此,对于RequestBodyAdvice和ResponseBodyAdvice接口的处理调用源码便分析完毕,其中需要注意的是getAdvice()方法返回的对象类型是RequestResponseBodyAdviceChain,这里的处理逻辑都是在RequestResponseBodyAdviceChain中完成的。

SpringMVC框架中@ControllerAdvice和对应Advice切面使用原理相关推荐

  1. SpringMVC框架中注解的详细介绍

    目录 前言 1. @Controller注解(用在类上面) 2. @RequestMapping(用在类上面) 3. @RequestMapping(用在方法上面) 4.  @ResponseBody ...

  2. SpringMVC框架中的异常处理机制

    目录 1. 什么是异常处理? 2. SpringMVC框架中的异常处理机制是什么? 3. SpringMVC框架汇中实现异常处理的实现步骤  4. SpringMVC框架出现异常时候的处理过程  5. ...

  3. 解决SpringMvc框架中提示的“Required request body is missing”异常。

    之前没搞过Web后台,现在刚接手后台项目就遇到了这种异常问题,做次记录,避免更多的人跳坑. 瞎搞了两天才找到问题原因也是没谁了. 出现该"Required request body is m ...

  4. SpringMVC框架中ModelAndView、Model、ModelMap的区别与使用

    1. Model Model 是一个接口, 其实现类为ExtendedModelMap,继承了ModelMap类. public class ExtendedModelMap extends Mode ...

  5. java中mapper层作用_Java的MyBatis框架中Mapper映射配置的使用及原理解析

    Mapper的内置方法model层就是实体类,对应数据库的表.controller层是Servlet,主要是负责业务模块流程的控制,调用service接口的方法,在struts2就是Action.Se ...

  6. 框架 - SpringMVC框架

    框架 - SpringMVC框架 第一章 SpringMVC概述 1.1 SpringMVC简介 1.2 SpringMVC 优点 1.3 第一个注解的 SpringMVC程序 1.4 SpringM ...

  7. SpringMVC框架 学习DAY_03:@RequestMapping注解/拦截器与过滤器

    1. 关于@RequestMapping注解 在控制器中,在处理请求的方法之前添加@RequestMapping注解,可以配置请求路径与处理请求的方法的映射关系! 在@RequestMapping注解 ...

  8. SpringMVC框架 学习DAY_01:框架概括 / 简易应用 / 核心执行流程图 /在框架下显示HTML模板页面/ 接受请求

    1. SpringMVC框架的作用 MVC = Model(数据模型) + View(视图) + Controller(控制器) SpringMVC框架主要解决了接收请求与处理响应的问题,也可以认为是 ...

  9. Java EE——SpringMVC框架学习

    文章目录 一.SpringMVC的基本概念: 1.三层架构和MVC: 2.SpringMVC的概述: 3.SpringMVC在三层架构的位置: 二.SpringMVC 的入门: 三.入门案例的执行过程 ...

最新文章

  1. 搞懂限流算法这一篇就够了 No.154
  2. linq where 数组_C#中LINQ与数据管道
  3. 2140: 学无止境(差分)
  4. linux iptables 如何设置允许几个 ip访问,Linux防火墙iptables限制几个特定ip才能访问服务器。...
  5. TeeChart中 Line的Clear方法
  6. FZU - 2268 Cutting Game
  7. AlphaFold2立功!清华团队用深度学习增强新冠抗体,创AI里程碑
  8. ubuntu16.04安装teamviewer12
  9. Springboot+idea的一个bug(Unregistering JMX-exposed beans on shutdown)
  10. [编织消息框架][消息处理模式]管道模式
  11. 在 Mac 上如何使用时间机器备份文件?
  12. 彭亚雄:7月24日阿里云上海峰会企业存储大神
  13. Vue移动端项目模板h5
  14. MacBookPro外接显示器开启HiDPI
  15. 从一篇防范钓鱼邮件的通知说起
  16. 【动态规划】黑熊过河
  17. Word 2003 出现 向程序发送命令时出现问题 的 解决方案
  18. 论文导读:DINO -自监督视觉Transformers
  19. 设计一套简单的计算机系统及其指令系统,【精品】计算机组成综合设计指导书...
  20. Linux命令:netstat【监控TCP/IP网络,可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息】【TCP的11种状态】

热门文章

  1. MacBook Pro 触控板目前没有触感反馈怎么办?
  2. BSV 上的 zk-SNARKs
  3. 对ArrayList集合里面数据排序
  4. Android Google原生系统刷机
  5. 声源定位系统设计(一)——MVDR波束形成算法
  6. windows 下查看防火墙状态命令
  7. VMware的更新怎么这么恶心
  8. Ubuntu虚拟机中网络中没有网卡
  9. 区块链DAPP开发 以太坊智能合约框架有哪些
  10. Element UI Table表格样式调整