Jersey 2.x具有属性的自定义注入注释
我正在从DropWizard 0.7.1迁移到0.8.1。 这包括从Jersey 1.x迁移到2.x. 在我使用Jersey 1.18.1的实现中,我实现了一个实现InjectableProvider
的MyProvider
(为了简单起见改变了所有的类名)。 这个类将创buildMyInjectable
对象,包含自定义注入注释MyToken
。 MyToken
包含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而不是单例