典型的有

  • @RequestBody

接口定义

public interface RequestEntityResolver extends Resolver {

    /**
     * Deserialize the given {@code entity} to result.
     *
     * @param entity  entity
     * @param context context
     * @return resolved value, which must not be null.
     * @throws Exception any exception
     */
    HandledValue<Object> readFrom(RequestEntity entity, RequestContext context) throws Exception;

}

框架会在启动的初始化阶段试图为每一个Controller中的每一个参数都找到一个与之匹配的RequestEntityResolver用于请求的参数解析。

框架如何确定RequestEntityResolver

RequestEntityResolverAdapter


public interface ParamPredicate {

    /**
     * Whether current {@link ParamResolver} implementation is support given parameter.
     *
     * @param param param
     * @return {@code true} if it supports
     */
    boolean supports(Param param);

}

public interface RequestEntityResolverAdapter extends RequestEntityResolver, ParamPredicate, Ordered {

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

}

初始化逻辑:

  1. 按照getOrder()方法的返回将Spring容器中所有的RequestEntityResolver进行排序
  2. 按照排序后的顺序依次调用supports(Param param)方法, 返回true则将其作为该参数的RequestEntityResolver,运行时每次请求都将按优先级顺序调用readFrom(RequestEntity entity, RequestContext context)方法进行参数解析,直至处理成功。
  3. 未找到则启动报错。

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

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

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

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

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

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

ParamResolverFactory

public interface RequestEntityResolverFactory extends ParamPredicate, Ordered {

    RequestEntityResolver createResolver(Param param,
                                         StringConverterProvider converters,
                                         List<? extends HttpRequestSerializer> serializers);

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

与上面的RequestEntityResolver类似

初始化逻辑:

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

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

两种模式的定位

  • RequestEntityResolver: 适用于参数解析器不依赖方法元数据信息以及序列化的场景。例如: 如果参数上使用了@XXX注解则返回某个固定的值。
  • RequestEntityResolverFactory: 适用于参数解析器依赖方法元数据信息以及序列化的场景。

自定义参数解析器

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

  • RequestEntityResolverAdapter案例

场景: 当参数上有@Pojo注解时,使用固定的pojo

// 自定义注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Pojo {
}
private static final Pojo pojo = new Pojo();

@Bean
public RequestEntityResolver resolver() {

    return new RequestEntityResolverAdapter() {
        @Override
        public HandledValue<Object> readFrom(RequestEntity entity, RequestContext context) throws Exception {
            return HandledValue.succeed(pojo);
        }

        @Override
        public boolean supports(Param param) {
            return param.hasAnnotation(Pojo.class);
        }
    };
}

controller使用


@GetMapping("/foo")
public Pojo foo(@Pojo Pojo pojo) {
    return pojo;
}

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

  • RequestEntityResolverFactory

场景: 通过自定义注解解析请求实体数据并将将解析后的POJO的name设置为固定值。

// 自定义注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomPojo {
    String value();
}
@Bean
public RequestEntityResolverFactory resolver() {
    return new RequestEntityResolverFactory() {
        @Override
        public RequestEntityResolver createResolver(Param param,
                                                    StringConverterProvider converters,
                                                    List<? extends HttpRequestSerializer> serializers) {
            return new Resolver(param);
        }

        @Override
        public boolean supports(Param param) {
            // 当方法上有此注解时生效
            return param.hasAnnotation(CustomPojo.class);
        }
    };
}

/**
 * 实际RequestEntityResolver实现
 */
private static class Resolver implements RequestEntityResolver {

    private final HttpRequestSerializer serializer;
    private final String fixedName;

    private Resolver(HttpRequestSerializer serializer, Param param) {
        CustomPojo anno = param.getAnnotation(CustomPojo.class);
        if (anno.value().length() == 0) {
            throw new IllegalArgumentException("Name of header must not be empty.");
        }
        // 初始化时组装好需要的参数
        this.fixedName = anno.value();
        this.serializer = serializer;
    }

    @Override
    public HandledValue<Object> readFrom(RequestEntity entity, RequestContext context) throws Exception {
        HandledValue<Object> handled = serializer.deserialize(entity);
        if (handled.isSuccess()) {
            Pojo pojo = (Pojo) handled.value();
            pojo.setName(fixedName);
        }
        return handled;
    }

}

controller使用


@GetMapping("/foo")
public Pojo foo(@CustomPojo("foo") Pojo foo) {
    return foo;
}