简体   繁体   English

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

[英]Extensible/adaptable Java EE application: interfaces vs interceptors and decorators

We are currently in the process of analyzing the technical requirements for a new version of a Java EE application. 我们目前正在分析Java EE应用程序新版本的技术要求。 It's Java EE 6, but moving onto 7 is an option. 它是Java EE 6,但可以选择迁移到7。 Until now this was a single application, one EAR, offering well-defined functionality. 到目前为止,这只是一个提供完善功能的应用程序,一个EAR。 However, it needs to become possible to define some functionality in a very specific way based on implementation project and/or customer. 但是,变得有可能根据实施项目和/或客户以非常特定的方式定义一些功能。

For example, some customers are going to have very specific security constraints. 例如,某些客户将具有非常特定的安全性约束。 A list of messages processed by a framework can be shown. 可以显示框架处理的消息列表。 For one customer, it's okay to have users see everything, but another customer would like to show only certain types of messages based on the user's group. 对于一个客户,可以让用户看到所有内容,但是另一位客户只希望根据用户的组显示某些类型的消息。 That's an example of defining an implementation in a context-specific way, adapting the core functionality. 这是以特定于上下文的方式定义实现并调整核心功能的示例。 Another possible requirement is that some customers will want to expand on the given functionality, adding new possibilities. 另一个可能的要求是,某些客户希望扩展给定的功能,从而增加新的可能性。 So that's the extensible part. 这就是可扩展部分。

Because of this, it is necessary to have an architecture that defines generic functionality but has pluggable parts, as well as a possibility for extension. 因此,必须有一个定义通用功能但具有可插拔部件的体系结构,并有可能进行扩展。 For some aspects I have a rough idea of how this could be handled. 在某些方面,我对如何处理有一个粗略的了解。 This question has an answer that would work perfectly for the presentation layer, which we'll do in JSF 2: How to create a modular JSF 2.0 application? 这个问题有一个非常适合表示层的答案,我们将在JSF 2中做到这一点: 如何创建模块化JSF 2.0应用程序?

I'm less certain about the business logic layer, security etc. My initial idea would be to define interfaces (a couple of facades) and find the implementation at run-time or deployment time. 我不太确定业务逻辑层,安全性等。我最初的想法是定义接口(几个立面)并在运行时或部署时找到实现。 In much the same way as the service provider mechanism. 与服务提供者机制几乎相同。 A default implementation can be offered, with the possibility of defining a custom one. 可以提供默认的实现,并可以定义一个自定义的实现。 This does feel like a sort of Java SE solution, and I'm wondering if I'm just applying concepts that I'm familiar with from that context and if there's nothing better for EE. 这确实感觉像是一种Java SE解决方案,我想知道我是否只是在应用我从该上下文中熟悉的概念,而EE是否没有更好的选择。 I think the @Alternative annotation serves a purpose like this. 我认为@Alternative注释具有这样的目的。

Another possibility might be to use interceptors and decorators. 另一种可能是使用拦截器和装饰器。 I'm not certain to what extent interceptors are useful outside of logging, auditing and other things that don't touch the core business logic. 我不确定拦截器在日志记录,审计和其他不影响核心业务逻辑的事情之外有用的程度。 Decorators seem suitable for allowing an implementation to be extended with custom functionality, and perhaps also the pluggable parts. 装饰器似乎适合允许使用自定义功能以及可能的可插入部件扩展实现。

Can someone offer some insight into which solution is best for which part of this challenge? 有人可以提供一些见解,以了解哪种解决方案最能应对这一挑战吗? Should I combine these methods for various parts of the problem domain? 是否应将这些方法结合用于问题域的各个部分? Are there possibilities I'm not seeing? 有没有我看不到的可能性?

An important requirement: we want to make it possible to keep the code that is specific to a customer/project separate. 一个重要的要求:我们希望能够将特定于客户/项目的代码分开。 We don't want to have a complete version of the application under version control for each implementation, as that would become a maintenance nightmare fast. 我们不想为每个实现都拥有受版本控制的完整版本的应用程序,因为这将很快成为维护的噩梦。 Ideally, it would also not be necessary to build this as a monolithic EAR, but be capable of adding pluggable parts to some lib folder or deploy them separately. 理想情况下,也不必将其构建为整体式EAR,而是能够将可插拔部件添加到某个lib文件夹或单独部署它们。

