返回值解析逻辑

Restlight默认支持的返回值解析方式包括

  • @ResponseBody
  • @ResponseStatus
  • 普通类型(String, byte[], int 等)

与参数解析类似, 每个功能都对应了一个返回值解析器的实现。

接口定义

public interface ResponseEntityResolver {

    /**
     * 解析出对应返回值并通过channel写回
     */
    HandledValue<Void> writeTo(ResponseEntity entity,
                               ResponseEntityChannel channel,
                               RequestContext context) throws Exception;

}

框架会在启动的初始化阶段试图为每一个Controller中的每一个参数都找到一组与之匹配的ResponseEntityResolver用于响应的返回值解析。

框架如何确定ResponseEntityResolver

ResponseEntityResolverAdapter


public interface HandlerPredicate {

    /**
     * 判断当前ResponseEntityResolver是否支持给定HandlerMethod解析
     * 每一个Controller都对应一个HandlerMethod实例, 
     * 可以通过HandlerMethod获取注解, 返回值类型等各类反射相关的元数据信息
     */
    boolean supports(HandlerMethod method);

}

public interface ResponseEntityResolverAdapter extends ResponseEntityResolver, HandlerPredicate, Ordered {

    @Override
    default int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

初始化逻辑:

  1. 按照getOrder()方法的返回将Spring容器中所有的ResponseEntityResolverAdapter进行排序
  2. 按照排序后的顺序依次调用supports(HandlerMethod method)方法, 返回true则将其作为该参数的ResponseEntityResolver,运行时每次请求都将按顺序调用writeTo(ResponseEntity entity, ResponseEntityChannel channel, RequestContext context)方法进行返回值处理,直至处理成功。
  3. 未找到则启动报错。

细心的人可能会发现该设计可能并不能覆盖到以下场景

  • 因为writeTo(ResponseEntity entity, ResponseEntityChannel channel, RequestContext context)方法参数中并没有传递HandlerMethod参数,虽然初始化阶段能根据HandlerMethod method方法获取Controller方法元数据信息(获取某个注解,获取参数类型等等)判断是否支持,但是如果运行时也需要获取Controller方法的元数据信息(某个注解的值等)的话,此接口则无法满足需求。
  • 假如ResponseEntityResolverAdapter实现中需要做序列化操作,因此期望获取到Spring容器中的序列化器时,则该接口无法支持。

针对以上问题, 答案是确实无法支持。因为Restlight的设计理念是

  • 能在初始化阶段解决的问题就在初始化阶段解决

因此不期望用户以及Restlight的开发人员大量的在运行时去频繁获取一些JVM启动后就不会变动的内容(如: 注解的值), 甚至针对某些元数据信息使用ConcurrentHashMap进行缓存(看似是为了提高性能的缓存, 实际上初始化就固定了的内容反而增加了并发性能的损耗)。

基于以上原因我们提供了另一个ResponseEntityResolver的实现方式

ResponseEntityResolverFactory

public interface ResponseEntityResolverFactory extends HandlerPredicate, Ordered {

    ResponseEntityResolver createResolver(HandlerMethod method,
                                          List<? extends HttpResponseSerializer> serializers);

    @Override
    default int getOrder() {
        return HIGHEST_PRECEDENCE;
    }
}

与上面的ResponseEntityResolver类似

初始化逻辑:

  1. 按照getOrder()方法的返回将所有的ResponseEntityResolverFactory进行排序
  2. 按照排序后的顺序依次调用supports(HandlerMethod method)方法,返回true则将其作为该参数的ResponseEntityResolverFactory,同时调用createResolver(HandlerMethod method, List<? extends HttpResponseSerializer> serializers)方法创建出对应的ResponseEntityResolver
  3. 未找到则启动报错。

由于初始化时通过createResolver(HandlerMethod method, List<? extends HttpResponseSerializer> serializers)方法传入了HandlerMethod以及序列化器,因此能满足上面的要求。

两种模式的定位

  • ResponseEntityResolver: 适用于解析器不依赖方法元数据信息以及序列化的场景。例如: 如果Controller方法上上使用了@XXX注解则返回某个固定的值。
  • ResponseEntityResolverFactory: 适用于解析器依赖方法元数据信息以及序列化的场景。例如: @ResponseBody, @ResponseStatus(reason = “error”)。

自定义返回值解析器

将自定义实现的ResponseEntityResolverAdapter或者ResponseEntityResolverFactory注入到Spring容器即可。

  • ResponseEntityResolverAdapter案例

场景: 当Controller方法上有@AppId注解时, 返回固定的AppId

// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AppId {
}
@Bean
public ResponseEntityResolver resolver() {

    private static byte[] APP_ID = "your appid".getBytes(StandardCharsets.UTF_8);

    return new AbstractResponseEntityResolver() {

        @Override
        protected byte[] serialize(ResponseEntity entity,
                                   List<MediaType> mediaTypes,
                                   RequestContext context) {
            return APP_ID;
        }
        
        @Override
        public boolean supports(HandlerMethod method) {
            // 当方法上有此注解时生效
            return method.hasMethodAnnotation(AppId.class);
        }
    };
}

controller使用


@GetMapping("/foo")
@AppId
public String foo() {
    return "";
}

上面的代码自定义实现了依据自定义注解获取固定appId的功能

  • ResponseEntityResolverFactory

场景: 通过自定义注解对所有String类型的返回值加上一个指定前缀。

// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Suffix {
    String value();
}
    @Bean
    public ResponseEntityResolverFactory resolver() {
        return new ResponseEntityResolverFactory() {
    
            @Override
            public ResponseEntityResolver createResolver(HandlerMethod method,
                                                         List<? extends HttpResponseSerializer> serializers) {
                return new Resovler(method);
            }
            
            @Override
            public boolean supports(HandlerMethod method) {
                return String.class.equals(method.method().getReturnType()) 
                        && method.hasMethodAnnotation(CustomHeader.class, false);
            }
        };
    }

    /**
     * 实际ResponseEntityResolver实现
     */
    private static class Resolver extends AbstractResponseEntityResolver {

        private final String suffix;

        private Resolver(HandlerMethod method) {
            // 获取前缀
            Suffix anno = method.getMethodAnnotation(Suffix.class, false);
            this.suffix = anno.value();
        }

        @Override
        protected byte[] serialize(ResponseEntity entity,
                                   List<MediaType> mediaTypes,
                                   RequestContext context) {
            // 拼接
            return (suffix + entity.response().entity()).getBytes(StandardCharsets.UTF_8);
        }

    }

controller使用


@GetMapping("/foo")
@Suffix
public String foo() {
    return "foo";
}