我很喜欢学习 JAX-RS 和 Jersey,但是我在尝试测试需要注入 DAO 的简单资源时遇到了障碍,如下所示:
@Stateless
@Path("simple")
public class SimpleResource {
@PersistenceContext
private EntityManager em;
// @GET, etc...
}
(我将转向更抽象的 DAO 模式,但问题是一样的,即如何注入 @EJB DAO?)
在我的单元测试中,我使用的是在其 web.xml 中配置 Jersey 的嵌入式 Jetty 服务器,并且我想挂钩资源的生命周期,以便我可以注入一个模拟 EntityManager,但我还没有找到一个干净的答案经过大量搜索。你能帮我吗?我遇到的一些可能的方向:
1) 在我的代码中使用 JNDI 上下文查找来获取 DAO bean,并在测试中注册模拟对象。
而不是@EJB 或@PersistenceContext,在资源的构造函数中使用类似这样的东西:
theDAO = (DAOImpl) new InitialContext().lookup("java:global/EJB/DAOImpl");
但是,这意味着我的测试环境需要支持 JNDI,而在 Jetty 中这样做可能会带来一些痛苦。另外,它不使用干净的注释方法。
2)使用方法注入。
注入该方法,以便我可以设置 DAO 后实例化,例如,
@PersistenceContext(name = "persistence/pu00")
public void setPersistenceUnit00(final EntityManager em) {
em00 = em;
}
或者
private MyEjbInterface myEjb;
@EJB(mappedName="ejb/MyEjb")
public void setMyEjb(MyEjb myEjb) {
this.myEjb = myEjb;
}
但是,要做到这一点,我需要 Jersey 实例化的实例,例如 SimpleResource。我怎么得到它?
3)使用反射。
一种 DIY 注射,类似于:
public static void setPrivateField(Class<? extends Object> instanceFieldClass, Object instance, String fieldName, Object fieldValue) {
Field setId = instanceFieldClass.getDeclaredField(fieldName);
setId.setAccessible(true);
setId.set(instance, fieldValue);
}
同样,我需要 Jersey 实例化的实例。
4) 使用注入提供程序。
我仍然粗略地了解它是如何工作的,但看起来 Jersey 提供了一种定义自定义可注射注释的方法,例如,
@Provider
public class EJBProvider implements InjectableProvider<EJB, Type> {
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
public Injectable getInjectable(ComponentContext cc, EJB ejb, Type t) {
if (!(t instanceof Class)) {
return null;
}
try {
Class c = (Class) t;
Context ic = new InitialContext();
final Object o = ic.lookup(c.getName());
return new Injectable<Object>() {
public Object getValue() {
return o;
}
};
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
使用辅助类的变体:
Server server = new Server(8080);
Context root = new Context(server,"/",Context.SESSIONS);
ResourceConfig rc = new PackagesResourceConfig("edu.mit.senseable.livesingapore.platform.restws.representations");
rc.getSingletons().add(new SingletonTypeInjectableProvider<javax.ws.rs.core.Context, Myobj>(Myobj.class, new Myobj(12,13)){});
root.addServlet(new ServletHolder(new ServletContainer(rc)), "/");
server.start();
有了这个用途:
@Path("/helloworld")
public class HelloWorldResource {
@Context Myobj myClass;
....
}
这对@EJB 或@PersistenceContext 是否可行?
5) 扩展 javax.ws.rs.core.Application。
对此粗略,但是:
@javax.ws.rs.ApplicationPath("application")
public class InjectionApplication extends javax.ws.rs.core.Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> classes = new HashSet<Class<?>>();
public InjectionApplication() {
// no instance is created, just class is listed
classes.add(BookResource.class);
}
@Override
public Set<Class<?>> getClasses() {
return classes;
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}
6)扩展ServletContainer。
使用 InjectableProvider 的旧风格?看起来更复杂:
public class ServletAdapter extends ServletContainer {
@Override
protected void configure(ServletConfig servletConfig, ResourceConfig rc, WebApplication wa) {
super.configure(servletConfig, rc, wa);
rc.getSingletons().add(new InjectableProvider<Resource, Type>() {
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
public Injectable<Object> getInjectable(ComponentContext ic, Resource r, Type c) {
final Holder value = new Holder();
Context ctx = new InitialContext();
try {
value.value = ctx.lookup(r.name());
} catch (NamingException ex) {
value.value = ctx.lookup("java:comp/env/" + r.name());
}
return new Injectable<Object>() {
public Object getValue() {
return value.value;
}
};
}
});
}
}
7) 使用嵌入式 EJB 容器。
例如, http: //openejb.apache.org。这很重,我预计开始工作会很混乱。(事实上,让我开始走“Jetty + Jersey”路线的是 GlassFish Embedded 中围绕安全登录的一个错误。我还查看了其他 Java EE 6 应用程序容器,如 JBoss AS,但每个在嵌入式模式下都有问题,用户有限社区支持。)
8) 使用第三方 IoC 库,如 Spring 或 Guice。
Spring 显然通常用于解决这类问题(在单元测试时注入模拟),但我想避免不得不学习另一组 API ——纯 Java EE 已经足够挑战了!但如果这是最好的解决方案,我很乐意。我还没有仔细研究过 Spring 或 Guice。
你成功使用过这些吗?您喜欢其他任何解决方案吗?我真的很期待您对此的建议。提前致谢——马特