我有一个使用Jersey / JAX-RS进行Web服务(注释等)和Guice注入服务实现的应用程序。我真的不喜欢Guice直接与servlet一起工作的方式,我更喜欢Jersey方式,所以我不得不做些大惊小怪的事情,以使服务注入正常工作,因为Guice不会创建我的servlet类,而我没有不想处理HK2-Guice桥。为此,我创建了一个侦听器类(称为Configuration),该类在应用程序启动时在静态字段中设置了注入器,然后通过创建一个父类来手动实现每个servlet类中的注入,我的所有servlet都通过一个包含以下内容的构造函数进行扩展:
public MasterServlet() {
// in order for the Guice @Inject annotation to work, we have to create a constructor
// like this and call injectMembers(this) on all our injectors in it
Configuration.getMyServiceInjector().injectMembers(this);
Configuration.getDriverInjector().injectMembers(this);
}
我知道这有点hacky,但这在我的servlet中很好用。我可以在服务上使用Guice @Inject批注,并在命名的实现之间进行切换,等等。当我去设置单元测试时,问题就来了。我正在使用JerseyTest进行测试,但是对我的servlet运行测试会导致500的错误,而Guice则显示以下内容:
com.google.inject.ConfigurationException:Guice配置错误: 1)没有实现com.mycompany.MyService的绑定。 定位com.mycompany.MyService时 com.mycompany.servlet.TestGetServlet.service(TestGetServlet.java:21)上的字段 在定位com.mycompany.servlet.TestGetServlet
时
测试如下:
public class TestServletTest extends JerseyTest {
@Test
public void testServletFunctional() {
final String response = target("/testget").request().get(String.class);
assertEquals("get servlet functional", response);
}
@Before
public void setup() {
Configuration configuration = new Configuration();
configuration.contextInitialized(null);
}
@Override
protected Application configure() {
return new ResourceConfig(TestGetServlet.class);
}
}
您会注意到在setup方法中,由于我不能依靠测试容器(Grizzly)来创建它,因此我正在手动创建Configuration类(没有这两行,我会得到NullPointerExceptions)。有关此内容的更多信息。
这是正在测试的servlet:
@Path("/testget")
public class TestGetServlet extends MasterServlet {
@Inject
MyService service;
@GET
@Produces({"text/plain", MediaType.TEXT_PLAIN})
public String testGet() {
//service = Configuration.getServiceInjector().getInstance(MyService.class);
return "get servlet functional";
}
}
是否注意到testGet()方法中的注释行?如果我改为这样做并删除上面的@Inject批注,则一切正常,这表明Grizzly并未按照我期望的方式创建我的servlet。
我认为正在发生的事情是,灰熊对吉斯一无所知。一切似乎都表明Grizzly没有看到Configuration类,尽管事实是,通过将其放入我测试的@Before方法中,它似乎至少对使用它的类是 available (请参阅: TestGetServlet类中的注释行)。我只是不知道如何解决。
答案 0 :(得分:2)
我仍在设法解决这个问题,但与此同时,我从Guice切换到HK2,这花了点功夫,但我认为这对将来遇到此问题的人可能会有帮助。
我认为这是一个答案,因为说实话,我尝试绕过Guice-HK2桥但仍将Guice与Jersey结合使用并不是最佳方法。
从Guice切换到HK2需要花点时间做,并且没有关于所有答案的全面指南。例如,依赖关系确实很挑剔。如果您尝试使用Jersey 2.27,则可能会遇到著名的
java.lang.IllegalStateException:找不到InjectionManagerFactory
错误。由于HK2本身,Jersey 2.27不能与以前的版本向后兼容。我仍在努力使所有功能正常工作,但与此同时,我必须将所有Jersey依赖项降级为2.26-b06才能使HK2正常工作。
庆幸的是,Jersey已经实现了一堆HK2样板文件,因此,要进行注入工作,您需要正确使用@ Contract,@ Service(有关详细信息,请参阅HK2文档),然后再使用两个新的类,如下所示:>
public class MyHK2Binder extends AbstractBinder {
@Override
protected void configure() {
// my service here is a singleton, yours might not be, so just omit the call to in()
// also, the order here is switched from Guice! very subtle!
bind(MyServiceImpl.class).to(MyService.class).in(Singleton.class);
}
}
这:
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new MyHK2Binder());
packages(true, "com.mycompany");
}
}
足够简单,但这仅适用于应用程序本身。测试容器对此一无所知,因此您必须自己在测试类中重做Binder和ResourceConfig,如下所示:
public class TestServletTest extends JerseyTest {
@Test
public void testServletFunctional() {
final String response = target("/testget").request().get(String.class);
assertEquals("get servlet functional", response);
}
@Before
public void setup() {
}
@Override
protected Application configure() {
return new TestServletBinder(TestGetServlet.class);
}
public class TestServletBinder extends ResourceConfig {
public TestServletBinder(Class registeree) {
super(registeree);
register(new MyHK2Binder());
packages(true, "com.mycompany");
}
}
}
执行此操作实际上很好,因为您可以将Binder换成测试绑定器,而在其中将服务绑定到模拟服务或其他东西。我在这里还没有做过,但这很容易做:用一个像这样进行绑定的寄存器替换对register()的调用中的新MyHK2Binder():
bind(MyTestServiceImpl.class).to(MyService.class).in(Singleton.class);
瞧。非常好。显然,使用命名绑定可以达到类似的结果,但是效果很好,甚至可能更简单,更清晰。
希望这可以帮助某人节省我花很多时间来解决这个问题。