<tr valign="top"><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr>
级别: 中级
Roland Barcia (barcia@us.ibm.com ), IT 咨询专家, IBM WebSphere 软件服务部
2007 年 11 月 01 日
概要介绍新的 Java™ EE 5 规范、它的许多重要增强功能以及它对 WebSphere® 应用程序的意义。
引言
纵 观 Java Platform, Enterprise Edition (Java EE) 规范的历史可以看出,每次重大修订都是由一个重要主题推动的。例如,第一次发布 J2EE™ 1.2 时,伴随的重要主题是首次将单独的规范绑定在一起,后来,在 J2EE 1.4 中,关注的重要主题则是 Web 服务。图 1 显示了 Java EE 的摘要历史,列出了每个版本的重要功能以及促成每次修订的一些重要外部影响。
图 1. Java EE 的历史
与一些新技术的大多数早期版本一样,Java EE 规范的以前版本中存在一些“难点”,其中包括:
业务逻辑编程的复杂性。
持久性编程模型的复杂性和性能。
表示层/逻辑混合。
Web 服务的类型、复杂性、文档模型、扩展和性能。
多成员团队开发。
漫长的编辑-编译-调试周期。
这时,毫无疑问,Java EE 5 规范 的主题就是简化,这一目标已通过改善以下领域的开发体验得到实现:
简化业务逻辑开发。
简化测试和依赖关系管理。
简化 O/R 持久性。
增强 Web 服务编程模型。
Java EE 5 中的许多升级都受到商业和开放源代码领域创新技术的影响,例如 Hibernate、Spring、Service Data Object (SDO) 以及其他技术。另外,还预期对规范的级别进行升级,做一些小幅度的改进。
为 您使用 Java EE 5 做准备,本文重点介绍新规范的一些主要功能,如 EJB 3.0、Java Persistance Architecture (JPA)、Web 服务、JAX-WS 及其他一些内容,然后简单地介绍我们对 Java EE 6 的一些可能的预期。
EJB 3.0
对于 Java EE 5 中的所有技术增强而言,Enterprise JavaBean™ (EJB) 3.0 是最为显著的,已对它的外观进行了很大的更改,明显简化了开发。EJB 3.0 规范 已拆分为三个子规范:
EJB 3.0 简化 API: 定义用于编码 EJB 组件(特别是会话 Bean 和消息驱动的 Bean)的新简化的 API。
核心契约和要求: 定义 Bean 和 EJB 容器之间的 EJB 契约。
Java 持久性体系结构 API: 为持久性定义新实体 Bean 模型。
下一部分描述 EJB 3.0 和 JPA API 的更新。
EJB 3.0 简化
POJO (传统 Java 对象)是最近经常提到的一个术语,它是指编写为简单 Java 类的代码。由于 EJB 2.x 程序要求您扩展特定的类、提供多个接口并编写部署描述符,因此它们被视为“重载”的 Java 对象,而不再是简单的对象;而且,还需要 J2EE 容器来运行和测试它们。EJB 3.0 的更改包括:
EJB 组件不再要求主接口。 另外,不再需要 EJB 组件提供不同的接口或扩展任何特定于 EJB 的类。
J2SE 5.0 标注现在是实现 EJB 3.0 组件的一个主要辅助方法。 通过指定特殊的标注,开发人员可以创建 EJB 组件的 POJO 类,并将其作为 XML 的备选方案。
EJB 3.0 引入了业务接口概念,而非单独的远程和本地接口。 下面给出了它的一个示例:清单 1
public interface Stock { public double getQuote(String symbol); }
您的 Bean 类可以实现以下接口:清单 2
@Stateless public class StockBean implements Stock public double getQuote(String symbol) { return 100.33; } }
在上面的示例中,@Stateless 标注意味着此类现在是一个无状态的会话 Bean,并且使用业务接口来调用它。还可以在不实现特定接口的情况下对无状态会话 Bean 进行编码,而且可以通过容器生成一个:清单 3
@Stateless public class StockBean public double getQuote(String symbol) { return 100.33; } }
缺省情况下,除非特别标注,否则,所有公共方法都包括在业务接口中。例如,如果使用 @BusinessMethod 至少指定一个方法,则只有使用 @BusinessMethod 指定的方法才包括在业务接口中:清单 4
@Stateless public class StockBean @BusinessMethod public double getQuote(String symbol) { return 100.33; } }
现在已经创建了一个接口,那么如何指定一个方法是远程的还是本地的?当然,可以使用一个标注:清单 5
@Stateless public class StockBean @Remote public double getQuote(String symbol) { return 100.33; } }
您可以标注业务接口或者 Bean 类本身。当选择生成业务接口时,在 Bean 类上启用标注非常有用。
EJB 3.0 规范的更新包括以下内容:
容器服务
回调
拦截器
依赖项注入
容器服务
EJB 组件由于隐式支持事务管理和安全性,因此非常受欢迎。EJB 3.0 规范使用标注(和 XML)来应用容器服务。下面给出了一个示例,介绍如何在无状态会话 Bean 上指定事务属性。 清单 6
@Stateless public class StockBean { @TransactionAttribute(TransactionAttributeType.REQUIRESNEW) public double getQuote(String symbol) { return 100.33; } }
此标注意味着,该方法将在新事务中运行。请参见规范 ,了解不同标注的具体语法和语义,但相同事务和安全功能都有标注。还可以使用 XML 部署描述符应用容器服务,而XML 部署描述符可以在部署时覆盖启用灵活性的标注。
回调
回 调是什么情况?在 EJB 3.0 之前,必须在 Bean 类上实现回调方法,如 ejbCreate();Bean 类必须实现所有方法,无论是否使用它们。在大多数情况下,这些方法实现是空的。现在还通过标注并使用回调方法或回调侦听器类处理回调。下面是一个示例,说 明如何编写代码来使用回调方法响应回调: 清单 7
@Stateless public class StockBean implements Stock public double getQuote(String symbol) { return 100.33; } @PostConstruct initializeCache() { } }
以上代码可让您在创建 Bean 实例之后实现代码。如果希望使用回调侦听器,则可以创建回调侦听器类: 清单 8
public class MyCallbackListener { @PrePassivate public clearCache(Object obj) { Stock stock = (Stock) obj; //perform logic } }
不属于 Bean 类的回调类必须获取 java.lang.Object 参数。容器然后传递 Bean 实例。Bean 类通过使用特殊回调标注在 Bean 类级别添加回调侦听器类: 清单 9
@CallbackListener MyCallbackListener @Stateless public class StockBean implements Stock public double getQuote(String symbol) { return 100.33; } }
回调方法比较好,因为在代码中包括回调方法是有条件的,这与实现接口不同。
拦截器
EJB 规范另一增强功能是使用拦截器。EJB 组件以前缺少的功能是无法对预处理/后处理和横切关注点(与 Servlet 筛选器对 Servlet 的作用类似)等内容执行面向方面的开发 (AOP)。现在可以开发一个拦截器类并将其应用到 Bean。下面是一个审核 StockBean 类调用的拦截器类的示例: 清单 10
public class StockRequestAudit { @AroundInvoke public Object auditStockOperation(InvocationContext inv) throws Exception { try { Object result = inv.proceed(); Auditor.audit(inv.getMethod().getName(), inv.getParameters[0]); return result; } catch (Exception ex) { Auditor.auditFailure(ex); throw ex; } } }
上 面的拦截器拦截对目标 EJB 方法的调用,然后调用 InvocationContext 上的 proceed() 方法。这可让该调用方法流经实际被调用的 EJB 方法。在返回目标 EJB 方法之后,它使用 InvocationTarget 中的元数据来获取被调用的目标 EJB 组件的方法名和参数。然后可以将拦截器应用到 Bean 类: 清单 11
@Stateless @Interceptors({StockRequestAudit}) public class StockBean implements Stock public double getQuote(String symbol) { return 100.33; } }
另 外,还可以开发一个在 Bean 类内部实现的拦截器方法,而且可以指定多个拦截器,在指定多个拦截器时,它们的调用顺序由其在 Bean 类中定义的顺序指定。还可以使用 XML 在 Bean 以外应用拦截器,这在 AOP 中为首选方法,因为您希望向 Bean 透明地应用横切关注点。
依赖项注入
依 赖于数据源之类的 EJB 代码依赖项和 EJB 客户端调用 EJB 组件的方式难以进行 EJB 开发测试。为解决此问题,EJB 3.0 规范引入了依赖项注入机制。EJB 没有使用 JNDI 查找,而是通过注入代码定义一个资源引用。下面是 EJB Bean 的一个示例,此 EJB Bean 需要调用另一个 EJB 组件并使用数据源执行 JDBC 工作: 清单 12
@Stateless public class StockBean implements Stock { @EJB(name="MarketBean", businessInterface="Market") Market market; @Resource(name="StockDB, resourceType="javax.sql.DataSource") DataSource stockDS public double getQuote(String symbol) { Connection con = stockDS.getConnection(); //DO JDBC work return market.getCurrentPrice(symbol); } }
依赖项注入可以通过多种方式进行,例如通过 setter 方法或类变量。有关详细信息,请参见规范 。
Java 持久性体系结构 (JPA)
EJB 持久性的规范已发生了显著变化。该规范称为容器管理的持久性 (CMP),它没有定义映射层,而是由容器映射;因此它由供应商实现映射。受多个商业和开源产品和技术(如 Hibernate、Java Data Objects (JDO) 和 TopLink)的影响,EJB 3.0 引入了新的持久性样式,即基于 POJO 的持久性:
在成功模式上建模。
简化 JDBC 访问模式。
可以与 Web 服务集成。
不受容器的阻碍;可以在 Java EE 或 Java SE 环境中使用 JPA。
标准化 O/R 映射元数据。
支持自顶向下、中间相遇和自底向上的开发。
支持断接和连接的对象状态,省去了对单独的数据传输对象的需要。图 2 显示了一个示例。
图 2. 支持对象状态
JPA API 的更新包括以下内容:
类型:实体和表
实例:Java 对象
属性:Java 属性和列标注
依赖对象:嵌入式 Java 对象
派生属性:瞬态标注
键属性:标注的字段和键类
关系:标注和联合列
约束:标注和数据库
继承:标注——单个表、联合表和按类表
类型:实体和表
JPA 的类型是实体(不再称为实体 Bean),这些实体可以映射到表。映射的主要机制是通过标注实体类。下面是客户 Java 对象和 CUSTOMER 表之间的一个映射示例:清单 13
@Entity @Table(name="CUSTOMER") public class Customer implements Serializable { ...
类被标注为一个实体并使用客户标注。部署描述符可用作备用或覆盖机制。
实例:Java 对象
应 用程序在运行时与 Java 对象交互。通过使用称为实体管理器的特殊对象,应用程序可以查询或保持对象。使用 EJB 容器中的依赖项注入将实体管理器注入到应用程序(在 Java SE 环境中也可以通过 EntityManagerFactory 查找)。下面是一个示例:清单 14
@PersistenceContext (unitName="db2") private EntityManager em;
应用程序可以检索对象或者将它们传递到实体管理器。下面是使用实体管理器通过主键查找对象的应用程序示例:清单 15
Customer customer = (Customer)em.find(Customer.class,customerId);
下面是另一个示例,说明创建 Java 对象,并通过将其传递到实体管理器而保存到数据库:清单 16
CustomerOrder newOrder = new CustomerOrder(); newOrder.setStatus("OPEN"); newOrder.setTotal(new Integer(0)); newOrder.setCustomerId(customerId); em.persist(newOrder);
属性:Java 属性和列标注
属性是类中的 Java 属性。Java 属性可以通过 @column 标注映射到数据库列。共有两种属性映射形式:字段或属性(缺省):清单 17
@Entity(access=FIELD) @Table(name="PRODUCT") public class Product implements Serializable { @Id @Column(name="SKU") Integer sku; @Column(name="DESC") String description; @Column(name="PRICE") Integer cost;
依赖对象:嵌入式 Java 对象
JPA 支持依赖对象。可以创建一个特殊的可嵌入对象,可以通过使用 @Embeddable 标注类对其进行定义:清单 18
@Embeddable public class CustomerAddress { private int streetAddress; private String city; private String state; private String country; ... }
然后可以将该对象定义为实体类上的一个字段:清单 19
@Entity @Table(name="CUSTOMER") public class Customer { private String name; private CustomerAddress ca; @Embedded @AttributeOverrides({ @AttributeOverride(name="streetAddress", column=@Column(" STRT_ADD")), @AttributeOverride(name="city", column=@Column("CITY")) ... //more }) public CustomerAddress getCustomerAddress() { ... }
使用特殊的属性覆盖,可以将可嵌入类的字段映射到实体中。备用方法是直接将列映射到可嵌入类。
派生属性:瞬态标注
缺省情况下,所有字段在 JPA 中都是持久性的;但是,您可以将一个字段标注为瞬态的,然后可以使用逻辑派生任何字段。下面是执行派生字段的查询:清单 20
@Transient public Integer getTotal() { Query totalLineItems = em.createNamedQuery("getOrderTotal"); totalLineItems.setParameter("orderId",orderId); Integer result = (Integer)totalLineItems.getSingleResult(); return result; }
键属性:标注的字段和键类
JPA 支持多种主键以及各种键的生成。在下面的简单示例中,您可以使用 @Id 标注将实体上的字段标注为主键:清单 21
@Entity @Table(name="CUSTOMER") public class Customer implements Serializable { private Integer id; private String name; private CustomerOrder currentOrder; @Id @Column(name="CUST_ID") public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } ...
还可以为具有组合键的实体创建一个主键类:清单 22
public class LineItemId implements Serializable { private Integer orderId; private Integer productId; public LineItemId() { super(); // TODO Auto-generated constructor stub } @Column(name="ORDER_ID") public Integer getOrderId() { return orderId; } public void setOrderId(Integer orderId) { this.orderId = orderId; } @Column(name="PRODUCT_ID") public Integer getProductId() { return productId; } public void setProductId(Integer productId) { this.productId = productId; } public boolean equals(Object arg0) { if (arg0 == this) return true; if (!(arg0 instanceof LineItemId)) return false; LineItemId other = (LineItemId)arg0; if(other.getOrderId().equals(orderId) && other.getProductId().equals(productId)) { return true; } return false; } public int hashCode() { return orderId + productId.hashCode(); } }
然后可以使用 @IdClass 标注在实体上定义组合键:清单 23
@Entity @Table(name="LINEITEM") @IdClass(LineItemId.class) public class LineItem implements Serializable {
您的实体类必须在类上具有匹配字段,或者它可以作为可嵌入键嵌入该键:清单 24
@Entity @Table(name="LINEITEM") @IdClass(LineItemId.class) public class LineItem implements Serializable { private LineItemId lineItemId; @EmbeddedId public LineItemId getLineItemId() { return lineItemId; } ...
另一个主要增强是支持生成主键。通过使用 @Id 标注的生成属性,可以选择一个不同的策略。例如,您可以选择向 DB2 标识列委派主键生成,如下所示:清单 25
@Id(generate=GeneratorType.IDENTITY) @Column(name="ORDER_ID") public Integer getOrderId() { return orderId; } public void setOrderId(Integer orderId) { this.orderId = orderId; }
其他受支持机制包括序列和表生成。
关系:标注和联合列
JPA 对实体之间的关系提供强大的支持。JPA 支持一对一、一对多、多对一和多对多关系。在 JPA 中,关系不是双向的,这与 EJB 2.x 中相同。相反,对象将其他实体声明为成员,并添加标注来定义关系。可以使用特殊属性将关系转换为双向的。下面是具有两个不同关系的 CustomerOrder 类的示例: 清单 26
@Entity @Table(name="CUSTOMER") public class Customer implements Serializable { private Integer id; private String name; private CustomerOrder currentOrder; @Id @Column(name="CUST_ID") public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="NAME") public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(cascade=CascadeType.ALL , fetch=FetchType.EAGER ) @JoinColumn(name="OPEN_ORDER_ID",referencedColumnName="ORDER_ID") public CustomerOrder getCurrentOrder() { return currentOrder; } public void setCurrentOrder(CustomerOrder currentOrder) { this.currentOrder = currentOrder; } }
在 此关系中,通过在 Customer 类中标注 CustomerOrder 属性,将 Customer 与 CustomerOrder 定义为一对一关系。还定义了 JoinColumn 信息。名称属性在 Customer 类映射到的表上定义外键,参考列定义 Table CustomerOrder 映射到的主键。对关系的任何约束都定义为 @OneToOne 标注的属性。CustomerOrder 类如下所示。CustomerOrder 定义了一个 Customer 属性;不过 CustomerOrder 对象有一个返回到 Customer 的一对多关系。这是因为 Customer 只能有一个当前顺序;实际上,一个客户可以有多个顺序,因此 CustomerOrder 显示了这一点。不过,此处的 mappedBy 属性用于定义由 Customer 类将 CustomerOrder 映射到关系的另一侧。此类如下所示: 清单 27
public class CustomerOrder implements Serializable { private Integer orderId; private String status; private Integer total; private Integer customerId; private Collection<LineItem> lineItems; private Customer customer; @Column(name="CUSTOMER_ID") public Integer getCustomerId() { return customerId; } public void setCustomerId(Integer customerId) { this.customerId = customerId; } @Id(generate=GeneratorType.IDENTITY) @Column(name="ORDER_ID") public Integer getOrderId() { return orderId; } public void setOrderId(Integer orderId) { this.orderId = orderId; } @Column(name="STATUS") public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } @Column(name="TOTAL") public Integer getTotal() { return total; } public void setTotal(Integer total) { this.total = total; } @ManyToOne(fetch=FetchType.EAGER,optional=false) @JoinColumn(name="CUSTOMER_ID", referencedColumnName="CUST_ID",insertable=false,updatable=false, nullable=false,unique=true) public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } @OneToMany(mappedBy="customerOrder", cascade=CascadeType.ALL , fetch=FetchType.EAGER) public Collection<LineItem> getLineItems() { return lineItems; } public void setLineItems(Collection<LineItem> lineItems) { this.lineItems = lineItems; } }
CustomerOrder 中的另一关系包含 LineItem 对象的集合。这是一个一对多关系。注意,JRE 5 定义泛型的使用,指定集合的类型。此处还使用了特殊的 mappedBy 属性,使关系的另一侧映射成为双向关系。 清单 28
@Entity @Table(name="LINEITEM") @IdClass(LineItemId.class) public class LineItem implements Serializable { private Product product; private Integer orderId; private Integer productId; private Integer quantity; private Integer total; private CustomerOrder customerOrder; @Column(name="QUANTITY") public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { this.quantity = quantity; } @Column(name="AMOUNT") public Integer getTotal() { return total; } public void setTotal(Integer total) { this.total = total; } @ManyToOne(fetch=FetchType.EAGER,optional=false) @JoinColumn(name="PRODUCT_ID", referencedColumnName="SKU",insertable=false,updatable=false, nullable=false,unique=true) public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } @Column(name="ORDER_ID") public Integer getOrderId() { return orderId; } public void setOrderId(Integer orderId) { this.orderId = orderId; } @Column(name="PRODUCT_ID") public Integer getProductId() { return productId; } public void setProductId(Integer productId) { this.productId = productId; } @ManyToOne(fetch=FetchType.EAGER,optional=false) @JoinColumn(name="ORDER_ID", referencedColumnName="ORDER_ID",insertable=false,updatable=false, nullable=false,unique=true) public CustomerOrder getCustomerOrder() { return customerOrder; } public void setCustomerOrder(CustomerOrder customerOrder) { this.customerOrder = customerOrder; } }
LineItem 类有一个 CustomerOrder 属性。这里定义了一个多对一关系,如上面所示。类似地,LineItem 类与产品对象也具有多对一关系。
另一映射类型是,表可能有一对一关系,但对象模型只有一个对象。换句话说,您希望在跨多个表映射单一对象(在某种程度上与依赖对象相反)。可以通过添加一个或多个辅助表做到这一点。下面是跨 Customer 和 Order 表映射客户对象的一个示例。 清单 29
@Entity @Table(name="CUSTOMER") @SecondaryTable(name="ORDER ", pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID")) public class Customer { ... }
约束:标注和数据库
JPA 除支持各种数据库约束外,还可以使用它定义对各种关系的约束: 清单 30
@OneToMany(mappedBy="customerOrder", cascade=CascadeType.ALL , fetch=FetchType.EAGER) public Collection<LineItem> getLineItems() { return lineItems; } public void setLineItems(Collection<LineItem> lineItems) { this.lineItems = lineItems; }
在本例中显示了级联影响。例如,如果删除了客户,也将删除客户的顺序。下面是一个具有更多约束的示例: 清单 31
@ManyToOne(fetch=FetchType.EAGER,optional=false) @JoinColumn(name="CUSTOMER_ID", referencedColumnName="CUST_ID", insertable=false,updatable=false, nullable=false,unique=true) public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; }
在上例中,跨外键(也是主键的一部分)定义了多对一关系。在本例中,不允许任何人更新或插入同样标记为唯一和不得为空的列。此关系也指定为不可选。JPA 还可让您在特定实体上定义唯一约束,如下所示: 清单 32
@Entity @Table( name="EMPLOYEE", uniqueConstraints= {@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})} )
继承:标注——单个表、联合表和按类表
JPA 规范定义了对映射继承的三种类型的支持;其中只有一种在当前规范中是强制的:
单个表: 对象树映射到一个表。
联合表: (可选)将子类映射到与父类映射到的表具有外键关系的表。
按类表: (可选)每个具体的子类映射到一个表,并且包含超类属性的列。
下面是一个使用单表策略映射超类和子类的示例: 清单 33
@Entity@Table(name="CUST") @Inheritance(strategy=SINGLE_TABLE, discriminatorType=STRING, discriminatorValue="CUST") public class Customer { ... } @Entity@Inheritance(discriminatorValue="VCUST") public class ValuedCustomer extends Customer { ... }
下面是一个使用联合表策略的示例: 清单 34
@Entity@Table(name="CUST") @Inheritance(strategy=JOINED, discriminatorType=STRING, discriminatorValue="CUST") public class Customer { ... } @Entity@Table(name="VCUST") @Inheritance(discriminatorValue="VCUST") @PrimaryKeyJoinColumn(name="CUST_ID") public class ValuedCustomer extends Customer { ... }
请参阅参考资料 ,以了解 JPA 中提供的更多其他功能。
JAX-WS
Java EE 5 还为 Web 服务引入了一个新的编程模型:JAX-WS。在了解不同之处之前,您需要知道从 JAX-RPC 到 JAX-WS 哪些内容没有发生变化:
JAX-WS 仍然支持 SOAP 1.1 over HTTP 1.1,因此互操作性将不会受到影响。仍然可以在网上传递相同的消息。
JAX-WS 仍然支持 WSDL 1.1,因此您所学到的有关该规范的知识仍然有用。新的 WSDL 2.0 规范已经接近完成,但在 JAX-WS 2.0 相关工作结束时其工作仍在进行中。
系列文章 Web 服务提示与技巧:JAX-RPC 与 JAX-WS 的比较 包含关于 JAX-WS 的详细信息,下面大致列出了从 JAX-RPC 1.1 到 JAX-WS 2.0 都发生了哪些更改:
SOAP 1.2: JAX-RPC 和 JAX-WS 都支持 SOAP 1.1。JAX-WS 还支持 SOAP 1.2。
XML/HTTP: WSDL 1.1 规范在 HTTP 绑定中定义,这意味着利用此规范可以在不使用 SOAP 的情况下通过 HTTP 发送 XML 消息。JAX-RPC 忽略了 HTTP 绑定。而 JAX-WS 添加了对其的支持。
WS-I Basic Profile: JAX-RPC 支持 WS-I Basic Profile (BP) V1.0。JAX-WS 支持 BP 1.1。(WS-I 即 Web 服务互操作性组织。)
新的 Java 功能: JAX-RPC 映射到 Java 1.4。JAX-WS 映射到 Java 5.0。JAX-WS 依赖于 Java 5.0 中的很多新功能。(Java EE 5 是 J2EE 1.4 的后续版本,添加了对 JAX-WS 的支持,但仍然支持 JAX-RPC,这可能会对 Web 服务新手造成混淆。)
数据映射模型: JAX-RPC 具有自己的映射模型,此模型大约涵盖了所有模式类型中的 90%。那些没有包括的模式被映射到 javax.xml.soap.SOAPElement(JAX-WS 数据映射模型是 JAXB,该模型映射所有 XML 模式)。
接口映射模型: JAX-WS 的基本接口映射模型与 JAX-RPC 的区别并不大,不过 JAX-WS 模型使用新的 Java 5.0 功能,并引入了异步功能。
动态编程模型: JAX-WS 的动态客户端模式与 JAX-RPC 模式差别很大。根据业界的需要进行了许多更改,其中包括面向消息的功能和动态异步功能。JAX-WS 还添加了动态服务器模型,而 JAX-RPC 则没有此模型。
消息传输优化机制 (MTOM): JAX-WS 通过 JAXB 添加了对新附件规范 MTOM 的支持,因此应该能够实现附件互操作性。
处理程序模型: 从 JAX-RPC 到 JAX-WS 的过程中,处理程序模型发生了很大的变化。JAX-RPC 处理程序依赖于 SAAJ 1.2,而 JAX-WS 处理程序则依赖于新的 SAAJ 1.3 规范。
JAX-WS 还可以与 EJB 3.0 一起使用来简化编程模型。例如,下面的代码显示了将 EJB 3.0 POJO 转换为 Web 服务的简单方法:
清单 35
@WebService public interface StockQuote { public float getQuote(String sym); } @Stateless public class QuoteBean implements StockQuote { public float getQuote(String sym) { ... } }
还添加了其他标注以便支持 Web 服务中更高级的功能。JAX-B 提供了 POJO(清单 36)和 XML 模式(清单 37)之间的标准映射,下面给出了它的一个示例:
清单 36
@XmlType public class Trade { @XmlElement(name="tickerSymbol") public String symbol; @XmlAttribute int getQuantity() {...} void setQuantity() {...} }
清单 37
<xs:complexType name="trade"> <xs:sequence> <xs:element name="tickerSymbol" type="xs:string"/> </xs:sequence> <xs:attribute name="quantity" type="xs:int"/> </xs:complexType>
JavaServer Faces
JavaServer™ Faces (JSF) 到目前为止已有数年的历史,并且受大多数 Java EE 应用服务器支持,例如 IBM® WebSphere Application Server。在 Java EE 5 中,JSF 现在是 Java EE 5 规范的一部分。JSF 为 Java EE 应用程序提供了许多好处:
丰富、可扩展的 UI 组件。
事件驱动。
托管组件状态。
呈现器/客户端独立性。
验证程序。
类型转换。
外部化导航。
JavaServer Pages 和 Faces 通用表达语言。
展望 Java EE 6
最近对 JSR 316 (Java EE 6) 提出了一些议案,尽管对其规范定义为时尚早,但该议案突出了以下几个重要主题:
可扩展性: 通过添加更多扩展点和更多服务提供程序接口,可以将其他技术简洁高效地插入平台实现,从而实现了可增长性。
概要: 概 要将根据 JCP 过程的定义参考 Java EE 平台,并且可能包括 Java EE 平台技术的子集或/和不属于基本 Java EE 平台的其他 JCP 技术。专家组还将定义 Java EE Web 概要的第一个版本,这是一个用于 Web 应用程序开发的 Java EE 平台的子集。
技术修剪: Java EE 平台中的一些技术不再具有它们刚引入平台时的相关性。需要有一种方法,能够认真有序地将这些技术从平台中“修剪”掉,并让仍使用这些技术的开发人员受到的 影响降到最小,同时使平台能够更健壮地成长。正如该过程定义的那样,专家组将考虑在将来的 Java EE 平台规范中应将某些技术标记为可被移除。这些可能被移除的技术包括:
EJB CMP,被 Java Persistence 有效地代替。
JAX-RPC,被 JAX-WS 有效地代替。
SOA 支持: Java EE 平台已经广泛应用于 SOA 应用程序。随着越来越多的企业认识到 SOA 体系结构的好处,对该平台的功能和互操作性的需要也相应提高。Java EE 6 需要考虑增加对 Web 服务的支持。尽管基本 Web 服务支持现在是 Java SE 6 平台的一部分,但此规范将需要这些技术的更新版本,以便提供更多的 Web 服务支持。服务组件体系结构 (SCA) 定义一些可以在 SOA 环境中由组合应用程序使用的工具。专家组在考虑将所有 SCA 定义的工具都包含在 Java EE 6 平台中是否合适。
其他增加内容: 专家组提议在 Java EE 6 中包括下列新的 JSR:
用于容器的 JSR-196 Java 身份验证 SPI。
用于应用服务器的 JSR-236 计时器。
用于应用服务器的 JSR-237 工作管理器。
JSR-299 Web Bean。
JSR-311 JAX-RS:用于 RESTful Web 服务的 Java API。
预计在以下领域进行进一步更新:
Enterprise JavaBeans。
Java Persistence API。
Servlet。
JavaServer Faces。
JAX-WS。
Java EE Connector API。
具体要包括哪些技术将由专家组根据合作伙伴和客户要求确定。其中一些规范还要考虑快速发展的 Web 2.0 空间。
结束语
Java EE 5 是一个功能强大而重要的发行版,是用于企业开发的最完善的版本。显然,已经采取了一些重要步骤解决了围绕以前 Java 开发的一些问题。EJB 3.0 和 JPA 是功能强大而又易用的技术,而且 JAX-WS 中的增强功能使得 Web 服务的开发比以往更加容易。
致谢
作者感谢 Jim Knutson 和 Russell Butek 对本文所做的贡献。
参考资料
学习
获得产品和技术
关于作者
Roland Barcia 是位于纽约/新泽西州 Metro 区 IBM WebSphere 软件服务部 的 IT 咨询专家。他是 IBM WebSphere: Deployment and Advanced Configuration 的合著者。有关 Roland 的详细信息,请访问他的网站 。