Frankly speaking there are not so many good approaches to make your application modular (I think this is the proper way to rephrase your requirements). 坦白地说,没有多少好的方法可以使您的应用程序modular (我认为这是重新表达您的要求的正确方法)。

Please also note, there were so many discussions, failed attempts and strange designs to simplify development of modular applications, but still we have so many monolithic monsters. 还请注意,为了简化模块化应用程序的开发,进行了太多的讨论,失败的尝试和奇怪的设计,但是我们仍然有那么多单片怪兽。 I've been supporting enough enterprise systems to be frightened till the end of my life. 我一直在支持足够多的企业系统,直到我生命的尽头。

Initial design is very important and it is not only about basic concepts like interfaces . 初始设计非常重要,不仅涉及interfaces等基本概念。 Please don't get me wrong. 请不要误会我的意思。 Interfaces are important, but I would prefer to use a bit different terminology -- contracts or extension points 接口很重要,但我希望使用一些不同的术语- contractsextension points

在此处输入图片说明

Let's imagine you've defined extension points properly. 假设您已经正确定义了extension points What will you do next? 接下来您要做什么? I'd say eventually you'll implement some kind of plugin framework . 我说最终您将实现某种plugin framework (You can spend an hour to play with JSPF and see how it simplifies development of modular applications and encourages loose coupling ). (您可以花一个小时来使用JSPF,并查看它如何简化模块化应用程序的开发并鼓励松耦合 )。

Bad news, this solution might be a bit dangerous for any production system. 坏消息是,此解决方案可能对任何生产系统都有些危险。 Mainly because complex class loading strategy (introduced by this approach) might cause memory leaks. 主要是因为复杂的类加载策略(此方法引入)可能导致内存泄漏。 So you'll find yourself analyzing memory dumps quite soon. 因此,您很快就会发现自己正在分析内存转储。 Class-loaders and all related stuff became a bit complex :) 类加载器和所有相关的东西变得有点复杂:)

在此处输入图片说明

Anyway, let's assume you've solved all issues with class loading, but what about plugins/modules life-cycle? 无论如何,我们假设您已经解决了类加载的所有问题,但是插件/模块的生命周期又如何呢? In any loosely coupled system you'll need to to strictly define how modules will interact. 在任何松散耦合的系统中,您都需要严格定义模块之间的交互方式。 Plugin framework will not entirely solve this problem. 插件框架不会完全解决这个问题。

Eventually you'll come up with modules life-cycle in order to define all important aspects. 最终,您将提出模块的life-cycle ,以定义所有重要方面。 For example: 例如:

在此处输入图片说明

My suggestion is to avoid reinventing the wheel. 我的建议是避免重新发明轮子。 OSGi might be a good solution for you. OSGi可能是您的理想解决方案。 Please also note OSGi is good, mature and provides a lot stuff out of the box, but it is also somewhat complex: 另请注意,OSGi很好,很成熟,并且提供了很多现成的东西,但是它也有些复杂:

在此处输入图片说明

So you'd definitely need some time to investigate it a bit deeply. 因此,您肯定需要一些时间来对其进行深入研究。 Please also check the following : What benefits does OSGi's component system provide you? 请同时检查以下内容: OSGi的组件系统为您提供什么好处?

Another suggestion is to check any existing big software product known to be good and modular ( Jenkins , for example). 另一建议是检查任何已知的良好且模块化的现有大型软件产品(例如Jenkins )。

Update 更新资料

