Jersey 2.x具有属性的自定义注入注释

我正在从DropWizard 0.7.1迁移到0.8.1。 这包括从Jersey 1.x迁移到2.x. 在我使用Jersey 1.18.1的实现中,我实现了一个实现InjectableProviderMyProvider (为了简单起见改变了所有的类名)。 这个类将创buildMyInjectable对象,包含自定义注入注释MyTokenMyToken包含MyToken传递和读取的各种属性。 最后,在Application类中,我注册了一个MyProvider的新实例,如下所示。

我已经做了一些研究,似乎无法绕过我在泽西2.x上重新创build(或replace,我想是)这样一个场景。

这里是目前的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中使定制注射的创build变得更加复杂一些。 自定义注入有几个主要组件,您需要了解Jersey 2.x

  • org.glassfish.hk2.api.Factory – 创build可注入的对象/服务
  • org.glassfish.hk2.api.InjectionResolver – 用于为自己的注释创build注入点。
  • org.glassfish.jersey.server.spi.internal.ValueFactoryProvider – 提供参数值注入。

您可以阅读更多关于自定义注入和生命周期pipe理中的自定义注入 。 文档的一个缺点是缺乏对如何注入参数值的解释。 你可以简单地实现InjectResolver ,而且你可以使用自定义注释注入字段,但为了注入方法参数,我们需要ValueFactoryProvider

幸运的是,我们可以扩展一些抽象类(文档中也没有提到),这会使生活变得容易一些。 我必须org.glassfish.jersey.server.internal.inject研究一下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 ,但是我们只扩展了AbstractConatainerRequestValueFactory ,在那里我们可以访问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如果你对引擎盖下的东西感兴趣,你可以在我链接到的源代码中find类)

 import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver; public class TokenParamInjectionResolver extends ParamInjectionResolver { public TokenParamInjectionResolver() { super(TokenFactoryProvider.class); } } 

TokenFactoryProvider (每个第三个项目符号都实现了ValueFactoryProvider ,我只是简单的扩展了AbstractValueFactoryProvider ,再次,你可以看一下ValueFactoryProvider细节的源代码)

 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; } } 

最后只需注册该function

 register(TokenFeature.class); 

现在你应该可以使用@TokenParam来注入Token ,以及你通常的实体主体(如果我们没有实现ValueFactoryProvider ,这是不可能的

 @POST @Consumes(MediaType.APPLICATION_JSON) public String postToken(@TokenParam Token token, User user) { } 

UPDATE

对于您的特定用例来说,这是一个半 – @ $$的例子。 一个更好的方法可能在你的Factory类中有一个克隆方法,并用一些参数创build一个新的TokenFactory (也许你从你的注解中获得), . For example, in the TokenFactory中,你可以有类似的东西

 public class TokenFactory extends AbstractContainerRequestValueFactory<Token> { public TokenFactory clone(boolean someAttribute) { return new TokenFactory(authenticator, someAttribute); } 

TokenFactoryProvider createValueFactory方法中,然后调用clone方法

 TokenParam annotation = parameter.getAnnotation(TokenParam.class); if (annotation != null && paramType.isAssignableFrom(Token.class)) { return tokenFactory.clone(annotation.someAttribute()); } 

或者你可以在方法中创build工厂。 你有select。

更新2

也可以看看

  • jersey 2上下文注入基于HttpRequest而不是单例