我找到了一种很好的方法来做到这一点,它不使用ThreadLocal 在ContainerRequestFilter 和ClientRequestFilter 之间进行通信,因为您不能假设响应容器请求的客户端请求将是在同一个线程上。
我实现这一点的方法是在ContainerRequestFilter 中的ContainerRequestConext 对象中设置一个属性。然后我可以将ContainerRequestContext 对象(显式或通过依赖注入)传递给我的ClientRequestFilter。如果您使用依赖注入(如果您使用的是 Jersey 2,那么您可能使用的是 HK2),那么所有这些都可以在不修改任何资源级别逻辑的情况下实现。
有一个像这样的ContainerRequestFilter:
public class RequestIdContainerFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
containerRequestContext.setProperty("property-name", "any-object-you-like");
}
还有一个 ClientRequestFilter 在其构造函数中采用 ContainerRequestContext:
public class RequestIdClientRequestFilter implements ClientRequestFilter {
private ContainerRequestContext containerRequestContext;
public RequestIdClientRequestFilter(ContainerRequestContext containerRequestContext) {
this.containerRequestContext = containerRequestContext;
}
@Override
public void filter(ClientRequestContext clientRequestContext) throws IOException {
String value = containerRequestContext.getProperty("property-name");
clientRequestContext.getHeaders().putSingle("MyHeader", value);
}
}
那么这只是将这一切捆绑在一起的情况。您将需要一个工厂来创建您需要的任何 Client 或 WebTarget:
public class MyWebTargetFactory implements Factory<WebTarget> {
@Context
private ContainerRequestContext containerRequestContext;
@Inject
public MyWebTargetFactory(ContainerRequestContext containerRequestContext) {
this.containerRequestContext = containerRequestContext;
}
@Override
public WebTarget provide() {
Client client = ClientBuilder.newClient();
client.register(new RequestIdClientRequestFilter(containerRequestContext));
return client.target("path/to/api");
}
@Override
public void dispose(WebTarget target) {
}
}
然后注册过滤器并将你的工厂绑定到你的主应用ResourceConfig:
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(RequestIdContainerFilter.class);
register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(MyWebTargetFactory.class).to(WebTarget.class);
}
}
}
}