Ok, taking into the account the discussion below I would suggest the following: 好吧,考虑到下面的讨论,我建议以下几点:

  1. Reed or looks through several relevant books Real World Java EE Patterns Rethinking Best Practices , Real World Java EE Night Hacks Dissecting the Business Tier 励志或浏览几本相关书籍《 Real World Java EE模式反思最佳实践》 ,《 Real World Java EE Night Hacks剖析业务层》
  2. Look through EE patterns 浏览EE模式
  3. Consider using of ServiceLocator 考虑使用ServiceLocator
  4. Consider using Injection over ServiceLocator 考虑在ServiceLocator上使用注入
  5. Decide whether or not you need DAO ( some help ) 确定是否需要DAO( 一些帮助
  6. Do not forget to review Java EE 7 Tutorial 不要忘记阅读Java EE 7教程
  7. Looks through all patterns once again and create small cheat-sheet in order to refresh your mind from time to time, something like the following - example 再次浏览所有模式并创建一个小的备忘单,以不时刷新您的想法,例如以下示例
  8. Design for the future 面向未来的设计
  9. Finally, create several PoCs in order to prove your approach is good enough. 最后,创建几个PoC,以证明您的方法足够好。

Conclusion 结论

Sorry for the long answer, but I have to make sure there is a clear and logical point behind it. 很抱歉,答案很长,但是我必须确保其背后有一个明确和合乎逻辑的观点。 Your initial intention is 100% correct and good. 您的初衷是100%正确和良好。 It is really better to define interfaces/contracts and hide all complexity behind them. 定义接口/合同并隐藏其后的所有复杂性确实更好。 This is exactly what OOP is about. 这正是OOP的目的。

Anyway, the most important task is not to provide good design, but preserve it over time. 无论如何,最重要的任务不是提供良好的设计,而是随着时间的流逝保留它。 My suggestion is to enforce good design by using loosely-coupled approach from the same beginning. 我的建议是从一开始就使用松耦合方法来实施良好的设计。 Service Locator patter is what you actually need. 服务定位器的模式是您真正需要的。

在此处输入图片说明

It will act as a some kind of barrier to save your project and minimize spaghetti-like code. 这将成为保存您的项目并最小化类似意大利面条的代码的障碍。 If you identify some problematic module - ok, no problem, it will be properly isolated and easy to replace. 如果确定有问题的模块-好的,没问题,它将被正确隔离并且易于更换。

If you have enough confidence skip Service Locator and go for Dependency Injection . 如果您有足够的信心,请跳过Service Locator ,然后进行Dependency Injection Additional reading: Fowler on Dependency Injection vs Service Locator 附加阅读: Fowler依赖注入与服务定位器

If one of your goals is to keep the source code separate, I would definitely use the interface approach. 如果您的目标之一是使源代码分开,那么我肯定会使用接口方法。 You could access your code using a builder based on run-time settings and instantiate the proper class. 您可以使用基于运行时设置的构建器来访问代码,并实例化适当的类。

I have done this approach in the past, although with one code base. 尽管只有一个代码库,但我过去曾经做过这种方法。

You can define a permission-based design to (not) show to a user (or costumer) some messages. 您可以定义基于权限的设计,以(不)向用户(或客户)显示某些消息。 And extend your software functionalities using scripts like bsh or python outside the main application. 并在主应用程序外部使用bsh或python等脚本扩展软件功能。

Have you investigated the Strategy Pattern? 您是否研究了策略模式? It sounds exactly like what you are looking for. 听起来完全像您要找的东西。 It allows your code to select behaviors at run time without using instanceof or complex class inheritance. 它允许您的代码在运行时选择行为,而无需使用instanceof或复杂的类继承。 Here is an example and here is the 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);
    }
}

In your case, your clients can provide "pluggable" implementations for whatever view/service/model strategy you want to let them alter. 就您而言,您的客户可以为您想让他们改变的任何视图/服务/模型策略提供“可插入”的实现。

You may provide a REST for all of your features and you can define a CLI (Command line interface) on top of that REST for more secure clients. 您可以为所有功能提供REST,并且可以在该REST之上定义CLI(命令行界面)以获取更安全的客户端。 You use that same REST if you want to build a browser front-end. 如果要构建浏览器前端,则可以使用相同的REST。 While extending, You only need to document when a new API (REST call) is designed(added in the go). 在扩展时,您仅需要在设计新的API(REST调用)时添加文档(即可添加)。 Under this core, You can go for any level of security by configuring access parameters through j2ee security. 在此核心下,可以通过j2ee安全性配置访问参数来获得任何级别的安全性。 I am not a j2ee expert at all, but I am thinking that you are actually looking for a best design pattern for a front-end for a jee app. 我根本不是j2ee专家,但是我认为您实际上是在为jee应用程序的前端寻找最佳设计模式。

JERSEY has clear functionality for marshaling things over wires. JERSEY具有清晰的功能,可通过电线封送邮件。 If you combine jaxb with this, I think you will be liking it. 如果您jaxbjaxb结合使用,我想您会喜欢的。 Club this with JMS for service updates. 将其与JMS以更新服务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM