Spring 的 ResponseBodyAdvice:执行顺序?

Posted

技术标签:

【中文标题】Spring 的 ResponseBodyAdvice:执行顺序?【英文标题】:Spring's ResponseBodyAdvice: Order of execution? 【发布时间】:2015-04-15 05:19:29 【问题描述】:

我想实现一个自定义ResponseBodyAdvice,它只是查找Page<?>,然后将总元素数添加到响应标头中。

@ControllerAdvice
public class PageResponseAdvice implements ResponseBodyAdvice<Object> 

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) 
        return Page.class.isAssignableFrom(returnType.getParameterType());
    

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) 
        ((Page<?>) body).getTotalElements();
        ...
    


问题是:

为什么body(在beforeBodyWrite 方法中)是MappingJacksonValue 类型的?

有没有更好的方法来实现这一点? / 我是否使用了错误的拦截器?

我不想处理包装类,尽管我只想要 beforeBodyWrite 方法中的普通未修改的 Page 对象。


编辑:

我现在只是extend AbstractMappingJacksonResponseBodyAdvice。这很好用,但 感觉 不对。也许有人还有更好的主意。

这是Page -&gt; Content-Range Header的代码:

@ControllerAdvice
public class PageResponseAdvice extends AbstractMappingJacksonResponseBodyAdvice 
    @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) 
        return super.supports(returnType, converterType) && Page.class.isAssignableFrom(returnType.getParameterType());
    

    @Override protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) 
        Page<?> page = ((Page<?>) bodyContainer.getValue());
        Long from = null;
        Long to = null;
        if(page.getTotalElements() > 0 && page.getNumberOfElements() > 0) 
            from = Integer.valueOf(page.getNumber()).longValue()*page.getSize();
            to = from + page.getNumberOfElements() - 1;
        
        response.getHeaders().add(
                HttpHeaders.CONTENT_RANGE,
                ContentRangeEncoder.encode(
                        "items",
                        from,
                        to,
                        page.getTotalElements()
                )
        );
        response.getHeaders().add(
                HttpHeaders.ACCEPT_RANGES,
                "items"
        );
    

(如果有人感兴趣。这里是ContentRangeEncoder):

public class ContentRangeEncoder 
    private static final Pattern TYPE_PATTERN = Pattern.compile("[a-zA-Z0-9]+");
    private static final Predicate<String> TYPE_PATTERN_PREDICATE = TYPE_PATTERN.asPredicate();

    public static <T extends Number & Comparable<T>> String encode(String unit, T from, T to, T length) 
        StringBuilder sb = new StringBuilder();
        if(unit != null) 
            Assert.isTrue(TYPE_PATTERN_PREDICATE.test(unit));
            sb.append(unit).append(" ");
        

        if(from == null && to == null) 
            sb.append("*");
         else 
            Assert.notNull(from);
            Assert.notNull(to);
            Assert.isTrue(from.compareTo(to) <= 0);
            sb.append(from).append("-").append(to);
        
        sb.append("/");
        if(length == null) 
            sb.append("*");
         else 
            Assert.isTrue(to == null || length.compareTo(to) > 0);
            sb.append(length);
        
        return sb.toString();
    

【问题讨论】:

【参考方案1】:

至于为什么 body 是 MappingJacksonValue 类型,这取决于你的配置和你的特定处理程序方法的返回值。

作为旁注,WebMvcConfigurationSupportJsonViewResponseBodyAdvice 添加到RequestMappingHandlerAdapter,如果这个ResponseBodyAdvice bean 在你之前被调用,这就解释了为什么body 是MappingJacksonValue。主体从一个 ResponseBodyAdvice bean 传递到下一个,每个都以它认为合适的任何“形式”返回主体。

这是JsonViewResponseBodyAdvice 的超类“AbstractMappingJacksonResponseBodyAdvice”的摘录,看看身体会发生什么:

public final Object beforeBodyWrite(@Nullable Object body,
    MethodParameter returnType, ..., ..., ..., ...) 

    //...

    MappingJacksonValue container = getOrCreateContainer(body);

    //...

    return container;

【讨论】:

以上是关于Spring 的 ResponseBodyAdvice:执行顺序?的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记——Spring简介;Spring搭建步骤;Spring的特性;Spring中getBean三种方式;Spring中的标签

Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC

Spring--Spring入门

Spring:Spring介绍

Spring浅析Spring框架的搭建

Spring DAO vs Spring ORM vs Spring JDBC