可扩展/适应性强的Java EE应用程序:接口与拦截器和装饰器

时间:2014-01-15 17:19:36

标签: java java-ee ejb extensibility

我们目前正在分析新版Java EE应用程序的技术要求。这是Java EE 6,但转到7是一个选项。到目前为止,这是一个单一的应用程序,一个EAR,提供定义明确的功能。但是,需要能够基于实现项目和/或客户以非常特定的方式定义某些功能。

例如,某些客户将面临非常具体的安全限制。可以显示框架处理的消息列表。对于一个客户,可以让用户看到所有内容,但另一个客户希望仅根据用户的组显示某些类型的消息。这是一个以特定于上下文的方式定义实现,调整核心功能的示例。 另一个可能的要求是一些客户希望扩展给定的功能,增加新的可能性。这是可扩展的部分。

因此,有必要建立一个定义通用功能但具有可插拔部分的架构,以及扩展的可能性。在某些方面,我粗略地了解了如何处理这个问题。这个问题有一个完美适用于表示层的答案,我们将在JSF 2中做到这一点:How to create a modular JSF 2.0 application?

我不太确定业务逻辑层,安全性等。我最初的想法是定义接口(几个外观)并在运行时或部署时找到实现。与服务提供商机制大致相同。可以提供默认实现,可以定义自定义实现。这确实感觉像是一种Java SE解决方案,我想知道我是否只是应用那些我熟悉的概念,如果没有更好的EE。我认为@Alternative注释有这样的目的。

另一种可能性是使用拦截器和装饰器。我不确定拦截器在日志,审计和其他不涉及核心业务逻辑的事情之外的用途有多大。装饰器似乎适合允许使用自定义功能扩展实现,也可能是可插拔部分。

有人可以提供一些见解,了解哪种解决方案最适合这一挑战的哪一部分?我应该将这些方法结合到问题域的各个部分吗?我有没有看到的可能性?

一个重要的要求:我们希望能够将特定于客户/项目的代码分开。我们不希望在每个实现的版本控制下拥有完整版本的应用程序,因为这将成为快速维护的噩梦。理想情况下,也没有必要将其构建为单片EAR,但能够将可插入部件添加到某些lib文件夹或单独部署它们。

5 个答案:

答案 0 :(得分:13)

坦率地说,没有那么多好的方法来制作你的申请modular(我认为这是改写你的要求的正确方法)。

请注意,有很多讨论,失败的尝试和奇怪的设计来简化模块化应用程序的开发,但我们仍然有这么多单片怪物。我一直在支持足够的企业系统受到惊吓,直到我的生命结束。

初始设计非常重要,它不仅仅与interfaces等基本概念有关。请不要误会我的意思。接口很重要,但我更倾向于使用一些不同的术语 - contractsextension points

enter image description here

让我们假设您已正确定义extension points。接下来你会做什么?我会说最终你会实现某种plugin framework。 (您可以花一个小时玩JSPF,看看它如何简化模块化应用程序的开发,并鼓励loose coupling)。

坏消息,这个解决方案可能对任何生产系统都有点危险。主要是因为复杂的类加载策略(由此方法引入)可能导致内存泄漏。所以你会发现自己很快就会分析内存转储。类加载器和所有相关的东西变得有点复杂:)

enter image description here

无论如何,我们假设您已经解决了类加载的所有问题,但插件/模块的生命周期呢?在任何松散耦合的系统中,您都需要严格定义模块的交互方式。插件框架不会完全解决这个问题。

最终,您将提出模块life-cycle,以便定义所有重要方面。例如:

enter image description here

我的建议是避免重新发明轮子。 OSGi可能是一个很好的解决方案。请注意OSGi是好的,成熟的,并提供了很多开箱即用的东西,但它也有点复杂:

enter image description here

所以你肯定需要一些时间来深入研究它。另请检查以下内容:What benefits does OSGi's component system provide you?

