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

时间:2015-05-22 13:25:58

标签: java jersey jersey-2.0 dropwizard hk2

我正在从DropWizard 0.7.1迁移到0.8.1。这包括从Jersey 1.x迁移到2.x.在我的 使用Jersey 1.18.1的实现,我有一个MyProvider(为了简单起见改变了所有类名),它实现了InjectableProvider。这个 class将创建MyInjectable个对象,其中包含自定义注入注释MyTokenMyToken包含各种属性 由MyInjectable传递和阅读的内容。最后,在Application类中,我注册了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)));

1 个答案:

答案 0 :(得分:23)

是的,泽西岛在2.x中创造了更复杂的定制注射。使用Jersey 2.x

,您需要了解自定义注入的几个主要组件

您可以在Custom Injection and Lifecycle Management中详细了解自定义注入。文档的一个缺点是缺乏如何注入参数值的解释。您可以简单地实现InjectResolver,并且可以使用自定义注释注入字段,但为了注入方法参数,我们需要ValueFactoryProvider

幸运的是,我们可以扩展一些抽象类(文档也没有提及),这将使生活变得更容易。我必须搜索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。我们可以访问{ {1}}。注意,所有这些HK2组件,我们可以将其他依赖项注入其中,例如ContainerRequestContext,稍后我们将绑定到HK2。

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"); } } (每个项目符号第2点实现TokenParamInjectionResolver。我只是扩展InjectResolver。如果您对在{2}下发生的事情感兴趣引擎盖,你可以在我链接的源代码中找到该类)

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,但是如果你常用的Dropwizard 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); 注入Token以及你常用的实体(如果我们没有实现@TokenParam那就不可能

ValueFactoryProvider

更新

它是您的特定用例的一半 - @ $$示例。一个更好的方法可能在你的@POST @Consumes(MediaType.APPLICATION_JSON) public String postToken(@TokenParam Token token, User user) { } 类中有一个克隆方法并创建一个带有一些参数的新Factory(也许你可以从你的注释TokenFactory TokenFactory获得你可以有类似< / p>

. For example, in the

public class TokenFactory extends AbstractContainerRequestValueFactory<Token> { public TokenFactory clone(boolean someAttribute) { return new TokenFactory(authenticator, someAttribute); } ine TokenFactoryProvider方法中,然后调用克隆方法

createValueFactory

或者您实际上可以在方法中创建工厂。你有选择。

更新2

另见