是的,Jersey 在 2.x 中使自定义注入的创建变得更加复杂。对于 Jersey 2.x,您需要了解一些自定义注入的主要组件
-
org.glassfish.hk2.api.Factory https://javaee.github.io/hk2/apidocs/org/glassfish/hk2/api/Factory.html- 创建可注入对象/服务
-
org.glassfish.hk2.api.InjectionResolver https://javaee.github.io/hk2/apidocs/org/glassfish/hk2/api/InjectionResolver.html- 用于为您自己的注释创建注入点。
-
org.glassfish.jersey.server.spi.internal.ValueFactoryProvider http://static.javadoc.io/org.glassfish.jersey.core/jersey-server/2.6/org/glassfish/jersey/server/spi/internal/ValueFactoryProvider.html- 提供参数值注入。
您可以阅读有关自定义注入的更多信息定制注入和生命周期管理 https://jersey.github.io/documentation/latest/ioc.html。该文档的一个缺点是缺乏如何注入参数值的解释。你可以通过简单地实现InjectResolver
,您将能够使用自定义注释注入字段,但为了注入方法参数,我们需要ValueFactoryProvider
.
幸运的是,我们可以扩展一些抽象类(文档也没有提及),这将使生活变得更轻松。我必须搜寻的源代码org.glassfish.jersey.server.internal.inject package https://github.com/jersey/jersey/tree/master/core-server/src/main/java/org/glassfish/jersey/server/internal/inject花点时间尝试弄清楚这一切。
这是一个完整的示例,可帮助您入门。
Token
(可注射物体)
public class Token {
private final String token;
public Token(String token) { this.token = token; }
public String getToken() { return token; }
}
@TokenParam
(我们的注入注释)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface TokenParam {
boolean someAttribute() default true;
}
TokenFactory
(实现Factory
根据第一个要点,但我们只是扩展AbstractContainerRequestValueFactory
。在那里我们可以访问ContainerRequestContext
。请注意,所有这些 HK2 组件,我们都可以向其中注入其他依赖项,例如TokenAuthenticator
,稍后我们将其绑定到 HK2。
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {
private final TokenAuthenticator tokenAuthenticator;
@Inject
public TokenFactory(TokenAuthenticator tokenAuthenticator) {
this.tokenAuthenticator = tokenAuthenticator;
}
@Override
public Token provide() {
String auth = getContainerRequest().getHeaderString(HttpHeaders.AUTHORIZATION);
try {
if (tokenAuthenticator.authenticate(auth).get() == null) {
throw new WebApplicationException(Response.Status.FORBIDDEN);
}
} catch (AuthenticationException ex) {
Logger.getLogger(TokenFactory.class.getName()).log(Level.SEVERE, null, ex);
}
return new Token("New Token");
}
}
TokenParamInjectionResolver
(实现了InjectResolver
每个要点第二点。我只是简单地扩展ParamInjectionResolver
。如果您对幕后发生的事情感兴趣,您可以在我链接到的源代码中找到该类)
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
public class TokenParamInjectionResolver extends ParamInjectionResolver {
public TokenParamInjectionResolver() {
super(TokenFactoryProvider.class);
}
}
TokenFactoryProvider
(实现了ValueFactoryProvider
根据第三个要点。我只是简单地扩展AbstractValueFactoryProvider
。同样,您可以查看源代码以获取幕后详细信息)
import javax.inject.Inject;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.model.Parameter;
public class TokenFactoryProvider extends AbstractValueFactoryProvider {
private final TokenFactory tokenFactory;
@Inject
public TokenFactoryProvider(
final MultivaluedParameterExtractorProvider extractorProvider,
ServiceLocator locator,
TokenFactory tokenFactory) {
super(extractorProvider, locator, Parameter.Source.UNKNOWN);
this.tokenFactory = tokenFactory;
}
@Override
protected Factory<?> createValueFactory(Parameter parameter) {
Class<?> paramType = parameter.getRawType();
TokenParam annotation = parameter.getAnnotation(TokenParam.class);
if (annotation != null && paramType.isAssignableFrom(Token.class)) {
return tokenFactory;
}
return null;
}
}
TokenFeature
(这里我们绑定了上面看到的所有组件,甚至是TokenAuthentictor
,我省略了,但如果你常用的 DropwizardAuthenticator
。我还使用了Feature
。我倾向于这样做来包装自定义功能的组件。您也可以在此处决定所有范围。只需注意一些组件需要位于Singleton
scope)
import javax.inject.Singleton;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
public class TokenFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder(){
@Override
public void configure() {
bind(TokenAuthenticator.class)
.to(TokenAuthenticator.class)
.in(Singleton.class);
bind(TokenFactory.class).to(TokenFactory.class)
.in(Singleton.class);
bind(TokenFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(TokenParamInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<TokenParam>>(){})
.in(Singleton.class);
}
});
return true;
}
}
最后简单地注册该功能
register(TokenFeature.class);
现在您应该能够注入Token
with @TokenParam
,以及您通常的实体机构(如果我们不实现的话,这是不可能的ValueFactoryProvider
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String postToken(@TokenParam Token token, User user) {
}
UPDATE
对于您的特定用例来说,这是一个半@$$示例。更好的方法可能是在你的Factory
类并创建一个新的TokenFactory
带有一些参数(也许你从注释中得到. For example, in the
TokenFactory`你可以有类似的东西
public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {
public TokenFactory clone(boolean someAttribute) {
return new TokenFactory(authenticator, someAttribute);
}
In the TokenFactoryProvider
ine createValueFactory
方法,然后调用克隆方法
TokenParam annotation = parameter.getAnnotation(TokenParam.class);
if (annotation != null && paramType.isAssignableFrom(Token.class)) {
return tokenFactory.clone(annotation.someAttribute());
}
或者你实际上可以create方法内部的工厂。你有选择。
UPDATE 2
See Also
- jersey 2 基于 Http 请求的上下文注入,无需单例 https://stackoverflow.com/q/34747574/2587435
UPDATE 3
从 Jersey 2.26 开始,依赖注入发生了变化。你会想看看这个帖子 https://stackoverflow.com/q/50975973/2587435例如,代码在实现同一注入时发生了怎样的变化。