我得到我的代码下面的错误在发射:吉斯代理支持循环依赖
试图进行代理com.bar.Foo支持循环依赖,但它是不是 的接口。
这个代理工作到底如何?如果我只是在接口后面抛出足够的类,一切都会好起来吗?
(我知道,循环依赖通常是一个代码味道,但我认为在这种情况下,它的确定。)
我得到我的代码下面的错误在发射:吉斯代理支持循环依赖
试图进行代理com.bar.Foo支持循环依赖,但它是不是 的接口。
这个代理工作到底如何?如果我只是在接口后面抛出足够的类,一切都会好起来吗?
(我知道,循环依赖通常是一个代码味道,但我认为在这种情况下,它的确定。)
我是新来这个概念,但这里是我的理解。
假设你有接口A
和B
,以及实现Ai
和Bi
。
如果Ai
对B
的依赖,并Bi
对A
的依赖,那么吉斯可以创建A
代理实现(称之为Ap
),将在未来的某个时刻被给予Ai
委派。 Guice给出Ap
为Bi
,因为它依赖于A
,允许Bi
完成实例化。然后,由于Bi
已被实例化,Guice可以用Bi
实例化Ai
。然后,由于Ai
现在很好做,Guice告诉Ap
委托给Ai
。
如果A
和B
没有接口(和你刚Ai
和Bi
)这只是将是不可能的,因为创建Ap
会要求你延长Ai
,已经需要Bi
。
下面是它可能会是什么样代码:
public interface A {
void doA();
}
public interface B {
void doB();
}
public class Ai implements A {
private final B b;
@Inject
public Ai(B b) {
this.b = b;
}
public void doA() {
b.doB();
}
}
public class Bi implements B {
private final A a;
@Inject
public Bi(A a) {
this.a = a;
}
public void doB() {
}
}
的代理类,吉斯使得看起来像这样:
public class Ap implements A {
private A delegate;
void setDelegate(A a) {
delegate = a;
}
public void doA() {
delegate.doA();
}
}
而且,它还将所有使用这个基本思想连线:
Ap proxyA = new Ap();
B b = new B(proxyA);
A a = new A(b);
proxyA.setDelegate(a);
这就是它会是什么样子,如果你只有Ai
和Bi
,无接口A
和B
。
public class Ap extends Ai {
private Ai delegate;
public Ap() {
super(_); //a B is required here, but we can't give one!
}
}
如果我只是把后面的接口不够班,一切都将被罚款?
我猜想,代理可以在构造函数中如何进行交互有严格的限制。换句话说,如果B在Guice有机会用真实A填充A的代理之前尝试调用A,那么我会期待RuntimeException。
虽然“注入接口”方法是完全有效的,并且在某些情况下甚至可能是更好的解决方案,但通常情况下,您可以使用更简单的解决方案:提供者。
对于Guice可以管理的每个班级“A”,guice还提供“Provider<A>
”。这是javax.inject.Provider接口的内部实现,其接口消息将为“return injector.getInstance(A.class)
”。你不必自己实现接口,它是“guice魔术”的一部分。
因此可以缩短A-> B,BA例子:
public class CircularDepTest {
static class A {
private final Provider<B> b;
private String name = "A";
@Inject
public A(Provider<B> b) {
this.b = b;
}
}
static class B {
private final Provider<A> a;
private String name = "B";
@Inject
public B(Provider<A> a) {
this.a = a;
}
}
@Inject
A a;
@Inject
B b;
@Before
public void setUp() {
Guice.createInjector().injectMembers(this);
}
@Test
public void testCircularInjection() throws Exception {
assertEquals("A", a.name);
assertEquals("B", a.b.get().name);
assertEquals("B", b.name);
assertEquals("A", b.a.get().name);
}}
我喜欢这个,因为它更具有可读性(你是不是上当beleive,构造函数已经持有B的”一个实例“),并且由于您可以自己实现提供者,所以它仍然可以在guice环境之外”手工“工作(例如,用于测试)。
这里是@扬加林斯基的回答,在斯卡拉重做:
import javax.inject.Inject
import com.google.inject.{Guice, Injector, Provider}
import net.codingwell.scalaguice.InjectorExtensions._
/** Demonstrates the problem by failing with `Tried proxying CircularDep1$A to support a circular dependency, but it is not an interface.
while locating CircularDep1$A for parameter 0 at CircularDep1$B.<init>(CircularDep.scala:10)
while locating CircularDep1$B for parameter 0 at CircularDep1$A.<init>(CircularDep.scala:6)
while locating CircularDep1$A` */
object CircularDep1 extends App {
class A @Inject() (val b: B) {
val name = "A"
}
class B @Inject() (val a: A) {
val name = "B"
}
val injector: Injector = Guice.createInjector()
val a: A = injector.instance[A]
val b: B = injector.instance[B]
assert("A" == a.name)
assert("B" == a.b.name)
assert("B" == b.name)
assert("A" == b.a.name)
println("This program won't run!")
}
/** This version solves the problem by using `Provider`s */
object CircularDep2 extends App {
class A @Inject() (val b: Provider[B]) {
val name = "A"
}
class B @Inject() (val a: Provider[A]) {
val name = "B"
}
val injector: Injector = Guice.createInjector()
val a: A = injector.instance[A]
val b: B = injector.instance[B]
assert("A" == a.name)
assert("B" == a.b.get.name)
assert("B" == b.name)
assert("A" == b.a.get.name)
println("Yes, this program works!")
}
我也喜欢这种方式。这意味着当你不需要它们时,你不必创建接口。与创建接口相比,将注入更改为提供者也更快/更不可能。 – specialtrevor 2013-11-19 16:30:50
如何在你的应用程序代码中显式依赖'Guice'?我一直认为独立于DI框架是很好的。在你将'Provider'引入你的代码之前,你可能已经转向说'Spring'了,但这是不可能的。 – 2015-01-19 09:30:52
我正在谈论一个JSR330标准接口javax.inject.Provider。不需要guice依赖。 –
2015-01-19 12:44:40