java Jersey 2.x 带有属性的自定义注入注解
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30397933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Jersey 2.x Custom Injection Annotation With Attributes
提问by Eric Bernier
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.
我正在从 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'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.
我做了一些研究,似乎无法理解我将如何在 Jersey 2.x 中重新创建(或替换,我想)这样的场景。
Here is the current, 1.18.1 implementation:
这是当前的 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)));
回答by Paul Samsotha
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
是的 Jersey 在 2.x 中使自定义注入的创建变得更加复杂。在 Jersey 2.x 中,您需要了解一些自定义注入的主要组件
org.glassfish.hk2.api.Factory
- Creates injectable objects/servicesorg.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.
org.glassfish.hk2.api.Factory
- 创建可注入的对象/服务org.glassfish.hk2.api.InjectionResolver
- 用于为您自己的注释创建注入点。org.glassfish.jersey.server.spi.internal.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
.
您可以在自定义注入和生命周期管理中阅读更多关于自定义注入的信息。该文档的一个缺点是缺乏对如何注入参数值的解释。您可以简单地实现InjectResolver
,并且可以使用自定义注释注入字段,但是为了注入方法参数,我们需要ValueFactoryProvider
.
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
packagefor a bit to try and figure it all out.
幸运的是,我们可以扩展一些抽象类(文档也没有提到),这将使生活更轻松。我必须搜索一下包的源代码org.glassfish.jersey.server.internal.inject
以尝试弄清楚这一切。
Here's a full example to help get you started.
这是帮助您入门的完整示例。
Token
(injectable object)
Token
(注射对象)
public class Token {
private final String token;
public Token(String token) { this.token = token; }
public String getToken() { return token; }
}
@TokenParam
(our injection annotation)
@TokenParam
(我们的注入注解)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface TokenParam {
boolean someAttribute() default true;
}
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.
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");
}
}
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)
TokenParamInjectionResolver
(实现InjectResolver
每个项目符号的第二点。我只是扩展ParamInjectionResolver
。如果您对引擎盖下发生的事情感兴趣,可以在我链接到的源代码中找到该类)
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
public class TokenParamInjectionResolver extends ParamInjectionResolver {
public TokenParamInjectionResolver() {
super(TokenFactoryProvider.class);
}
}
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)
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
(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)
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;
}
}
And finally simply register the feature
最后简单地注册该功能
register(TokenFeature.class);
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
现在您应该能够注入Token
with@TokenParam
以及您通常的实体主体(如果我们不实现ValueFactoryProvider
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String postToken(@TokenParam Token token, User user) {
}
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 the
TokenFactory` you can have something like
对于您的特定用例,这是一个半@$$ 示例。更好的方法可能是在您的Factory
类中有一个 clone 方法并TokenFactory
使用一些参数创建一个新方法(也许您从您的注释. For example, in the
TokenFactory` 中获得,您可以拥有类似
public class TokenFactory extends AbstractContainerRequestValueFactory<Token> {
public TokenFactory clone(boolean someAttribute) {
return new TokenFactory(authenticator, someAttribute);
}
In the TokenFactoryProvider
ine createValueFactory
method, you then call the clone method
在TokenFactoryProvider
inecreateValueFactory
方法中,然后调用clone方法
TokenParam annotation = parameter.getAnnotation(TokenParam.class);
if (annotation != null && paramType.isAssignableFrom(Token.class)) {
return tokenFactory.clone(annotation.someAttribute());
}
Or you could actually createthe factory inside the method. you have options.
或者您实际上可以在方法中创建工厂。你有选择。
UPDATE 2
更新 2
See Also
也可以看看