EJB3 - 自远程注入/EJB 插件
EJB3 - Self Remote Injection / EJB Plugins
我想使用 EJB3 实现一种 "plugin mechanism"。我有两个以上 war,每个都包含自己的 bean 类型,使用在单独项目中定义的远程接口。基本上,我希望始终部署我的主 Bean(产品)并为其他 Bean(n 个不同的项目 Bean)提供注册机制。重要的是任意 Bean 都可以注册,只要它们知道产品的远程接口。示例代码:
产品 Bean
@Singleton
@ConcurrencyManagement(BEAN)
public class ProductBean implements ProductRemote, Serializable {
private static final long serialVersionUID = 4686943363874502919L;
private ProjectRemote project;
public void registerProject(ProjectRemote project) {
this.project = project;
}
public void something() {
if(project != null)
project.doSomething();
}
}
Bean 项目
@ConcurrencyManagement(BEAN)
@Singleton
@Startup
public class ProjectBean implements ProjectRemote, Serializable {
private static final long serialVersionUID = -2034521486195622039L;
@EJB(lookup="java:global/product/ProductBean")
private ProductRemote product;
@PostConstruct
private void registerSelf() {
product.registerProject(this);
}
public void doSomething() {
System.err.println("FOOBAR");
}
}
产品界面
@Remote
public interface ProductRemote {
public void registerProject(ProjectRemote project);
}
项目界面
@Remote
public interface ProjectRemote {
public void doSomething();
}
不幸的是,当我尝试部署项目时,出现了 ClassNotFoundException:
22:56:33,146 ERROR [org.jboss.as.controller.management-operation] (XNIO-1 task-7) JBAS014613: Operation ("add") failed - address: ([{"deployment" => "project-0.1-SNAPSHOT.war"}]) - failure description: {"JBAS014671: Failed services" => {"jboss.deployment.unit.\"project-0.1-SNAPSHOT.war\".component.ProjectBean.START" => "org.jboss.msc.service.StartException in service jboss.deployment.unit.\"project-0.1-SNAPSHOT.war\".component.ProjectBean.START: java.lang.IllegalStateException: JBAS011048: Failed to construct component instance
Caused by: java.lang.IllegalStateException: JBAS011048: Failed to construct component instance
Caused by: javax.ejb.EJBException: java.lang.RuntimeException: JBAS014154: Failed to marshal EJB parameters
Caused by: java.lang.RuntimeException: JBAS014154: Failed to marshal EJB parameters
Caused by: java.lang.ClassNotFoundException: com.xxx.test.project.ProjectBean from [Module \"deployment.product-0.1-SNAPSHOT.war:main\" from Service Module Loader]"}}
所以我的问题是:有什么办法可以实现这样的功能吗?还是因为每个 war 的类路径不同而完全不可能?
我认为第一个问题是,即使您正在共享您的界面,客户端实现 class 仍然无法用于其他项目,并且它的实例加载了不同的 classloader(即另一个 war 容器)。
您可以使用消息 bean (JMS) 设计来实现,这将允许关注点分离、准注册(到 queue/topic),并允许您部署您想要的任何实现。查看我如何使用 .
另一种解决方案是使用委托模式,例如:
假设您从两个单独的 war 部署转到单个 EAR 部署,您的项目将具有以下模块。
我的图书馆
EJB 接口:
@Local
public interface MyBeanLocal
public void doSomething(String something);
MyClient 注释:
@Documented
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface MyClient {
}
我的默认注释:
@Documented
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface MyDefault {
}
myClientEJB
@Stateless
@MyClient
public class MyBean implements MyBeanLocal {
@Inject
@MyDefault
private MyBeanLocal myDefault;
@Override
public void doSomething(String something) {
myDefault.doSomething(something);
}
}
myDefaultEJB
@Stateless
@MyDefault
public class MyBean implements MyBeanLocal {
@Override
public void doSomething(String something) {
// do something
}
}
我的网站
@Named
@Request
public class MyRequestBean {
@Inject
@MyClient
private MyBeanLocal myClient;
public void something() {
myClient.doSomething("...");
}
}
最后,您可能需要考虑 OSGi。我没有对此做任何事情,但就注册资源而言,这可能是您正在寻找的东西。当然,您始终可以考虑 资源适配器,具体取决于您的问题域。
我的一位同事给了我正确的提示,告诉我我在代码中做错了什么 - 我写代码的方式是试图让我的 实现 注入。显然,由于 class 路径(和 JVM)不同,这不起作用。相反,我不得不将 对为远程接口创建的代理对象 的引用传递到注册方法中。
因此,为了使上述代码正常工作,对 Project.java 进行了两项更改:
为自己添加一个 EJB:
@EJB
private ProjectRemote self;
更改@PostConstruct 方法以引用 EJB
@PostConstruct
private void registerSelf() {
product.registerProject(self);
}
这样,class 的实际实现不需要被使用它的 class 知道。容器根据为 EJB 创建的代理对象知道将信息传递到哪里。
我想使用 EJB3 实现一种 "plugin mechanism"。我有两个以上 war,每个都包含自己的 bean 类型,使用在单独项目中定义的远程接口。基本上,我希望始终部署我的主 Bean(产品)并为其他 Bean(n 个不同的项目 Bean)提供注册机制。重要的是任意 Bean 都可以注册,只要它们知道产品的远程接口。示例代码:
产品 Bean
@Singleton
@ConcurrencyManagement(BEAN)
public class ProductBean implements ProductRemote, Serializable {
private static final long serialVersionUID = 4686943363874502919L;
private ProjectRemote project;
public void registerProject(ProjectRemote project) {
this.project = project;
}
public void something() {
if(project != null)
project.doSomething();
}
}
Bean 项目
@ConcurrencyManagement(BEAN)
@Singleton
@Startup
public class ProjectBean implements ProjectRemote, Serializable {
private static final long serialVersionUID = -2034521486195622039L;
@EJB(lookup="java:global/product/ProductBean")
private ProductRemote product;
@PostConstruct
private void registerSelf() {
product.registerProject(this);
}
public void doSomething() {
System.err.println("FOOBAR");
}
}
产品界面
@Remote
public interface ProductRemote {
public void registerProject(ProjectRemote project);
}
项目界面
@Remote
public interface ProjectRemote {
public void doSomething();
}
不幸的是,当我尝试部署项目时,出现了 ClassNotFoundException:
22:56:33,146 ERROR [org.jboss.as.controller.management-operation] (XNIO-1 task-7) JBAS014613: Operation ("add") failed - address: ([{"deployment" => "project-0.1-SNAPSHOT.war"}]) - failure description: {"JBAS014671: Failed services" => {"jboss.deployment.unit.\"project-0.1-SNAPSHOT.war\".component.ProjectBean.START" => "org.jboss.msc.service.StartException in service jboss.deployment.unit.\"project-0.1-SNAPSHOT.war\".component.ProjectBean.START: java.lang.IllegalStateException: JBAS011048: Failed to construct component instance
Caused by: java.lang.IllegalStateException: JBAS011048: Failed to construct component instance
Caused by: javax.ejb.EJBException: java.lang.RuntimeException: JBAS014154: Failed to marshal EJB parameters
Caused by: java.lang.RuntimeException: JBAS014154: Failed to marshal EJB parameters
Caused by: java.lang.ClassNotFoundException: com.xxx.test.project.ProjectBean from [Module \"deployment.product-0.1-SNAPSHOT.war:main\" from Service Module Loader]"}}
所以我的问题是:有什么办法可以实现这样的功能吗?还是因为每个 war 的类路径不同而完全不可能?
我认为第一个问题是,即使您正在共享您的界面,客户端实现 class 仍然无法用于其他项目,并且它的实例加载了不同的 classloader(即另一个 war 容器)。
您可以使用消息 bean (JMS) 设计来实现,这将允许关注点分离、准注册(到 queue/topic),并允许您部署您想要的任何实现。查看我如何使用
另一种解决方案是使用委托模式,例如:
假设您从两个单独的 war 部署转到单个 EAR 部署,您的项目将具有以下模块。
我的图书馆
EJB 接口:
@Local
public interface MyBeanLocal
public void doSomething(String something);
MyClient 注释:
@Documented
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface MyClient {
}
我的默认注释:
@Documented
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface MyDefault {
}
myClientEJB
@Stateless
@MyClient
public class MyBean implements MyBeanLocal {
@Inject
@MyDefault
private MyBeanLocal myDefault;
@Override
public void doSomething(String something) {
myDefault.doSomething(something);
}
}
myDefaultEJB
@Stateless
@MyDefault
public class MyBean implements MyBeanLocal {
@Override
public void doSomething(String something) {
// do something
}
}
我的网站
@Named
@Request
public class MyRequestBean {
@Inject
@MyClient
private MyBeanLocal myClient;
public void something() {
myClient.doSomething("...");
}
}
最后,您可能需要考虑 OSGi。我没有对此做任何事情,但就注册资源而言,这可能是您正在寻找的东西。当然,您始终可以考虑 资源适配器,具体取决于您的问题域。
我的一位同事给了我正确的提示,告诉我我在代码中做错了什么 - 我写代码的方式是试图让我的 实现 注入。显然,由于 class 路径(和 JVM)不同,这不起作用。相反,我不得不将 对为远程接口创建的代理对象 的引用传递到注册方法中。
因此,为了使上述代码正常工作,对 Project.java 进行了两项更改:
为自己添加一个 EJB:
@EJB private ProjectRemote self;
更改@PostConstruct 方法以引用 EJB
@PostConstruct private void registerSelf() { product.registerProject(self); }
这样,class 的实际实现不需要被使用它的 class 知道。容器根据为 EJB 创建的代理对象知道将信息传递到哪里。