我正在从DropWizard 0.7.1迁移到0.8.1的过程中。这包括从Jersey 1.x迁移到2.x。在使用Jersey 1.18.1的实现中,我实现了MyProvider(为简单起见,更改了所有类名)InjectableProvider。此类将创建MyInjectable包含自定义注入批注的对象MyToken。MyToken包含传递并由读取的各种属性MyInjectable。最后,在Application该类中,我注册的新实例MyProvider,如下所示。
MyProvider
InjectableProvider
MyInjectable
MyToken
Application
我已经进行了一些研究,似乎无法完全围绕如何在Jersey 2.x中创建(或替换)这样的secenario进行研究。
这是当前的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)));
是的Jersey在2.x中使自定义注入的创建更加复杂。对于Jersey 2.x,您需要了解一些自定义注入的主要组件。
org.glassfish.hk2.api.Factory
org.glassfish.hk2.api.InjectionResolver
org.glassfish.jersey.server.spi.internal.ValueFactoryProvider
您可以在“ 定制注入和生命周期管理”中阅读有关定制注入的更多信息。该文档的一个缺点是缺乏对如何注入参数值的解释。您可以通过简单地实现来摆脱困境InjectResolver,并且可以使用自定义注释将其注入字段,但是为了注入方法参数,我们需要ValueFactoryProvider。
InjectResolver
ValueFactoryProvider
幸运的是,我们可以扩展一些抽象类(文档中也没有提到),这些抽象类会使生活变得更轻松。我必须搜索软件包的源代码,org.glassfish.jersey.server.internal.inject以尝试找出所有问题。
org.glassfish.jersey.server.internal.inject
这是一个完整的示例,可帮助您入门。
Token (注射物)
Token
public class Token { private final String token; public Token(String token) { this.token = token; } public String getToken() { return token; } }
@TokenParam (我们的注解注解)
@TokenParam
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.FIELD}) public @interface TokenParam { boolean someAttribute() default true; }
TokenFactory (Factory每个第一个项目点的实现,但我们只需要扩展AbstractContainerRequestValueFactory。即可ContainerRequestContext。。请注意,所有这些HK2组件,我们都可以向其中注入其他依赖项,例如TokenAuthenticator,稍后我们将其绑定到HK2 。
TokenFactory
Factory
AbstractContainerRequestValueFactory
ContainerRequestContext
TokenAuthenticator
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一下。如果您对引擎盖下发生的事情感兴趣,则可以在我链接到的源代码中找到该类)
TokenParamInjectionResolver
ParamInjectionResolver
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver; public class TokenParamInjectionResolver extends ParamInjectionResolver { public TokenParamInjectionResolver() { super(TokenFactoryProvider.class); } }
TokenFactoryProvider (实现ValueFactoryProvider第三个要点。我只是扩展AbstractValueFactoryProvider一下。再次,您可以查看引擎盖下信息的来源)
TokenFactoryProvider
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范围内)
TokenFeature
TokenAuthentictor
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);
现在,您应该能够注入Tokenwith @TokenParam以及您通常的实体主体(如果我们未实现的话,这是不可能的)ValueFactoryProvider
@POST @Consumes(MediaType.APPLICATION_JSON) public String postToken(@TokenParam Token token, User user) { }
对于您的特定用例来说,这是一个一半@ $$$的示例。更好的方法可能是在Factory类中使用clone方法并TokenFactory使用一些参数创建一个新方法(也许是从注释. For example, in theTokenFactory中获得的,
. For example, in the
public class TokenFactory extends AbstractContainerRequestValueFactory<Token> { public TokenFactory clone(boolean someAttribute) { return new TokenFactory(authenticator, someAttribute); }
然后在TokenFactoryProviderine createValueFactory方法中,调用clone方法
createValueFactory
TokenParam annotation = parameter.getAnnotation(TokenParam.class); if (annotation != null && paramType.isAssignableFrom(Token.class)) { return tokenFactory.clone(annotation.someAttribute()); }
或者,您实际上 可以 在方法内部 创建 工厂。你有选择。