Jersey 2.x 带有属性的自定义注入注释
Jersey 2.x Custom Injection Annotation With Attributes
我正在从 DropWizard 0.7.1 迁移到 0.8.1。这包括从 Jersey 1.x 迁移到 2.x。在我的
使用 Jersey 1.18.1 的实现,我有一个实现 InjectableProvider
的 MyProvider
(为简单起见更改了所有 class 名称)。这个
class 将创建 MyInjectable
个对象,其中包含自定义注入注释 MyToken
。 MyToken
包含各种属性
由 MyInjectable
传递和阅读。最后,在 Application
class 中,我注册了一个 MyProvider
的新实例,如下所示。
我已经做了一些研究,但似乎无法全神贯注地思考如何在泽西岛重新创建(或替换,我想)这样的场景 2.x。
这是当前的 1.18.1 实现:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.FIELD })
public @interface MyToken {
// Custom annotation containing various attributes
boolean someAttribute() default true;
// ...
}
public class MyProvider implements InjectableProvider<MyToken, Parameter> {
// io.dropwizard.auth.Authenticator
private final Authenticator<String, MyObject> authenticator;
public MyProvider(Authenticator<String, MyObject> authenticator) {
this.authenticator = authenticator;
}
@Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
@Override
public Injectable<?> getInjectable(ComponentContext ic, MyToken t, Parameter p) {
return new MyInjectable(authenticator, t.someAttribute());
}
}
class MyInjectable extends AbstractHttpContextInjectable<MyObject> {
private final Authenticator<String, Session> authenticator;
private final boolean someAttribute;
public MyInjectable(Authenticator<String, MyObject> authenticator, boolean someAttribute) {
this.authenticator = authenticator;
this.someAttribute = someAttribute;
// ... Removed a few paramters for simplicity's sake
}
@Override
public MyObject getValue(HttpContext c) {
final HttpRequestContext request = c.getRequest();
// ... Removed code not pertaining to the question
return myObject;
}
}
// Lastly, the register call in the io.dropwizard.Application class
environment.jersey().register(new MyProvider(new MyProviderValidator(someValidator)));
是的,在 2.x 中,Jersey 使自定义注入的创建变得有点复杂。您需要了解 Jersey 2.x
自定义注入的一些主要组件
org.glassfish.hk2.api.Factory
- 创建可注射的 objects/services
org.glassfish.hk2.api.InjectionResolver
- 用于为您自己的注释创建注入点。
org.glassfish.jersey.server.spi.internal.ValueFactoryProvider
- 提供参数值注入。
您可以在 Custom Injection and Lifecycle Management 中阅读有关自定义注入的更多信息。该文档的一个缺点是缺乏对如何注入参数值的解释。您可以简单地实现 InjectResolver
,并且可以使用自定义注释注入字段,但是为了注入方法参数,我们需要 ValueFactoryProvider
.
幸运的是,我们可以扩展一些抽象的 classes(文档也没有提及),这会让生活变得更轻松。我必须搜索一下 source code of the org.glassfish.jersey.server.internal.inject
package 才能弄清楚。
这里有一个完整的示例,可以帮助您入门。
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
。如果你对下面发生的事情感兴趣引擎盖,你可以在我链接的源代码中找到 class)
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
,但是如果您通常使用 Dropwizard Authenticator
。我还使用了 Feature
。我倾向于这样做来包装自定义功能的组件。这也是您可以决定所有范围的地方。请注意一些组件需要位于Singleton
范围)
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);
现在你应该可以用 @TokenParam
注入 Token
,以及你通常的实体主体(如果我们不实现 ValueFactoryProvider
,这是不可能的
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String postToken(@TokenParam Token token, User user) {
}
更新
对于您的特定用例,这是一个半@$$ 示例。更好的方法可能是在您的 Factory
class 中使用克隆方法并创建一个带有一些参数的新 TokenFactory
(也许您可以从注释 . For example, in the
TokenFactory 中获得有类似的东西
public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {
public TokenFactory clone(boolean someAttribute) {
return new TokenFactory(authenticator, someAttribute);
}
在TokenFactoryProvider
ine createValueFactory
方法中,然后调用克隆方法
TokenParam annotation = parameter.getAnnotation(TokenParam.class);
if (annotation != null && paramType.isAssignableFrom(Token.class)) {
return tokenFactory.clone(annotation.someAttribute());
}
或者您实际上可以 在方法中创建 工厂。你有选择。
更新 2
另请参阅
更新 3
从 Jersey 2.26 开始,依赖注入发生了变化。您需要查看 this post 示例,了解代码在实现相同注入时发生了怎样的变化。
我正在从 DropWizard 0.7.1 迁移到 0.8.1。这包括从 Jersey 1.x 迁移到 2.x。在我的
使用 Jersey 1.18.1 的实现,我有一个实现 InjectableProvider
的 MyProvider
(为简单起见更改了所有 class 名称)。这个
class 将创建 MyInjectable
个对象,其中包含自定义注入注释 MyToken
。 MyToken
包含各种属性
由 MyInjectable
传递和阅读。最后,在 Application
class 中,我注册了一个 MyProvider
的新实例,如下所示。
我已经做了一些研究,但似乎无法全神贯注地思考如何在泽西岛重新创建(或替换,我想)这样的场景 2.x。
这是当前的 1.18.1 实现:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.FIELD })
public @interface MyToken {
// Custom annotation containing various attributes
boolean someAttribute() default true;
// ...
}
public class MyProvider implements InjectableProvider<MyToken, Parameter> {
// io.dropwizard.auth.Authenticator
private final Authenticator<String, MyObject> authenticator;
public MyProvider(Authenticator<String, MyObject> authenticator) {
this.authenticator = authenticator;
}
@Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
@Override
public Injectable<?> getInjectable(ComponentContext ic, MyToken t, Parameter p) {
return new MyInjectable(authenticator, t.someAttribute());
}
}
class MyInjectable extends AbstractHttpContextInjectable<MyObject> {
private final Authenticator<String, Session> authenticator;
private final boolean someAttribute;
public MyInjectable(Authenticator<String, MyObject> authenticator, boolean someAttribute) {
this.authenticator = authenticator;
this.someAttribute = someAttribute;
// ... Removed a few paramters for simplicity's sake
}
@Override
public MyObject getValue(HttpContext c) {
final HttpRequestContext request = c.getRequest();
// ... Removed code not pertaining to the question
return myObject;
}
}
// Lastly, the register call in the io.dropwizard.Application class
environment.jersey().register(new MyProvider(new MyProviderValidator(someValidator)));
是的,在 2.x 中,Jersey 使自定义注入的创建变得有点复杂。您需要了解 Jersey 2.x
自定义注入的一些主要组件org.glassfish.hk2.api.Factory
- 创建可注射的 objects/servicesorg.glassfish.hk2.api.InjectionResolver
- 用于为您自己的注释创建注入点。org.glassfish.jersey.server.spi.internal.ValueFactoryProvider
- 提供参数值注入。
您可以在 Custom Injection and Lifecycle Management 中阅读有关自定义注入的更多信息。该文档的一个缺点是缺乏对如何注入参数值的解释。您可以简单地实现 InjectResolver
,并且可以使用自定义注释注入字段,但是为了注入方法参数,我们需要 ValueFactoryProvider
.
幸运的是,我们可以扩展一些抽象的 classes(文档也没有提及),这会让生活变得更轻松。我必须搜索一下 source code of the org.glassfish.jersey.server.internal.inject
package 才能弄清楚。
这里有一个完整的示例,可以帮助您入门。
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
。如果你对下面发生的事情感兴趣引擎盖,你可以在我链接的源代码中找到 class)
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
,但是如果您通常使用 Dropwizard Authenticator
。我还使用了 Feature
。我倾向于这样做来包装自定义功能的组件。这也是您可以决定所有范围的地方。请注意一些组件需要位于Singleton
范围)
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);
现在你应该可以用 @TokenParam
注入 Token
,以及你通常的实体主体(如果我们不实现 ValueFactoryProvider
,这是不可能的
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String postToken(@TokenParam Token token, User user) {
}
更新
对于您的特定用例,这是一个半@$$ 示例。更好的方法可能是在您的 Factory
class 中使用克隆方法并创建一个带有一些参数的新 TokenFactory
(也许您可以从注释 . For example, in the
TokenFactory 中获得有类似的东西
public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {
public TokenFactory clone(boolean someAttribute) {
return new TokenFactory(authenticator, someAttribute);
}
在TokenFactoryProvider
ine createValueFactory
方法中,然后调用克隆方法
TokenParam annotation = parameter.getAnnotation(TokenParam.class);
if (annotation != null && paramType.isAssignableFrom(Token.class)) {
return tokenFactory.clone(annotation.someAttribute());
}
或者您实际上可以 在方法中创建 工厂。你有选择。
更新 2
另请参阅
更新 3
从 Jersey 2.26 开始,依赖注入发生了变化。您需要查看 this post 示例,了解代码在实现相同注入时发生了怎样的变化。