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

Eri*_*ier 11 java jersey dropwizard jersey-2.0 hk2

我正在从DropWizard 0.7.1迁移到0.8.1.这包括从Jersey 1.x迁移到2.x. 在我使用Jersey 1.18.1的实现中,我实现了MyProvider(为简单起见改变了所有类名)InjectableProvider.该类将创建MyInjectable包含自定义注入注释的对象MyToken.MyToken包含传递和读取的各种属性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)));
Run Code Online (Sandbox Code Playgroud)

Pau*_*tha 24

Yeah Jersey在2.x中创建了更复杂的定制注射.使用Jersey 2.x需要了解自定义注入的几个主要组件

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

幸运的是,我们可以扩展一些抽象类(文档也没有提到),这将使生活变得更容易.我必须仔细搜索软件包源代码,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; }
}
Run Code Online (Sandbox Code Playgroud)

@TokenParam (我们的注射注释)

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface TokenParam {
    boolean someAttribute() default true;
}
Run Code Online (Sandbox Code Playgroud)

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");
    }  
}
Run Code Online (Sandbox Code Playgroud)

TokenParamInjectionResolver(实现InjectResolver每个子弹点二.我只是扩展ParamInjectionResolver.如果你对引擎盖下的内容感兴趣,你可以在我链接到的源代码中找到该类)

import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;

public class TokenParamInjectionResolver extends ParamInjectionResolver {
    public TokenParamInjectionResolver() {
        super(TokenFactoryProvider.class);
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    } 
}
Run Code Online (Sandbox Code Playgroud)

最后只需注册该功能

register(TokenFeature.class);
Run Code Online (Sandbox Code Playgroud)

现在你应该能够注入Tokenwith @TokenParam,以及你通常的实体(如果我们没有实现,那就不可能)ValueFactoryProvider

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

}
Run Code Online (Sandbox Code Playgroud)

UPDATE

对于您的特定用例,这是一个半 - @ $$示例.一个更好的方法可能在你的Factory类中有一个克隆方法并创建一个TokenFactory带有一些参数的新方法(也许你可以从你的注释. For example, in theTokenFactory中得到你可以有类似的东西)

public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {

    public TokenFactory clone(boolean someAttribute) {
        return new TokenFactory(authenticator, someAttribute);
    }
Run Code Online (Sandbox Code Playgroud)

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

TokenParam annotation = parameter.getAnnotation(TokenParam.class);

if (annotation != null && paramType.isAssignableFrom(Token.class)) {
    return tokenFactory.clone(annotation.someAttribute());
}
Run Code Online (Sandbox Code Playgroud)

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

更新2

也可以看看