應IT168寫的專稿:
http://publish.itpub.net/j/2008-01-29/200801291007089.shtml
Spring VS EJB 3 的若干認識誤區(qū)
在開源領域,Spring開源框架已成為企業(yè)應用開發(fā)中使用最多的開源框架。Spring框架的優(yōu)秀不但表現(xiàn)在其優(yōu)雅的底層設計、使用方便快捷、面向工作實踐、超強粘合能力等方面,另外一個不可忽視的方面是Spring擁有一個世界一流且活躍的技術開發(fā)團隊。隨著Spring的流行,于是,大家開始對比Spring與另一種流行的框架組件EJB。目前網上關于Spring和EJB的爭論頗多。很多架構師認為Spring會替代EJB,也有架構師認為Spring是開源的東西,是不成熟且無法和商業(yè)解決方案媲美的框架,因此,在Spring與EJB的對比過程,有若干對Spring的片面認識。而本文希望通過對這些誤區(qū)的分析,給Spring一個原本的認識與理解。
一、 前言
EJB 3.0框架是JCP定義的并且被所有主流J2EE提供商支持的標準框架。EJB 3.0規(guī)范的發(fā)布版本目前已經有開源的和商業(yè)的實現(xiàn),如JBOSS和ORACLE。EJB 3.0大量使用了JAVA注解(Java annotations,是JDK1.5提供的新功能)。
EJB最初的設計思想考慮的是為分布式的應用服務的,分布式是針對大型應用構造的跨平臺的協(xié)作計算,EJB最初的目的就是為這種計算服務的。但是軟件發(fā)展到目前為止,大多數(shù)應用不需要采用分布式的解決方案,因此用EJB顯得太臃腫了。Spring的出現(xiàn)恰恰為了解決這個問題。舉個例子來說,EJB就是導彈,專門設計為打高空飛機。但是現(xiàn)在發(fā)現(xiàn)飛機不多。于是將它用來對付步兵,這個實在太糟糕了。這個時候有人發(fā)明了狙擊步槍(Spring),發(fā)現(xiàn)對付步兵太好用了。
Spring框架是一個廣受歡迎的但是非標準的開源框架。它主要由Interface21公司開發(fā)和控制。Spring框架的體系結構是基于注射依賴(DI)模式。Spring框架使用了大量的XML配置文件,它可以獨立應用,或者在現(xiàn)有的應用服務器上工作。
這兩個框架有著一個共同的核心設計理念:它們的目標是為松耦合的POJO類提供中間件服務。框架通過在運行時截取執(zhí)行環(huán)境,或將服務對象注射給POJO類的方式,將應用服務和POJO類“連接”起來。POJO類本身并不關注如何“連接”,而且也很少依賴于框架。
這樣,開發(fā)者可以將注意力集中在業(yè)務邏輯上,可以對他們的POJO類進行與框架無關的單元測試。并且,由于POJO類不需要繼承框架的類或實現(xiàn)框架提供的接口,開發(fā)者可以在更加靈活性的基礎上構建繼承體系,和搭建應用。
盡管有著共同的理念,但這兩個框架采取了不同的方式來提供POJO服務。由于已經出了大量的比較Spring和EJB3.0的文章。但發(fā)現(xiàn),隨著Spring的發(fā)展,其中對Spring的認識難免有失偏頗的地方,因此,本文將考察它們之間幾個關鍵的認識上的誤區(qū)進行分析。
二、 Spring的XML VS EJB的注釋
從應用開發(fā)者的角度來看,Spring的編程接口主要基于XML配置文件,而EJB 3.0則大量的使用了JAVA注解。XML文件可以表達復雜的關系,但是它們更加冗長而且不健壯。注解的方式很簡單明了,但是很難去表達復雜的或者繼承性的結構。
由于EJB 3.0和Spring相互學習了很多特性,所以,它們都在某種層次上支持XML和注釋。例如,EJB 3.0中可以應用XML配置文件作為一個選擇性的機制,用來改變注釋的默認行為。注釋也可以用來配置一些Spring服務。
在Spring 2中,采用了@PersistenceContext注釋的方式來整合JPA(Java 持久性 API),從而實現(xiàn)了EntityManager對象的注入,同時通過@Transactional實現(xiàn)聲明式事務管理。Spring 2利用注釋來支持AspectJ(一種面向切面的框架,它擴展了Java語言),如@Aspect、@Before、@After、@Around等等注釋。Spring 2中,使用@Repository注釋,可以直接操作JPA或Hibernate API,而不需要使用Spring模板。
Spring的元數(shù)據(jù)模型非常的靈活的,因此在Spring中可以快速的建立起基于注釋的元數(shù)據(jù)模型。而從最近發(fā)布的Spring 2.5看來,事實上也是這樣。Spring 2.5全面支持JSR-250注釋(JSR-250技術規(guī)范主要涉及J2SE和J2EE平臺上開發(fā)普通語義概念的標注,提供一種獨立技術),如@Resource、@PostConstruct、@PreDestroy、@WebServiceRef及@EJB。
特別值得一提的是@Resource,最早是為了在EJB 3使用Spring的依賴注入功能。但如今其功能已經擴展了,不但支持像JNDI的查找,還可以注入任何的Spring管理對象。這就把Spring的優(yōu)勢(Spring支持任何對象的依賴注入)和EJB的優(yōu)勢(使用注釋代替XML)充分的結合起來了。
Spring 2.5中提供了完整的基于注釋的依賴注入模型,如@Autowired及@Qualifier注釋。用戶通過@Autowired注解來對Bean的屬性變量、屬性Setter方法以及構造函數(shù)進行標注,配合AutowiredAnnotationBeanPostProcessor完成對Bean的自動裝配。
@Component注釋為用戶自定義原形進行了擴展。Spring可以自動的檢測到注釋的組件。例如下面的代碼
<context:component-scan base-package="org.springframework.samples.petclinic.web" />
Web控制器不需要額外的XML配置,因為使用了基于注釋的依賴注入以及基于注釋的請求映射。Web控制器通過如下的代碼進行管理:
@Controller
public class ClinicController {
private final Clinic clinic;
@Autowired
public ClinicController(Clinic clinic) {
this.clinic = clinic;
}
…
三、 EJB使用JPA,Spring使用Hibernate
很多人都認為,在EJB 3中通過使用@PersistenceContext注釋提供的entityManager對象來獲得JPA的數(shù)據(jù)訪問,而在Spring中,通過對SessionFactory對象的注入獲得Hibernate數(shù)據(jù)訪問。從而自然而然地認中,EJB使用JPA來操作數(shù)據(jù)對象,而Spring使用Hibernate來操作數(shù)據(jù)對象。
作為EJB3.0的一部分,JPA是一個好東西。其簡單的配置方式及強大的默認配置支持,使其可以輕松自由的存在于輕量與重量之間。事實上,Spring同樣支持使用JPA來操作數(shù)據(jù)對象(例如JpaTemplate),此外Spring提供了@PersistenceContext注釋來支持JPA。在輕量級 Spring 框架的第二代中添加了一大批特性,即使是新的服務器應用程序開發(fā)人員也能夠輕松上手。其關鍵增強之一就是 Spring 2 與JPA的集成。@PersistenceContext注釋的使用示例如下面的代碼所示:
package quickstart.service;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.transaction.annotation.Transactional;
import quickstart.model.Person;
@Transactional
public class PersonServiceImpl implements PersonService {
private EntityManager em;
@PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}
@SuppressWarnings("unchecked")
public List<Person> findAll() {
Query query = getEntityManager().createQuery("select p FROM Person p");
return query.getResultList();
}
public void save(Person person) {
if (person.getId() == null) {
// new
em.persist(person);
} else {
// update
em.merge(person);
}
}
public void remove(int id) {
Person person = find(id);
if (person != null) {
em.remove(person);
}
}
private EntityManager getEntityManager() {
return em;
}
public Person find(int id) {
return em.find(Person.class, id);
}
}
@PersistenceContext會讓Spring在實例化的時候給服務注入一個EntityManager。@PersistenceContext注解可以放在實例變量,或者setter方法前面。如果一個類被注解為@Transactional,Spring將會確保類的方法在運行在一個事務中。
當 Spring JPA 應用程序在 Tomcat 上運行時,要讓 JPA 支持正常工作,需要在類裝入期間進行字節(jié)碼“連接”。來自 Tomcat 的標準類裝入器不支持這個,需要用特定于 Spring 的類裝入器實現(xiàn)這個功能。要把這個特定于 Spring 的類裝入器安裝到 Tomcat 服務器,首先要把 spring-tomcat-weaver.jar 拷貝到 Tomcat 的 server/lib 子目錄。這個目錄包含的庫屬于 Tomcat 服務器私有,可以在 Spring 2 下載的 dist/weaver 目錄下找到 spring-tomcat-weaver.jar 庫。
四、 提供商無關性
開發(fā)者選擇JAVA平臺的一個最重要的原因就是它的提供廠商無關性。EJB 3.0是一個被設計為對提供商沒有依賴性的開放的標準。EJB 3.0規(guī)范由企業(yè)JAVA社區(qū)的主流開源組織和廠商共同編寫和支持的。EJB 3.0框架使開發(fā)者的應用程序實現(xiàn)可以獨立于應用服務器。
比如,JBoss的EJB 3.0的實現(xiàn)是基于Hibernate的,Oracle的EJB 3.0實現(xiàn)是基于TopLink的,但是,在JBoss或者Oracle上跑應用程序,開發(fā)者既不需要去學習Hibernate,也不需要學習TopLink提供的獨特API。廠商無關性使EJB 3.0框架區(qū)別于當前其他任何的POJO中間件框架。
很多人認為,盡管在任何應用服務器都上可以使用Spring框架,但基于Spring的應用仍然被限制于Spring本身,以及在應用中使用到的Spring提供的各種特別服務。但事實上是不是如此呢?大家應該知道,Spring的應用程序中,JtaTransactionManager使用了自動檢測機制,不管是MBeans應用服務器還是Tomcat應用服務器。同理,當使用JPA時,Spring自動檢測persistence.xml文件,并且創(chuàng)建EntityManagerFactory對象。在上面這些機制中,Spring不管是采用注釋(如PersistenceContext、@Transactional、@Resource等等)還是采用XML(如”jee:indi-lookup”等等),都可以像EJB應用一樣的與應用服務器提供商無關。
五、 小結
筆者認為,EJB和Spring設計的角度根本不同,就目前來看,還不能說哪一個能完全打倒另外一個。首先EJB最初的設計思想考慮的是為分布式的應用服務的。就因為這個原因,使得開發(fā)一個EJB不難,但是開發(fā)一個好的EJB卻非常難。此外對于中小型的應用項目而言,基本不采用分布式的解決方案,那么為什么要采取一個為分布式設計的方案來解決非分布式的問題呢? Spring就是為了解決這個問題而誕生的。
本文中,筆者希望比較客戶的評價Spring相對EJB所具有的一些特性,同時,Spring可以與EJB進行協(xié)同的工作,Spring可以應用到EJB應用中去,同樣,EJB可以在Spring應用中很好的使用。同時,Spring如今有力的支持注釋:@Resource、@PersistenceContext、@PostConstruct、@PreDestroy、@EJB及@WebServiceRef。當然,Spring 中不只是能使用Hibernate這樣的ORM框架,同樣可以使用JPA。更妙的是,Spring越來越與應用服務器提供商無關了,很容易實現(xiàn)在不同的應用服務中進行移植。