让我们分享基于Java的Web应用程序体系结构!
Web应用程序有许多不同的体系结构,这些体系结构将使用Java来实现。这个问题的答案可以用作各种Web应用程序设计的库,各有其优缺点。虽然我意识到答案将是主观的,但让我们尝试尽可能客观,并激发我们列出的利弊。
使用您喜欢的详细信息级别来描述您的体系结构。为了使您的答案具有任何价值,您至少必须描述所描述的体系结构中使用的主要技术和思想。最后但并非最不重要的一点,我们什么时候应该使用您的体系结构?
我开始...
体系结构概述
我们使用基于Sun的开放标准(如Java EE,Java Persistence API,Servlet和Java Server Pages)的三层体系结构。
- 坚持不懈
- 商业
- 介绍
层之间可能的通信流由以下方式表示:
Persistence <-> Business <-> Presentation
例如,这意味着表示层从不调用或执行持久性操作,而是始终通过业务层进行操作。该体系结构旨在满足高可用性Web应用程序的需求。
坚持不懈
执行创建,读取,更新和删除(CRUD)持久性操作。在本例中,我们正在使用(Java Persistence API)JPA,并且当前使用Hibernate作为持久性提供程序,并使用其EntityManager。
该层分为多个类别,其中有某种类型的实体的每一类交易(涉及到购物车即实体可能是由一个单独的持久类得到处理),并使用一个且只有一个经理。
此外,该层还存储JPA实体哪些是喜欢的东西Account
,ShoppingCart
等等。
商业
与Web应用程序功能相关的所有逻辑均位于此层。此功能可能是为希望使用其信用卡在线购买产品的客户启动汇款。也可能是在基于网络的游戏中创建新用户,删除用户或计算战斗结果。
该层分为多个类,每个类都带有注释,@Stateless
以成为无状态会话Bean(SLSB)。每个SLSB都称为管理器,例如,管理器可以是注释为的类,称为AccountManager
。
当AccountManager
需要执行CRUD操作时,它将对实例进行适当的调用,该实例AccountManagerPersistence
是持久层中的类。两种方法的大致草图AccountManager
可能是:
...
public void makeExpiredAccountsInactive() {
AccountManagerPersistence amp = new AccountManagerPersistence(...)
// Calls persistence layer
List<Account> expiredAccounts = amp.getAllExpiredAccounts();
for(Account account : expiredAccounts) {
this.makeAccountInactive(account)
}
}
public void makeAccountInactive(Account account) {
AccountManagerPersistence amp = new AccountManagerPersistence(...)
account.deactivate();
amp.storeUpdatedAccount(account); // Calls persistence layer
}
我们使用容器管理器事务,因此我们不必自己进行事务划分。根本上发生的事情是,我们在进入SLSB方法时启动一个事务,并在退出该方法之前立即提交(或回滚)它。这是约定而不是配置的示例,但是除了默认值(必需)外,我们不需要任何其他东西。
这是Sun的Java EE 5教程解释Enterprise JavaBeans(EJB)的Required事务属性的方式:
如果客户端在事务中运行并调用企业bean的方法,则该方法在客户端的事务中执行。如果客户端未与事务关联,则容器在运行该方法之前启动一个新事务。
Required属性是使用容器管理的事务划分运行的所有企业Bean方法的隐式事务属性。除非您需要覆盖另一个事务属性,否则通常不设置Required属性。由于事务属性是声明性的,因此以后可以轻松更改它们。
介绍
我们的演示层负责...演示!它负责用户界面,并通过构建HTML页面并通过GET和POST请求接收用户输入来向用户显示信息。当前,我们正在使用旧的Servlet + Java Server Pages(JSP)组合。
该层调用业务层管理器中的方法以执行用户请求的操作并接收要在网页中显示的信息。有时,从业务层接收到的信息是不太复杂的类型,String
年代和int
egers,并在其他时间JPA实体。
架构的优缺点
优点
- 在这一层中拥有与执行持久性的特定方式有关的所有内容,仅意味着我们可以从使用JPA转换为其他东西,而不必在业务层中重新编写任何内容。
- 对于我们来说,将表示层换成其他层很容易,而且如果发现更好的地方,我们很可能会这样做。
- 让EJB容器管理事务边界很好。
- 使用Servlet的+ JPA很容易(开始),并且该技术已在许多服务器中广泛使用和实现。
- 使用Java EE可以使我们更轻松地创建具有负载平衡和故障转移功能的高可用性系统。我们都认为我们必须拥有这两者。
缺点
- 使用JPA,您可以通过使用
@NamedQuery
JPA实体类上的注释将常用查询存储为命名查询。如果您在持久性类中与持久性有尽可能多的关系(如在我们的体系结构中那样),则这将扩展您可以在其中找到包含JPA实体的查询的位置。概述持久性操作将变得更加困难,因此难以维护。 - 我们将JPA实体作为持久层的一部分。但是
Account
和ShoppingCart
,它们不是真正的业务对象吗?通过这种方式可以完成此操作,因为您必须触摸这些类并将它们变成JPA知道如何处理的实体。 - JPA实体也是我们的业务对象,其创建方式类似于数据传输对象(DTO),也称为值对象(VO)。由于业务对象除了访问者方法之外没有其他逻辑,因此将导致贫乏的域模型。所有逻辑均由我们的经理在业务层完成,从而形成了更具过程性的编程风格。这不是一个好的面向对象设计,但是也许这不是问题吗?(毕竟,面向对象并不是唯一能带来结果的编程范例。)
- 使用EJB和Java EE会带来一些复杂性。而且,我们不能仅使用Tomcat(添加EJB微容器并不完全是 Tomcat)。
- 使用Servlet的+ JPA存在很多问题。使用Google了解有关这些问题的更多信息。
- 由于退出业务层时事务已关闭,因此我们无法从JPA实体中加载任何信息,这些信息已配置为在需要时
fetch=FetchType.LAZY
从表示层内部(使用)从数据库加载。它将触发异常。在返回包含这些字段的实体之前,我们必须确保调用相关的获取器。另一种选择是使用Java持久性查询语言(JPQL)并执行FETCH JOIN
。但是,这两个选项都比较麻烦。