另一个建议是检查任何已知的良好和模块化的大型软件产品(例如Jenkins)。

<强>更新

好的,考虑下面的讨论,我建议如下:

  1. Reed或浏览多本相关图书Real World Java EE Patterns Rethinking Best PracticesReal World Java EE Night Hacks Dissecting the Business Tier
  2. 浏览EE patterns
  3. 考虑使用ServiceLocator
  4. 考虑使用Injection over ServiceLocator
  5. 决定是否需要DAO(some help
  6. 不要忘记查看Java EE 7 Tutorial
  7. 再次浏览所有模式并制作小型作弊表,以便不时刷新您的想法,如下所示 - example
  8. 为未来而设计
  9. 最后,创建几个PoC以证明您的方法足够好。
  10. <强>结论

    很抱歉答案很长,但我必须确保它背后有明确而合乎逻辑的观点。你的初衷是100%正确和良好。最好定义接口/契约并隐藏它们背后的所有复杂性。这正是OOP的意思。

    无论如何,最重要的任务不是提供良好的设计,而是随着时间的推移保留它。我的建议是从同一个开始使用松散耦合的方法来强制执行良好的设计。 Service Locator patter是你真正需要的。

    enter image description here

    它将成为保存项目和减少意大利面条代码的某种障碍。如果你发现一些有问题的模块 - 好的,没问题,它将被正确隔离并且易于更换。

    如果您有足够的信心,请跳过Service Locator并转到Dependency Injection。补充阅读:Fowler on Dependency Injection vs Service Locator

答案 1 :(得分:2)

如果您的目标之一是保持源代码分离,我肯定会使用接口方法。您可以使用基于运行时设置的构建器来访问您的代码,并实例化正确的类。

我过去曾采用过这种方法,但只有一个代码库。

答案 2 :(得分:2)

您可以定义基于权限的设计,以(不)向用户(或客户)显示某些消息。并使用主应用程序之外的bsh或python等脚本扩展您的软件功能。

答案 3 :(得分:2)

您是否调查过策略模式?听起来和你想要的完全一样。它允许您的代码在运行时选择行为,而无需使用instanceof或复杂的类继承。这是一个示例,这里是full article

public interface ShippingMethod {
    public double getShippingCost(double weightInPounds, double distanceInMiles);
}

public class FirstClassShipping implements ShippingMethod {
    public double getShippingCost(double weightInPounds, double distanceInMiles) {
        // Calculate the shipping cost based on USPS First class mail table
    }
}

public class FedExShipping implements ShippingMethod {
    public double getShippingCost(double weightInPounds, double distanceInMiles) {
        // Calculate the shipping cost based on FedEx shipping
    }       
}

public class UPSShipping implements ShippingMethod {
    public double getShippingCost(double weightInPounds, double distanceInMiles) {
        // Calculate the shipping cost based on UPS table
    }       
}

public class ShippingInfo {

    private Address address;
    private ShippingMethod shippingMethod;

    public Address getAddress() {
        return this.address;
    }

    public double getShippingCost(double weightInPounds, double distanceInMiles) {
        return shippingMethod.getShippingCost(weightInPounds, distanceInMiles);
    }
}

在您的情况下,您的客户可以为您想要改变的任何视图/服务/模型策略提供“可插拔”实现。

答案 4 :(得分:1)

您可以为所有功能提供REST,并且可以在该REST之上定义CLI(命令行界面)以获得更安全的客户端。如果要构建浏览器前端,则使用相同的REST。在扩展时,您只需要记录何时设计新的API(REST调用)(在go中添加)。在此核心下,您可以通过j2ee安全性配置访问参数来实现任何级别的安全性。我根本不是一个j2ee专家,但我认为你实际上正在为一个jee应用程序寻找最佳的前端设计模式。

JERSEY具有明确的功能,可以通过电线封送东西。 如果你将jaxb与此结合起来,我想你会喜欢它。使用JMS与此俱乐部进行服务更新。