使用bindFactory时No object available for injection错误
No object available for injection error when using bindFactory
我有以下 Jersey (2.18) 应用程序的实现:
public class RootApplication extends ResourceConfig {
public RootApplication() {
packages("com.foo.bar");
register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(RepositoryFactory.class).to(Repository.class);
// if I use following line instead of bindFactory it works
// bind(OracleRepository.class).to(Repository.class);
}
});
}
public class RepositoryFactory implements Factory<Repository> {
private final Repository repo;
public RepositoryFactory() {
this.repo = new OracleRepository();
}
@Override
public Repository provide() {
return repo;
}
@Override
public void dispose(Repository repo) {
}
}
}
并在访问注入存储库的服务时出现以下异常
javax.servlet.ServletException: A MultiException has 3 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=Repository,parent=MeasureService,qualifiers={},position=-1,optional=false,self=false,unqualified=null,56464420)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.fidelity.pi.dashboard.rest.MeasureService errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.fidelity.pi.dashboard.rest.MeasureService
如果我注释掉 bindFactory
并使用注释掉的绑定,一切都有效。我是否遗漏了工厂实施方面的内容?异常似乎甚至在 RepositoryFactory
构造函数被命中之前就发生了。我需要工厂,因为我要对 OracleRepository
实例进行一些其他初始化。
我能够重现问题的唯一方法(使用您的不完整信息 - 即缺少注入点)是尝试注入 OracleRepository
而不是 Repository
。我不知道注入失败的确切原因,但我猜这是因为你绑定的是 Repository
而不是 OracleRepository
。如果这是问题所在,最简单的解决方法是将工厂绑定到 OracleRepository
或者简单地注入 Repository
.
对于 Repository
的注入,如果你想限定不同的实现,你可以通过将 named
或 qaulifiedBy
链接到绑定来实现,如下例所示(我在其中使用 named
并用 @Named
).
注释注入点
在示例中我使用了 Jersey Test Framework
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-inmemory</artifactId>
<version>${jersey2.version}</version>
<scope>test</scope>
</dependency>
这是完整的测试。您可以更改 "Sql"
和 "Oracle"
之间的 @Named
以查看差异。
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;
public class InjectionTest extends JerseyTest {
@Path("test")
public static class SimpleResource {
@Inject
@Named("Oracle")
private Repository repo;
@GET
public String getRepoName() {
return repo.getClass().getSimpleName();
}
}
@Override
public Application configure() {
ResourceConfig config = new ResourceConfig();
config.register(SimpleResource.class);
config.register(new AbstractBinder(){
@Override
protected void configure() {
bindFactory(SqlFactory.class)
.to(Repository.class).named("Sql");
bindFactory(OracleFactory.class)
.to(Repository.class).named("Oracle");
}
});
return config;
}
public static interface Repository {}
public static class OracleRepository implements Repository {}
public static class SqlRepository implements Repository {}
public static class SqlFactory implements Factory<Repository> {
@Override
public Repository provide() {
return new SqlRepository();
}
@Override
public void dispose(Repository t) {}
}
public static class OracleFactory implements Factory<Repository> {
@Override
public Repository provide() {
return new OracleRepository();
}
@Override
public void dispose(Repository t) {}
}
/**
* Change the `Assert` from "OracleRepository" to "SqlRepository"
* when switching the `@Named` on the injection point.
*/
@Test
public void testInjectOk() {
Response response = target("test").request().get();
String respString = response.readEntity(String.class);
Assert.assertEquals("OracleRepository", respString);
System.out.println(respString);
}
}
如果您仍然有问题,请 post 一个完整的单个 class 测试用例,就像我上面的那样,以证明问题。
我有以下 Jersey (2.18) 应用程序的实现:
public class RootApplication extends ResourceConfig {
public RootApplication() {
packages("com.foo.bar");
register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(RepositoryFactory.class).to(Repository.class);
// if I use following line instead of bindFactory it works
// bind(OracleRepository.class).to(Repository.class);
}
});
}
public class RepositoryFactory implements Factory<Repository> {
private final Repository repo;
public RepositoryFactory() {
this.repo = new OracleRepository();
}
@Override
public Repository provide() {
return repo;
}
@Override
public void dispose(Repository repo) {
}
}
}
并在访问注入存储库的服务时出现以下异常
javax.servlet.ServletException: A MultiException has 3 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=Repository,parent=MeasureService,qualifiers={},position=-1,optional=false,self=false,unqualified=null,56464420)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.fidelity.pi.dashboard.rest.MeasureService errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.fidelity.pi.dashboard.rest.MeasureService
如果我注释掉 bindFactory
并使用注释掉的绑定,一切都有效。我是否遗漏了工厂实施方面的内容?异常似乎甚至在 RepositoryFactory
构造函数被命中之前就发生了。我需要工厂,因为我要对 OracleRepository
实例进行一些其他初始化。
我能够重现问题的唯一方法(使用您的不完整信息 - 即缺少注入点)是尝试注入 OracleRepository
而不是 Repository
。我不知道注入失败的确切原因,但我猜这是因为你绑定的是 Repository
而不是 OracleRepository
。如果这是问题所在,最简单的解决方法是将工厂绑定到 OracleRepository
或者简单地注入 Repository
.
对于 Repository
的注入,如果你想限定不同的实现,你可以通过将 named
或 qaulifiedBy
链接到绑定来实现,如下例所示(我在其中使用 named
并用 @Named
).
在示例中我使用了 Jersey Test Framework
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-inmemory</artifactId>
<version>${jersey2.version}</version>
<scope>test</scope>
</dependency>
这是完整的测试。您可以更改 "Sql"
和 "Oracle"
之间的 @Named
以查看差异。
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;
public class InjectionTest extends JerseyTest {
@Path("test")
public static class SimpleResource {
@Inject
@Named("Oracle")
private Repository repo;
@GET
public String getRepoName() {
return repo.getClass().getSimpleName();
}
}
@Override
public Application configure() {
ResourceConfig config = new ResourceConfig();
config.register(SimpleResource.class);
config.register(new AbstractBinder(){
@Override
protected void configure() {
bindFactory(SqlFactory.class)
.to(Repository.class).named("Sql");
bindFactory(OracleFactory.class)
.to(Repository.class).named("Oracle");
}
});
return config;
}
public static interface Repository {}
public static class OracleRepository implements Repository {}
public static class SqlRepository implements Repository {}
public static class SqlFactory implements Factory<Repository> {
@Override
public Repository provide() {
return new SqlRepository();
}
@Override
public void dispose(Repository t) {}
}
public static class OracleFactory implements Factory<Repository> {
@Override
public Repository provide() {
return new OracleRepository();
}
@Override
public void dispose(Repository t) {}
}
/**
* Change the `Assert` from "OracleRepository" to "SqlRepository"
* when switching the `@Named` on the injection point.
*/
@Test
public void testInjectOk() {
Response response = target("test").request().get();
String respString = response.readEntity(String.class);
Assert.assertEquals("OracleRepository", respString);
System.out.println(respString);
}
}
如果您仍然有问题,请 post 一个完整的单个 class 测试用例,就像我上面的那样,以证明问题。