如果某些Guice范围使用不正确,我有一些测试我想失败。例如,@Singleton
不应该有任何@RequestScoped
或@TestScoped
个依赖项(Provider<>
s当然可以。)
在生产中,这部分解决了,因为在输入范围之前将构建热切的单体,从而产生OutOfScopeException
s。但是在开发过程中,单例将在范围内懒洋洋地创建,并且没有明显的问题。
根据these two未解决的问题判断,似乎没有简单的内置方法可以做到这一点。我可以使用SPI实现这一目标吗?我尝试使用TypeListener
,但不清楚如何获取给定类型的依赖项。
答案 0 :(得分:2)
这不是一个小问题,但绝对是一个很好的问题!可能有一个测试人员提到你提到的范围绑定问题。我想我可以让Junit跑步者用错误的绑定练习产生警告。我稍后会用它来更新这篇文章。
现在有一个例子来说明如何获取绑定范围。
<强>模块强>
public class ScopeTestModel extends ServletModule {
@Override
protected void configureServlets() {
super
.configureServlets();
bind(Key.get(Object.class, Names.named("REQ1"))).to(Object.class).in(ServletScopes.REQUEST);
bind(Key.get(Object.class, Names.named("REQ2"))).to(RequestScopedObject.class);
bind(Key.get(Object.class, Names.named("SINGLETON1"))).to(Object.class).asEagerSingleton();
bind(Key.get(Object.class, Names.named("SINGLETON2"))).to(Object.class).in(Scopes.SINGLETON);
bind(Key.get(Object.class, Names.named("SINGLETON3"))).to(SingletonScopedObject.class);
bind(Key.get(Object.class, Names.named("SESS1"))).to(Object.class).in(ServletScopes.SESSION);
bind(Key.get(Object.class, Names.named("SESS2"))).to(SessionScopedObject.class);
}
}
<强>测试用例强>
public class TestScopeBinding {
private Injector injector = Guice.createInjector(new ScopeTestModel());
@Test
public void testRequestScope() throws Exception {
Binding<Object> req1 = injector.getBinding(Key.get(Object.class, Names.named("REQ1")));
Binding<Object> req2 = injector.getBinding(Key.get(Object.class, Names.named("REQ2")));
Scope scope1 = getScopeInstanceOrNull(req1);
Scope scope2 = getScopeInstanceOrNull(req2);
Assert.assertEquals(ServletScopes.REQUEST,scope1);
Assert.assertEquals(ServletScopes.REQUEST,scope2);
}
@Test
public void testSessionScope() throws Exception {
injector.getAllBindings();
Binding<Object> sess1 = injector.getBinding(Key.get(Object.class, Names.named("SESS1")));
Binding<Object> sess2 = injector.getBinding(Key.get(Object.class, Names.named("SESS2")));
Scope scope1 = getScopeInstanceOrNull(sess1);
Scope scope2 = getScopeInstanceOrNull(sess2);
Assert.assertEquals(ServletScopes.SESSION,scope1);
Assert.assertEquals(ServletScopes.SESSION,scope2);
}
@Test
public void testSingletonScope() throws Exception {
injector.getAllBindings();
Binding<Object> sng1 = injector.getBinding(Key.get(Object.class, Names.named("SINGLETON1")));
Binding<Object> sng2 = injector.getBinding(Key.get(Object.class, Names.named("SINGLETON2")));
Binding<Object> sng3 = injector.getBinding(Key.get(Object.class, Names.named("SINGLETON3")));
Scope scope1 = getScopeInstanceOrNull(sng1);
Scope scope2 = getScopeInstanceOrNull(sng2);
Scope scope3 = getScopeInstanceOrNull(sng3);
Assert.assertEquals(Scopes.SINGLETON,scope1);
Assert.assertEquals(Scopes.SINGLETON,scope2);
Assert.assertEquals(Scopes.SINGLETON,scope3);
}
private Scope getScopeInstanceOrNull(final Binding<?> binding) {
return binding.acceptScopingVisitor(new DefaultBindingScopingVisitor<Scope>() {
@Override
public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
throw new RuntimeException(String.format("I don't know how to handle the scopeAnnotation: %s",scopeAnnotation.getCanonicalName()));
}
@Override
public Scope visitNoScoping() {
if(binding instanceof LinkedKeyBinding) {
Binding<?> childBinding = injector.getBinding(((LinkedKeyBinding)binding).getLinkedKey());
return getScopeInstanceOrNull(childBinding);
}
return null;
}
@Override
public Scope visitEagerSingleton() {
return Scopes.SINGLETON;
}
public Scope visitScope(Scope scope) {
return scope;
}
});
}
}
Scoped objects
@RequestScoped
public class RequestScopedObject extends Object {
}
@SessionScoped
public class SessionScopedObject extends Object {
}
@Singleton
public class SingletonScopedObject extends Object {
}
答案 1 :(得分:1)
以下是我使用ProvisionListener
使用Guice的4.0 beta完成此操作的方法。我尝试了TypeListener
但似乎在Guice必然具有该类型依赖项的绑定之前调用TypeListener
。这导致异常,甚至在一个案例中出现僵局。
private static class ScopeValidator implements ProvisionListener {
private @Inject Injector injector;
private @Inject WhateverScope scope;
@Override
public <T> void onProvision(ProvisionInvocation<T> provision) {
if (injector == null) {
// The injector isn't created yet, just return. This isn't a
// problem because any scope violations will be caught by
// WhateverScope itself here (throwing an OutOfScopeException)
return;
}
Binding<?> binding = provision.getBinding();
Key<?> key = binding.getKey();
if (Scopes.isSingleton(binding) && binding instanceof HasDependencies) {
Set<Dependency<?>> dependencies = ((HasDependencies) binding).getDependencies();
for (Dependency<?> dependency : dependencies) {
Key<?> dependencyKey = dependency.getKey();
Binding<?> dependencyBinding = injector.getExistingBinding(dependencyKey);
if (dependencyBinding != null && Scopes.isScoped(dependencyBinding, whateverScope, WhateverScoped.class)) {
throw new ProvisionException(String.format(
"Singleton %s depends on @WhateverScoped %s",
key, dependencyKey));
}
}
}
}
}