泽西岛 HK2 依赖注入
Jersey HK2 Dependency Injection
我正在编写一个公开 REST 的简单微服务 API。所以我开始使用 Jersey,当然我需要将我的对象注入到 jersey 资源中。基本上我有 2 个 classes 定义了一组资源,其中一些需要使用另一个服务。
基本上我有:
public interface MyService {
String getServiceName();
void doService();
}
此接口的 2 个实现(MyServiceBean 和 MyAlternativeServiceBean)
并且,据我阅读球衣文档的理解,我定义了一个 hk2 活页夹:
public class MyBinder implements Binder{
@Override
public void bind(DynamicConfiguration config) {
DescriptorImpl descriptor = BuilderHelper.link(MyServiceBean.class).named("MyServiceBean").to(MyService.class).build();
config.bind(descriptor);
config.bind(BuilderHelper.link(MyAlternativeServiceBean.class).named("MyAlternativeServiceBean").to(MyService.class).build());
}
我将此绑定程序注册到 ApplicationConfig class
public class ApplicationConfig extends ResourceConfig{
public ApplicationConfig(){
property("property.value", "MyAlternativeServiceImplementation");
registerInstances(new MyBinder());
}
}
并在资源中正确注释
@Path("first")
public class First {
@Inject @Named(value = "MyServiceBean")
private MyService myService;
//...
}
@Path("second")
public class Second {
@Inject @Named(value = "MyAlternativeServiceBean")
private MyService myService;
//...
}
一切正常,直到 MyService 实现没有 args 构造函数。但在 1 种情况下,我还需要提供对 MyAlternativeServiceBean 的依赖。
这里是构造函数
@Inject @Named("property.value")
public MyAlternativeServiceBean(String property){
this.property = property;
}
但我得到一个例外:
javax.servlet.ServletException: A MultiException has 5 exceptions. They are:|1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=String,parent=MyAlternativeServiceBean,qualifiers={}),position=0,optional=false,self=false,unqualified=null,2080509613)|2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.hpe.services.MyAlternativeServiceBean errors were found|3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.hpe.services.MyAlternativeServiceBean|4. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.hpe.tests.SecondEntryPoint errors were found|5. java.lang.IllegalStateException: Unable to perform operation: resolve on com.hpe.tests.SecondEntryPoint|
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:392)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:381)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:344)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:219)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:501)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:229)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:427)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.Server.handle(Server.java:370)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:973)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:1035)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:641)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:231)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:696)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:543)
at java.lang.Thread.run(Thread.java:745)
基本上我不知道如何在 hk2
中注入 properties/constants(例如我可以从配置文件中读取)
谢谢
此致
您可以做的是创建自定义注释
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Config {
String value();
}
然后为其创建一个InjectionResolver
(允许使用自定义注解进行注入)
public static class ConfigInjectionResolver implements InjectionResolver<Config> {
private static final Map<String, String> properties = new HashMap<>();
public ConfigInjectionResolver() {
properties.put("greeting.message", "Hello World");
}
@Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (String.class == injectee.getRequiredType()) {
AnnotatedElement elem = injectee.getParent();
if (elem instanceof Constructor) {
Constructor ctor = (Constructor) elem;
Config config = (Config) ctor.getParameterAnnotations()[injectee.getPosition()][0];
return properties.get(config.value());
} else {
Config config = elem.getAnnotation(Config.class);
return properties.get(config.value());
}
}
return null;
}
@Override
public boolean isConstructorParameterIndicator() { return true; }
@Override
public boolean isMethodParameterIndicator() { return false; }
}
这个例子只使用了 Map
,但我相信你能弄清楚如何使用 Properties
。注册 InjectionResolver
后,您现在可以执行
public SomeService(@Config("some.property") String property) {}
这是一个完整的测试用例
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import static org.junit.Assert.*;
/**
* Run like any other JUnit Test. Only one required dependency
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
* <version>${jersey2.version}</version>
* </dependency>
*
* @author Paul Samsotha
*/
public class ConfigExample extends JerseyTest {
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public static @interface Config {
String value();
}
public static class ConfigInjectionResolver implements InjectionResolver<Config> {
private static final Map<String, String> properties = new HashMap<>();
public ConfigInjectionResolver() {
properties.put("greeting.message", "Hello World");
}
@Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (String.class == injectee.getRequiredType()) {
AnnotatedElement elem = injectee.getParent();
if (elem instanceof Constructor) {
Constructor ctor = (Constructor) elem;
Config config = (Config) ctor.getParameterAnnotations()[injectee.getPosition()][0];
return properties.get(config.value());
} else {
Config config = elem.getAnnotation(Config.class);
return properties.get(config.value());
}
}
return null;
}
@Override
public boolean isConstructorParameterIndicator() { return true; }
@Override
public boolean isMethodParameterIndicator() { return false; }
}
private static interface GreetingService {
String getGreeting();
}
private static class ConfiguredGreetingService implements GreetingService {
private String message;
public ConfiguredGreetingService(@Config("greeting.message") String message) {
this.message = message;
}
@Override
public String getGreeting() {
return this.message;
}
}
@Path("greeting")
public static class GreetingResource {
@Inject
private GreetingService greetingService;
@GET
public String getConfigProp() {
return greetingService.getGreeting();
}
}
@Override
public ResourceConfig configure() {
ResourceConfig config = new ResourceConfig(GreetingResource.class);
config.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
config.register(new AbstractBinder(){
@Override
protected void configure() {
bind(ConfiguredGreetingService.class).to(GreetingService.class).in(Singleton.class);
bind(ConfigInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<Config>>(){})
.in(Singleton.class);
}
});
return config;
}
@Test
public void should_get_configured_greeting() {
final Response response = target("greeting")
.request().get();
assertEquals("Hello World", response.readEntity(String.class));
}
}
我正在编写一个公开 REST 的简单微服务 API。所以我开始使用 Jersey,当然我需要将我的对象注入到 jersey 资源中。基本上我有 2 个 classes 定义了一组资源,其中一些需要使用另一个服务。
基本上我有:
public interface MyService {
String getServiceName();
void doService();
}
此接口的 2 个实现(MyServiceBean 和 MyAlternativeServiceBean)
并且,据我阅读球衣文档的理解,我定义了一个 hk2 活页夹:
public class MyBinder implements Binder{
@Override
public void bind(DynamicConfiguration config) {
DescriptorImpl descriptor = BuilderHelper.link(MyServiceBean.class).named("MyServiceBean").to(MyService.class).build();
config.bind(descriptor);
config.bind(BuilderHelper.link(MyAlternativeServiceBean.class).named("MyAlternativeServiceBean").to(MyService.class).build());
}
我将此绑定程序注册到 ApplicationConfig class
public class ApplicationConfig extends ResourceConfig{
public ApplicationConfig(){
property("property.value", "MyAlternativeServiceImplementation");
registerInstances(new MyBinder());
}
}
并在资源中正确注释
@Path("first")
public class First {
@Inject @Named(value = "MyServiceBean")
private MyService myService;
//...
}
@Path("second")
public class Second {
@Inject @Named(value = "MyAlternativeServiceBean")
private MyService myService;
//...
}
一切正常,直到 MyService 实现没有 args 构造函数。但在 1 种情况下,我还需要提供对 MyAlternativeServiceBean 的依赖。
这里是构造函数
@Inject @Named("property.value")
public MyAlternativeServiceBean(String property){
this.property = property;
}
但我得到一个例外:
javax.servlet.ServletException: A MultiException has 5 exceptions. They are:|1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=String,parent=MyAlternativeServiceBean,qualifiers={}),position=0,optional=false,self=false,unqualified=null,2080509613)|2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.hpe.services.MyAlternativeServiceBean errors were found|3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.hpe.services.MyAlternativeServiceBean|4. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.hpe.tests.SecondEntryPoint errors were found|5. java.lang.IllegalStateException: Unable to perform operation: resolve on com.hpe.tests.SecondEntryPoint|
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:392)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:381)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:344)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:219)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:501)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:229)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:427)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.Server.handle(Server.java:370)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:973)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:1035)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:641)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:231)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:696)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:543)
at java.lang.Thread.run(Thread.java:745)
基本上我不知道如何在 hk2
中注入 properties/constants(例如我可以从配置文件中读取)谢谢
此致
您可以做的是创建自定义注释
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Config {
String value();
}
然后为其创建一个InjectionResolver
(允许使用自定义注解进行注入)
public static class ConfigInjectionResolver implements InjectionResolver<Config> {
private static final Map<String, String> properties = new HashMap<>();
public ConfigInjectionResolver() {
properties.put("greeting.message", "Hello World");
}
@Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (String.class == injectee.getRequiredType()) {
AnnotatedElement elem = injectee.getParent();
if (elem instanceof Constructor) {
Constructor ctor = (Constructor) elem;
Config config = (Config) ctor.getParameterAnnotations()[injectee.getPosition()][0];
return properties.get(config.value());
} else {
Config config = elem.getAnnotation(Config.class);
return properties.get(config.value());
}
}
return null;
}
@Override
public boolean isConstructorParameterIndicator() { return true; }
@Override
public boolean isMethodParameterIndicator() { return false; }
}
这个例子只使用了 Map
,但我相信你能弄清楚如何使用 Properties
。注册 InjectionResolver
后,您现在可以执行
public SomeService(@Config("some.property") String property) {}
这是一个完整的测试用例
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import static org.junit.Assert.*;
/**
* Run like any other JUnit Test. Only one required dependency
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
* <version>${jersey2.version}</version>
* </dependency>
*
* @author Paul Samsotha
*/
public class ConfigExample extends JerseyTest {
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public static @interface Config {
String value();
}
public static class ConfigInjectionResolver implements InjectionResolver<Config> {
private static final Map<String, String> properties = new HashMap<>();
public ConfigInjectionResolver() {
properties.put("greeting.message", "Hello World");
}
@Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (String.class == injectee.getRequiredType()) {
AnnotatedElement elem = injectee.getParent();
if (elem instanceof Constructor) {
Constructor ctor = (Constructor) elem;
Config config = (Config) ctor.getParameterAnnotations()[injectee.getPosition()][0];
return properties.get(config.value());
} else {
Config config = elem.getAnnotation(Config.class);
return properties.get(config.value());
}
}
return null;
}
@Override
public boolean isConstructorParameterIndicator() { return true; }
@Override
public boolean isMethodParameterIndicator() { return false; }
}
private static interface GreetingService {
String getGreeting();
}
private static class ConfiguredGreetingService implements GreetingService {
private String message;
public ConfiguredGreetingService(@Config("greeting.message") String message) {
this.message = message;
}
@Override
public String getGreeting() {
return this.message;
}
}
@Path("greeting")
public static class GreetingResource {
@Inject
private GreetingService greetingService;
@GET
public String getConfigProp() {
return greetingService.getGreeting();
}
}
@Override
public ResourceConfig configure() {
ResourceConfig config = new ResourceConfig(GreetingResource.class);
config.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
config.register(new AbstractBinder(){
@Override
protected void configure() {
bind(ConfiguredGreetingService.class).to(GreetingService.class).in(Singleton.class);
bind(ConfigInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<Config>>(){})
.in(Singleton.class);
}
});
return config;
}
@Test
public void should_get_configured_greeting() {
final Response response = target("greeting")
.request().get();
assertEquals("Hello World", response.readEntity(String.class));
}
}