提问人:topgun 提问时间:6/24/2022 最后编辑:topgun 更新时间:6/25/2022 访问量:280
Java8 - 对象类型 - 动态添加更多属性
Java8 - Object Type - Dynamically add more property
问:
我想从 http 调用的回复中获取标头值,将其存储在缓存中,并通过将其传递给 http 请求的标头来在下一个请求中使用此值,以实现粘性会话。我的项目是用Java 8编写的。
我有这样的假装 http 客户端构建器,它使用 JacksonDecoder 进行解码。
public <T> T buildClient(Class<T> type, String url) {
return Feign.builder()
.contract(new JAXRSContract())
.encoder(new JacksonEncoder(mapper))
.decoder(new CustomDecoder(mapper))
.logger(new Slf4jLogger(Constants.FFA_LOGGER_NAME))
.logLevel(Level.FULL)
.options(new Options(connectTimeout, readTimeout))
.retryer(Retryer.NEVER_RETRY)
.requestInterceptor(auth2FeignRequestInterceptor)
.invocationHandlerFactory(factory)
.target(type, url);
}
jackson 的默认解码器仅解码正文而不是标头,因此我正在实现自己的 CustomDecoder。
我想实现的是获取 resonse.headers 的值并将其映射到 body,或者在从映射器获取值后动态地在对象 responseBodyObject 中添加属性。
public final class CustomDecoder extends JacksonDecoder implements Decoder {
@Override
public Object decode(Response response, Type type) throws IOException {
//Here the default decoder only decoding body.
Reader reader = response.body().asReader();
Object responseBodyObject = mapper.readValue(reader, mapper.constructType(type));
答:
0赞
topgun
6/24/2022
#1
在这里,我如何用不同的方法解决自己的问题。为了将标头值复制到正文,我最终将响应转换为 JSON 树,然后将 sessionid/token 的标头值添加到正文,然后将其转换回对象。
@Override
public Object decode(Response response, Type type) throws IOException {
Map<String, Collection<String>> headers = response.headers();
Collection<String> values = headers.get("sessionid");
String value = null;
if (values != null && values.isEmpty() == false) {
String[] array = new String[values.size()];
values.toArray(array);
value = array[0];
}
if (response.status() == 404)
return Util.emptyValueOf(type);
if (response.body() == null)
return null;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try{
StreamUtils.copy(response.body().asInputStream(), output);
} catch (FileNotFoundException ex) {
}
JsonNode actualObj = mapper.readTree(output.toByteArray());
JsonNode node1=actualObj.get("header");
ObjectNode parentObjectNode = (ObjectNode) node1;
parentObjectNode.put("token", value);
Object returnObject = mapper.treeToValue(actualObj, mapper.constructType(type).getClass());
return returnObject;
}
}
1赞
M. Prokhorov
6/24/2022
#2
如果我理解正确的话,这是构造类型具有应从标头填充的其他属性的情况。然后,这里有几种方法。首先,我更喜欢的是有一个特殊的接口类型,你的解码器知道,重新编码的对象可以实现以接收值:
public interface SessionTokenAware {
void setSessionToken(String value);
}
然后在解码器中:
@Override
public Object decode(Response response, Type type) throws IOException {
Object parsedResponse = mapper.readValue(
response.body().asReader(),
mapper.constructType(type)
);
if (parsedResponse instanceof SessionTokenAware) {
SessionTokenAware sessionAware = (SessionTokenAware) parsedResponse;
Collection<String> sessionHeader = response.headers().get("sessionId");
if (sessionHeader != null && !sessionHeader.isEmpty()) {
sessionAware.setSessionToken(sessionHeader.iterator().next());
}
}
return parsedResponse;
}
当你的响应实现接口时,它将获得解码器设置的会话令牌值,而不必破解正文流并向其添加任何内容:
public class MyResponseBody implements SesionTokenAware {
private String token;
@Override
public void setSessionToken(String value) {
token = value;
}
public String getSessionToken() {
return token;
}
}
另外,如果你不喜欢为你想要接收的每种可能的标头使用单独的接口,你可以创建一个包罗万象的接口,让每个响应实现自己整理标头:
public interface HeadersAware {
void onHeaders(Map<String, Collection<String>> headerValues);
}
public Object decode(Response response, Type type) throws IOException {
Object parsedResponse = ....
if (parsedResponse instanceof HeadersAware) {
HeaderAware headerAware = (HeaderAware) parsedResponse;
headerAware.onHeaders(response.headers());
}
return parsedResponse;
}
public class MyResponse implements HeaderAware {
private String token;
public void onHeaders(Map<String, Collection<String>> headers) {
Collection<String> sessionHeader = headers.getOrDefault("sessionId", emptySet());
if (!sessionHeader.isEmpty()) {
token = sessionHeader.iterator().next();
}
}
}
我不喜欢后一种方法,原因有两个:
- 响应可能会看到它没有看到的标头的值,因为它接收所有值,而不仅仅是实现特定响应 POJO 所需的值。
- 如果您有多个不同的会话感知响应层次结构(因此不可能有通用的超类),则必须将 sessionId 处理代码复制粘贴到每个响应中,而不是将该代码整齐地放置在类中。
CustomDecoder
评论
0赞
topgun
6/24/2022
您非常准确地确定了问题所在。您似乎正在添加接口以获得其他属性。但是如何在映射器返回的 Object 类型中实现接口呢?
0赞
topgun
6/24/2022
我无法控制映射器对象来更改或实现接口。mapper 类来自 jackson。
0赞
M. Prokhorov
6/25/2022
映射器无需更改接口或将接口添加到反序列化类型 - 您可以在要反序列化的类型上执行此操作。例如,从您的异常中,您有一个似乎在您的控制范围内,您可以在其中添加一个接口。tech.csp.api.ffa.GetAccountsResponse
0赞
topgun
6/25/2022
感谢您的回复。我理解你。但我不明白如何执行returnObject以将令牌值与原始对象值一起返回。从您的代码中:“SessionTokenAware sessionAware 的 parsedResponse 实例”我看到您期望 SessionAware 由 parsedResponse 实现,但我将如何实现它?我总是从 mapper.readValue() 获取对象类型。
0赞
topgun
6/25/2022
我也在使用 Java 8。
评论
responseBodyObject
Class<?>
returnObject