0

我想构建一个基于 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 和 othergaugesaddGauge在注册表上调用相同的方法 ( ),但此注册表的实例不一样。

这在两种情况下都会发生,使用@ApplicationScoped@Singleton注释。我究竟做错了什么?

源代码可在https://github.com/hatit/research获得

几天后,我正在考虑使用 ServiceLocator 模式和远程引用而不是 CDI。有什么建议么?

4

1 回答 1

0

太好了,我获得了两次 -2 票(-4 声望),因为我问了软件开发人员的高级主题?

我搜索了关于stackoverflow并找到了这个

Stack Overflow 成立于 2008 年,是最大、最值得信赖的在线社区,供开发人员学习、分享知识和建立职业生涯......

如果对这个话题感兴趣,那么:

经过几个小时了解 CDI Bean 和 EJB 作为独立模块(JBoss 模块)使用时生命周期之间的差异后,我发现:

单例 CDI Bean 每个模块实例化一次,而不是所有模块中的真正单例。

为了避免这种情况,我必须将注册表创建为单例企业会话 Bean。这带来了新问题,CDI 注入在模块之间不起作用,所以我必须打包一个 CDI 生产者(我不在乎它是否是单例的,它只是一个生产者),它可以被任何模块实例化。这个生产者的主要职责是查找注册表 EJB,以避免每次我需要访问注册表时硬编码 jndi 路径。

我更改了我的简单示例以支持 JSF 插件,这是我当前使用的示例。

模块面:

注册表界面:

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;
    }

}
于 2018-03-17T14:37:47.410 回答