Jersey 2.x具有属性的自定义注入注释 [英] Jersey 2.x Custom Injection Annotation With Attributes

查看:151
本文介绍了Jersey 2.x具有属性的自定义注入注释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am in the process of migrating from DropWizard 0.7.1 to 0.8.1. This includes migrating from Jersey 1.x to 2.x. In my implementation that uses Jersey 1.18.1, I had a MyProvider (changed all class names for simplicity's sake) that implements InjectableProvider. This class would create MyInjectable objects, containing the custom injection annotation, MyToken. MyToken contains various attributes that are passed on and read by MyInjectable. Lastly, in the Application class I register a new instance of MyProvider, as seen below.

我做了一些研究,似乎无法解决我如何在泽西岛2.x中重新创建(或替换,我认为)这样一个场景。

I've done some research and can't seem to wrap my head around on how I'd recreate (or replace, I suppose) such a secenario in Jersey 2.x.

这是当前的1.18.1实施:

Here is the current, 1.18.1 implementation:

    @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.使用Jersey 2.x需要了解自定义注入的几个主要组件

Yeah Jersey made the creation of custom injections a bit more complicated in 2.x. There are a few main components to custom injection you need to know about with Jersey 2.x

  • org.glassfish.hk2.api.Factory - Creates injectable objects/services
  • org.glassfish.hk2.api.InjectionResolver - Used to create injection points for your own annotations.
  • org.glassfish.jersey.server.spi.internal.ValueFactoryProvider - To provide parameter value injections.

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

You can read more about custom injection in Custom Injection and Lifecycle Management. One shortcoming of the documentation is the lack of explanation of how to inject parameter values. You could get away with simply implementing the InjectResolver, and you would be able to inject into fields with your custom annotation, but in order to inject into method parameters, we need to ValueFactoryProvider.

幸运的是,我们可以扩展一些抽象类(文档也未提及)生活有点容易。我必须搜索 org.glassfish.jersey.server.internal.inject 软件包的源代码,可以尝试全部解决。

Luckily there are some abstract classes we can extend (which the documentation also fails to mention) that will make life a little easier. I has to scour the source code of the org.glassfish.jersey.server.internal.inject package for a bit to try and figure it all out.

以下是帮助您入门的完整示例。

Here's a full example to help get you started.

令牌 (可注入对象)

Token (injectable object)

public class Token {
    private final String token;
    public Token(String token) { this.token = token; }
    public String getToken() { return token; }
}

@TokenParam (我们的注入注释)

@TokenParam (our injection annotation)

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

TokenFactory (按照第一个项目点实现 Factory ,但我们只是扩展 AbstractContainerRequestValueFactory 。我们有' ll可以访问 ContainerRequestContext 。注意,所有这些HK2组件,我们可以注入其他依赖项,例如 TokenAuthenticator ,稍后我们将绑定到HK2。

TokenFactory (implements Factory per the first bullet point, but we just extend the AbstractContainerRequestValueFactory. There we'll have access to the ContainerRequestContext. Note, that all these HK2 components, we can inject other dependencies into them, for example the TokenAuthenticator, which we will bind to HK2 later.

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 (implements the InjectResolver per bullet point two. I simply extend ParamInjectionResolver. If your interested in what's going on under the hood, you can find the class in the source code I linked to)

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

public class TokenParamInjectionResolver extends ParamInjectionResolver {
    public TokenParamInjectionResolver() {
        super(TokenFactoryProvider.class);
    }
}

TokenFactoryProvider (根据第三个项目点实现 ValueFactoryProvider 。我只是扩展 AbstractValueFactoryProvider 再次,您可以查看引擎底下详细信息的来源)

TokenFactoryProvider (implements the ValueFactoryProvider per the third bullet point. I simply extend AbstractValueFactoryProvider. Again, you can look at the source for the under the hood details)

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 。我还使用了功能。我倾向于这样做来包装自定义功能的组件。这也是你可以在哪里决定所有的范围。只需注意一些组件必须在 Singleton 范围内)

TokenFeature (Here we bind all the components seen above, even the TokenAuthentictor, which I have left out, but if your usual Dropwizard Authenticator. I also made use of a Feature. I tend to do this to wrap components of a custom feature. This is also where you can decide all the scoping. Just note some components are required to be in Singleton scope)

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

现在你应该可以注入令牌使用 @TokenParam ,以及您通常的实体主体(如果我们没有实现 ValueFactoryProvider

Now you should be able to inject the Token with @TokenParam, as well as your usual entity bodies (which would not be possible if we didn't implement the ValueFactoryProvider

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

}






更新



对于您的特定用例,这是一个半 - @ $$示例。更好的方法可能在您的<$中有一个克隆方法c $ c> Factory class并使用一些参数创建一个新的 TokenFactory (也许你可以从你的注释中获得。例如,在 TokenFactory`中你可以有类似


UPDATE

It's kind of a half-@$$ example for your particular use case. A better approach would probably have a clone method in your Factory class and create a new TokenFactory with some parameters (maybe that you get from your annotation. For example, in theTokenFactory` you can have something like

public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {

    public TokenFactory clone(boolean someAttribute) {
        return new TokenFactory(authenticator, someAttribute);
    }

TokenFactoryProvider ine createValueFactory 方法,然后调用克隆方法

In the TokenFactoryProvider ine createValueFactory method, you then call the clone method

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

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

或者你实际上可以创建工厂里面的工厂方法。你有选择。

Or you could actually create the factory inside the method. you have options.

另见

  • jersey 2 context injection based upon HttpRequest without singleton

这篇关于Jersey 2.x具有属性的自定义注入注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