我想建立一个基于JEE插件的架构。主要思想是做一些类似于eclipse的事情,但是在JEE的上下文中。我的目标是以最少的模块为核心,并允许其他模块扩展其功能。为此,我使用4个模块实现了一个测试:
gauges: Defines and implements a gaugesregistry service, also defines a gauge POJO.
cashgauges: implements a gauge producer using CDI. this is a plugin mock.
othergauges: implements a gauge producer using CDI. this is a second plugin mock.
gauges-web: Contains a basic JSF view to query the gauges registry.依赖关系如下:
cashgauges --> gauges
othergauges --> gauges
gauges-web --> gauges这是通过在每个部署的文件上使用jboss-deployment-structure.xml来完成的。
部署是作为单独的文件完成的:
gauges.jar
cashgauges.jar
othergauges.jar
gauges-web.war所有服务都会启动,但我看到的是,我的gaugesregistry被实例化了几次。我在调试模式下启动了wildfly,我看到的是每个模块都有自己的gaugesregistry实例: cashgauges和其他debug在注册表上调用相同的方法(addGauge),但是这个注册表的实例是不同的。
使用@ApplicationScoped和@Singleton注释时,这两种情况都会发生。我做错了什么?
源代码可以在https://github.com/hatit/research上找到
几天后,我正在考虑使用ServiceLocator模式和远程引用来代替CDI。有什么建议吗?
发布于 2018-03-17 22:37:47
很好,我得到了两次-2票(-4声誉),因为我问了一个软件开发人员的高级主题?
我在关于stackoverflow中搜索了一下,发现了这个
成立于2008年,Stack Overflow是最大的,最值得信赖的在线社区,供开发人员学习,分享他们的知识,并建立他们的职业生涯...
如果任何人对此主题感兴趣,则:
在理解了CDI Beans和作为独立模块(JBoss模块)使用的EJB生命周期之间的差异几个小时后,我发现:
单例CDI Beans在每个模块中实例化一次,而不是所有模块之间真正的单例。
为了避免这种情况,我必须将Registry创建为Singleton Enterprise Session Bean。这带来了新的问题,CDI注入不能在模块之间工作,所以我不得不打包一个CDI生成器(我不在乎它是不是单例,它只是一个生成器),它可以被任何模块实例化。这个生产者的主要职责是查找注册表EJB,这是为了避免每次我需要访问注册表时硬编码jndi路径。
我改变了我的简单的例子来支持JSF插件,这是我目前使用的一个例子。
模块facelets:
注册表接口:
public interface FaceletsModuleRegistry {
void registerModule(String module);
List<String> getRegisteredModules();
}注册表实现:
@Local(FaceletsModuleRegistry.class)
@Singleton(name="FaceletsModuleRegistry")
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Vetoed
public class FaceletsModuleRegistryImpl implements FaceletsModuleRegistry {
private Set<String> registeredModuleNames = new TreeSet<>();
@Override
public void registerModule(String module) {
registeredModuleNames.add(module);
}
@Override
public List<String> getRegisteredModules() {
return Collections.unmodifiableList(new ArrayList<>(registeredModuleNames));
}
}注册表生产者:
@ApplicationScoped
public class FaceletsModuleRegistryBuilder {
@EJB(lookup="java:global/facelets/FaceletsModuleRegistry!co.hatit.enterprise.facelets.services.FaceletsModuleRegistry")
protected FaceletsModuleRegistry faceletsModuleRegistry;
@Produces
public FaceletsModuleRegistry getFaceletsModuleRegistry(){
return faceletsModuleRegistry;
}
}我想要插件的任何其他模块都可以实现此代码(请参阅@Inject可用于任何需要访问注册表单例实例的模块):
@ApplicationScoped
public class InmueblesActivator {
@Inject
private FaceletsModuleRegistry faceletsModuleRegistry;
public void init(@Observes @Initialized(ApplicationScoped.class) Object init){
String moduleName = Module.getCallerModule().getIdentifier().getName();
String name = StringUtils.substringBetween(moduleName, "deployment.", ".jar");
faceletsModuleRegistry.registerModule(name);
}
}然后,我可以从任何模块中引用Registry作为一个真正的单例实例(解决了在几个模块之间使用CDI单例bean时同一个类的多个实例的问题)。
现在,我可以插入JEE模块,不仅仅是java代码,还可以插入facelets资源:
public class FaceletsResourceHandler extends ResourceHandlerWrapper {
Logger logger = LoggerFactory.getLogger(FaceletsResourceHandler.class);
@Inject
FaceletsModuleRegistry faceletsModuleRegistry;
private ResourceHandler wrapped;
public FaceletsResourceHandler(ResourceHandler wrapped) {
this.wrapped = wrapped;
}
@Override
public ViewResource createViewResource(FacesContext context, final String name) {
ViewResource resource = super.createViewResource(context, name);
if (resource == null) {
resource = new ViewResource() {
@Override
public URL getURL() {
try {
//iterates over plugins to find the required resource.
for(String module : faceletsModuleRegistry.getRegisteredModules()){
URL resource = Module.getCallerModule().getModuleLoader()
.loadModule(ModuleIdentifier.create("deployment." + module + ".jar"))
.getExportedResource("META-INF/resources" + name);
if (resource != null) return resource;
}
} catch (ModuleLoadException e) {
throw new FacesException(e);
}
return null;
}
};
}
return resource;
}
@Override
public ResourceHandler getWrapped() {
return wrapped;
}
}https://stackoverflow.com/questions/49096953
复制相似问题