用户Id必须来自截取函数内的参数,而不是来自构造函数。该参数需要有用户Id,这样我们就不必做诸如ThreadLocal或同步之类的令人讨厌的事情。
使用代理可以解决这个问题。使用Jersey,您可以为用户id创建一个小包装,然后让Jersey代理它。我们可以用一个
javax.inject.Provider
在拦截器中延迟检索用户。当我们这样做时,用户将被绑定到请求的上下文(这是有保证的,我们不需要担心管理我们自己的线程局部变量或任何事情)。
public class UserIdInterceptor implements Interceptor {
private final Provider<User> userProvider;
UserIdInterceptor(Provider<User> userProvider) {
this.userProvider = userProvider;
}
@Override
public Response intercept(Chain chain) throws IOException {
final User user = userProvider.get();
if (user.isValid()) {
return chain.proceed(chain.request().newBuilder()
.addHeader("User-Id", userProvider.get().getName())
.build());
} else {
return chain.proceed(chain.request());
}
}
}
Factory
User
在请求范围中。这样,我们就可以为每个请求创建一个具有cookie的新用户。
public class UserFactory implements Factory<User> {
@Inject
private Provider<ContainerRequest> request;
@Override
public User provide() {
Cookie cookie = request.get().getCookies().get("User-Id");
return cookie != null
? new User(cookie.getValue())
: new User(null);
}
}
我们要做的是为
Retrofit
Provider<User>
当我们建造它时,把它传递给拦截器。
public class RetrofitFactory implements Factory<Retrofit> {
private final Retrofit retrofit;
@Inject
private RetrofitFactory(Provider<User> userProvider,
BaseUrlProvider urlProvider) {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new UserIdInterceptor(userProvider))
.build();
this.retrofit = new Retrofit.Builder()
.baseUrl(urlProvider.baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
@Override
public Retrofit provide() {
return this.retrofit;
}
}
然后把它们绑在一起
AbstractBinder
@Override
public void configure() {
bindFactory(RetrofitFactory.class)
.to(Retrofit.class)
.in(Singleton.class);
bindFactory(UserFactory.class)
.to(User.class)
.in(RequestScoped.class)
.proxy(true);
bindFactory(ClientFactories.MessageClientFactory.class)
.to(MessageClient.class);
bind(new BaseUrlProvider(this.baseUrl))
.to(BaseUrlProvider.class);
}
我在此制作了一个完整的演示
GitHub Repo