OnJava上看到一篇文章,貼在這里,做上筆記。
Albert Einstein once said, "Everything should be made as simple as possible, but not simpler." (哈哈,相對(duì)論再簡(jiǎn)單,也會(huì)令不知多少感到頭痛,包括我。
)Indeed, the pursuit of scientific truth has been all about simplifying a theory's underlying assumptions so that we can deal with the issues that really matter. The same can be said for enterprise software development.
A key to simplifying enterprise software development is to provide an application framework that hides complexities (such as transaction, security, and persistence) away from the developers. A well-designed framework promotes code reuse, enhances developer productivity, and results in better software quality. However, the current EJB 2.1 framework in J2EE 1.4 is widely considered poorly designed and over-complicated(復(fù)雜是對(duì)的,設(shè)計(jì)倒未必差。). Unsatisfied with the EJB 2.1 framework, Java developers have experimented with a variety of other approaches for middleware services delivery. Most noticeably, the following two frameworks have generated a lot of developer interest and positive feedback. They are posed to become the frameworks of choice for future enterprise Java applications.
- The Spring framework is a popular but non-standard open source framework. It is primarily developed by and controlled by Interface21 Inc. The architecture of the Spring framework is based upon the Dependency Injection (DI) design pattern. Spring can work standalone or with existing application servers and makes heavy use of XML configuration files. (為什么就讓Springg趕上春天了?其實(shí)DI在很多地方都用到了,其實(shí),應(yīng)該是說(shuō)Spring 用DI來(lái)解決Factory的依賴性問(wèn)題,這點(diǎn)占得先機(jī)。)
- The EJB 3.0 framework is a standard framework defined by the Java Community Process (JCP) and supported by all major J2EE vendors. Open source and commercial implementations of pre-release EJB 3.0 specifications are already available from JBoss and Oracle. EJB 3.0 makes heavy use of Java annotations.(要是搶標(biāo)準(zhǔn),Jboss顯然不是Oracle的對(duì)手,這不JavaOne之后,Oracle在這上面領(lǐng)先了。這下,Oracle連同它獨(dú)步天下的DB,加上這么一個(gè)持久層?xùn)|西,很是了得。這樣,Oralce今后,會(huì)不會(huì)形成JSF(ADF Faces)+EJB3的格局,在J2EE上來(lái)那么一個(gè)令人吃驚呢?IBM帕不怕?不知道,它和BEA搞的SDO也提到了JCP,不過(guò)不知前景如何?哪天,抽時(shí)間仔細(xì)看看。)
These two frameworks share a common core design philosophy: they both aim to deliver middleware services to loosely coupled plain old Java objects (POJOs). (POJO是個(gè)奇怪的名字,其中第一個(gè)O字母,就令人奇怪。呵呵,誰(shuí)說(shuō)老樹不能開新花?這下要是搞的風(fēng)水輪流轉(zhuǎn),就有好戲看啦。
)The framework "wires" application services to the POJOs by intercepting the execution context or injecting service objects to the POJO at runtime. The POJOs themselves are not concerned about the "wiring" and have little dependency upon the framework. As a result, developers can focus on the business logic, and unit test their POJOs without the framework. In addition, since POJOs do not need to inherit from framework classes or implement framework interfaces, the developers have a high degree of flexibility to build inheritance structures and construct applications.(有點(diǎn)包辦婚姻的感覺,容易就是這些POJO的父母。總是要等到最后,才知道和自己有關(guān)系的東西是誰(shuí)。是誰(shuí)的悲哀?沒(méi)有,但愿皆大歡喜。)
其實(shí),輕量架構(gòu)和敏捷方法的火爆主要來(lái)源于幾個(gè)主要的原因:1,程序員自身的武俠主義者,呵呵,也可說(shuō)英雄主義。總想什么都會(huì),認(rèn)為武功天下第一,就是懂得更多的語(yǔ)言,從需求、分析、設(shè)計(jì)、測(cè)試到部署,無(wú)所不通,多多益善。當(dāng)然這也是生存壓力的后果。
2 SUN曾經(jīng)設(shè)想,J2EE是一個(gè)重量的團(tuán)隊(duì)協(xié)作的開發(fā)方式,可是大大出人意料,這種方式要求很高的軟件工程水平,同時(shí)支撐它的同樣要重量的過(guò)程模型,比如RUP什么的。但是大型的分布式應(yīng)用畢竟是少數(shù),中小企業(yè)的信息化應(yīng)用才是主流。而且,MS的解決方案,從過(guò)程MSF到, .Net和開發(fā)環(huán)境VS,無(wú)一不在生產(chǎn)力方面占得先機(jī)。老大!要記住,用戶數(shù)量最所的才是市場(chǎng)。
3 出于開發(fā)成本考慮,老板總是希望工程師無(wú)所不會(huì),這樣才能減少成本嘛?!才會(huì)出現(xiàn)很多招聘廣告上這樣寫道“精通A,B,C,D,E。。。。,工作經(jīng)驗(yàn)2年”薪水卻只開到2K。這樣的事天天發(fā)生。是覺得可笑嗎?還是覺得悲哀。是覺得人事部門的問(wèn)題,還是該公司的技術(shù)管理著根本就不懂。或者就是老板的問(wèn)題。不,這是市場(chǎng)的問(wèn)題。同一個(gè)軟件,規(guī)模和功能幾乎一樣,在國(guó)內(nèi)賣個(gè)10萬(wàn),很高了,在英國(guó)卻賣到60萬(wàn)英鎊。呵呵,這就是市場(chǎng)。市場(chǎng)就絕對(duì)正確嗎?可是房?jī)r(jià)天天漲,現(xiàn)在畢業(yè)的程序員,看著房?jī)r(jià)怎的不愁?NND.
hehe, 跑題了。。。。。。。。。。。。。。
However, while sharing an overall philosophy, the two frameworks use very different approaches to deliver POJO services. While numerous books and articles have been published to compare either Spring or EJB 3.0 to the old EJB 2.1, no serious study has been done to compare Spring to EJB 3.0. In this article, I will examine some key differences behind the Spring and EJB 3.0 frameworks, and discuss their pros and cons. The topics covered in this article also apply to other lesser-known enterprise middleware frameworks, as they all converge on the "loosely coupled POJO" design. I hope this article will help you choose the best framework for your needs.
Vendor Independence
One of the most compelling reasons for developers to choose the Java platform is its vendor independence. EJB 3.0 is an open standard designed for vendor independence. The EJB 3.0 specification is developed and supported by all major open source and commercial vendors in the enterprise Java community. The EJB 3.0 framework insulates developers from application server implementations. For instance, while JBoss's EJB 3.0 implementation is based on Hibernate, and Oracle's is based on TopLink, developers need to learn neither Hibernate- nor TopLink-specific APIs to get their applications running on JBoss and Oracle. Vendor independence differentiates the EJB 3.0 framework from any other POJO middleware frameworks available today.(其實(shí)這點(diǎn)也是見仁見智了,總之走著瞧。)
However, as many EJB 3.0 critics are quick to point out, the EJB 3.0 specification has not yet reached the final release at the time of this writing. It will likely be another one to two years before EJB 3.0 is widely adopted by all major J2EE vendors. But even if your application server does not yet support EJB 3.0 natively, you can still run EJB 3.0 applications in the server by downloading and installing an "embeddable" EJB 3.0 product. For instance, the JBoss embeddable EJB 3.0 product is open source and runs in any J2SE-5.0-compatible environment (i.e., in any Java application server). It is currently under beta testing. Other vendors may also soon release their own embeddable EJB 3.0 products, especially for the "data persistence" part of the specification.(恐怕還是不好吧?一鍋燴?前些天看Walmart遷移到Java5,真夠大膽的。)
On the other hand, Spring has always been a non-standard technology and will remain that way in the foreseeable future. Although you can use the Spring framework with any application server, Spring applications are locked into both Spring itself and the specific services you choose to integrate in Spring.(其實(shí),標(biāo)準(zhǔn)不標(biāo)準(zhǔn),不是JCP說(shuō)了算的。是大家多了算的。)
- While the Spring framework is an open source project, Spring has a proprietary XML format for configuration files and a proprietary programming interface. Of course, this type of lock-in happens to any non-standard product; it is not specific to Spring. But still, the long-term viability of your Spring application depends on the health of the Spring project itself (or Interface21 Inc., which hires most of Spring's core developers). In addition, if you use any of the Spring-specific services, such as the Spring transaction manager or Spring MVC, you are locked into those APIs as well.
- Spring applications are not agnostic to back-end service providers, either. For instance, for data persistence services, the Spring framework comes with different DAO and template helper classes for JDBC, Hibernate, iBatis, and JDO. So if you need to switch the persistence service provider (e.g., change from JDBC to Hibernate) for a Spring application, you will need to refactor your application code to use the new helper classes.(這點(diǎn)就是我的最愛,Spring對(duì)誰(shuí)都很友好,也許也包括這里的EJB3。)
Service Integration
From a very high level, the Spring framework sits above application servers and service libraries. The service integration code (e.g., data access templates and helper classes) resides in the framework and is exposed to the application developers. In contrast, the EJB 3.0 framework is tightly integrated into the application server and the service integration code is encapsulated behind a standard interface.
As a result, EJB 3.0 vendors can aggressively optimize the overall performance and developer experience. For instance, in JBoss's EJB 3.0 implementation, when you persist an Entity Bean POJO using the EntityManager
, the underlying Hibernate session transaction is automatically tied to the calling method's JTA transaction, and it commits when the JTA transaction commits. Using a simple @PersistenceContext
annotation (see later in this article for an example), you can even tie the EntityManager
and its underlying Hibernate transaction to an application transaction in a stateful session bean. The application transaction spans across multiple threads in a session and it is very useful in transactional web applications, such as multi-page shopping carts. The above simple and integrated programming interface is made possible due to the tight integration between the EJB 3.0 framework, Hibernate, and Tomcat inside of JBoss. A similar level of integration is also archived between Oracle's EJB 3.0 framework and its underlying Toplink persistence service.
Another good example of integrated services in EJB 3.0 is clustering support. If you deploy an EJB 3.0 application in a server cluster, all of the fail-over, load-balancing, distributed cache, and state replication services are automatically available to the application.(這恐怕不是EJB3的原因吧?) The underlying clustering services are hidden behind the EJB 3.0 programming interface and they are completely transparent to EJB 3.0 developers.
In Spring, it is more difficult to optimize the interaction between the framework and the services. For instance, in order to use Spring's declarative transaction service to manage Hibernate transactions, you have to explicitly configure the Spring TransactionManager
and Hibernate SessionFactory
objects in the XML configuration file. Spring application developers must explicitly manage transactions that span across several HTTP requests. In addition, there is no simple way to leverage clustering services in a Spring application.
(其實(shí),通過(guò)annotation 擴(kuò)展了語(yǔ)義,而Spring 確實(shí)用常規(guī)手段。這點(diǎn)出發(fā)點(diǎn)都不一樣。)
Flexibility in Service Assembly
Since the service integration code in Spring is exposed as part of the programming interface, application developers have the flexibility to assemble services as needed. This feature enables you to assemble your own "lightweight" application servers. A common usage of Spring is to glue Tomcat together with Hibernate to support simple database-driven web applications. In this case, Spring itself provides transaction services and Hibernate provides persistence services--this setup creates a mini application server (這個(gè)觀點(diǎn)倒是新鮮)in itself.
EJB 3.0 application servers typically do not give you that kind of flexibility in picking and choosing on the services you need. Most of the time, you get a set of prepackaged features, some of which you might not need. However, if the application server features a modular internal design, as JBoss does, you might be able to take it apart and strip out the unnecessary features. In any case, it is not a trivial exercise to customize a full-blown application server.
Of course, if the application scales beyond a single node, you would need to wire in services (such as resource pooling, message queuing, and clustering) from regular application servers. The Spring solution would be just as "heavyweight" as any EJB 3.0 solution, in terms of the overall resource consumption.
(當(dāng)然,SPring 本身就不是針對(duì)分布式的應(yīng)用需求,也不支持。所以,沒(méi)必要這么比。關(guān)鍵是看針對(duì)那些應(yīng)用。)
In Spring, the flexible service assembly also makes it easy to wire mock objects, (For Testing)instead of real service objects, into the application for out-of-the-container unit testing. In EJB 3.0 applications, most components are simple POJOs, and they can be easily tested outside of the container. But for tests that involve container service objects (e.g., the persistence EntityManager
), in-container tests are recommended, as they are easier, more robust, and more accurate than the mock objects approach.(當(dāng)然,脫不脫離容器的測(cè)試其實(shí)對(duì)大家并不重要,關(guān)鍵是要簡(jiǎn)單,快速!)
XML Versus Annotation
From the application developer's point view, Spring's programming interface is primarily based upon XML configuration files while EJB 3.0 makes extensive use Java annotations. XML files can express complex relationships, but they are also very verbose and less robust. Annotations are simple and concise, but it is hard to express complex or hierarchical structures in annotations.(所以需要簡(jiǎn)化Spring 的XML語(yǔ)法,盡管我已經(jīng)覺得很簡(jiǎn)單了。比起Struts之類的已經(jīng)簡(jiǎn)單不少了。)
Spring and EJB 3.0's choices of XML or annotation depend upon the architecture behind the two frameworks: since annotations can only hold a fairly small amount of configuration information, only a pre-integrated framework (i.e., most of plumbing has been done in the framework) can make extensive use of annotations as its configuration option. As we discussed, EJB 3.0 meets this requirement, while Spring, being a generic DI framework, does not.(誰(shuí)知道,以后Spring 會(huì)不會(huì)也Anotation一下?我覺得很可能會(huì)。)
Of course, as both EJB 3.0 and Spring evolve to learn from each other's best features, they both support XML and annotations to some degree. For instance, XML configuration files are available in EJB 3.0 as an optional overriding mechanism to change the default behavior of annotations. Annotations are also available to configure some Spring services.
The best way to learn the differences between the XML and annotation approaches is through examples. In the next several sections, let's examine how Spring and EJB 3.0 provide key services to applications.
Declarative Services
Spring and EJB 3.0 wire runtime services (such as transaction, security, logging, messaging, and profiling services) to applications. Since those services are not directly related to the application's business logic, they are not managed by the application itself. Instead, the services are transparently applied by the service container (i.e., Spring or EJB 3.0) to the application at runtime. The developer (or administrator) configures the container and tells it exactly how/when to apply services.(AOP的天下)
EJB 3.0 configures declarative services using Java annotations, while Spring uses XML configuration files. In most cases, the EJB 3.0 annotation approach is the simpler and more elegant way for this type of services. Here is an example of applying transaction services to a POJO method in EJB 3.0.
public class Foo {
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public bar () {
// do something ...
}
}
You can also declare multiple attributes for a code segment and apply multiple services. This is an example of applying both transaction and security services to a POJO in EJB 3.0.
@SecurityDomain("other")
public class Foo {
@RolesAllowed({"managers"})
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public bar () {
// do something ...
}
}
Using XML to specify code attributes and configure declarative services could lead to verbose and unstable configuration files. Below is an example of XML elements used to apply a very simple Hibernate transaction to the Foo.bar()
method in a Spring application.
<!-- Setup the transaction interceptor -->
<bean id="foo"
class="org.springframework.transaction
.interceptor.TransactionProxyFactoryBean">
<property name="target">
<bean class="Foo"/>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<ref bean="attributeSource"/>
</property>
</bean>
<!-- Setup the transaction manager for Hibernate -->
<bean id="transactionManager"
class="org.springframework.orm
.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<!-- you need to setup the sessionFactory bean in
yet another XML element -- omitted here -->
<ref bean="sessionFactory"/>
</property>
</bean>
<!-- Specify which methods to apply transaction -->
<bean id="transactionAttributeSource"
class="org.springframework.transaction
.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="bar">
</props>
</property>
</bean>
(EJB的方式的確簡(jiǎn)單,可是如果復(fù)雜度上升,恐怕優(yōu)勢(shì)也不怎么明顯了。)
The XML complexity would grow geometrically if you added more interceptors (e.g., security interceptors) to the same POJO. Realizing the limitations of XML-only configuration files, Spring supports using Apache Commons metadata to specify transaction attributes in the Java source code. In the latest Spring 1.2, JDK-1.5-style annotations are also supported. (對(duì)啦,也許今后是個(gè)方向,學(xué)習(xí)是很重要的!)To use the transaction metadata, you need to change the above transactionAttributeSource
bean to an AttributesTransactionAttributeSource
instance and add additional wirings for the metadata interceptors.
(Spring的一個(gè)問(wèn)題是各種名稱都不肯縮寫,弄的老長(zhǎng)。當(dāng)然,意思更加清楚明了。)
<bean id="autoproxy"
class="org.springframework.aop.framework.autoproxy
.DefaultAdvisorAutoProxyCreator"/>
<bean id="transactionAttributeSource"
class="org.springframework.transaction.interceptor
.AttributesTransactionAttributeSource"
autowire="constructor"/>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor
.TransactionInterceptor"
autowire="byType"/>
<bean id="transactionAdvisor"
class="org.springframework.transaction.interceptor
.TransactionAttributeSourceAdvisor"
autowire="constructor"/>
<bean id="attributes"
class="org.springframework.metadata.commons
.CommonsAttributes"/>
The Spring metadata simplifies the transactionAttributeSource
element when you have many transactional methods. But it does not solve the fundamental problems with XML configuration files--the verbose and fragile transaction interceptor, transactionManager
, and transactionAttributeSource
are all still needed.
Dependency Injection
(這年頭松散耦合這么流行,就像男女關(guān)系之間,一夜情好生流行似的。我們先前說(shuō)的婚姻,恐怕也片面了些。各種滋味和含義,大家自己體會(huì)吧。)
A key benefit of middleware containers is that they enable developers to build loosely coupled applications. The service client only needs to know the service interface. The container instantiates service objects from concrete implementations and make them available to clients. This allows the container to switch between alternative service implementations without changing the interface or the client-side code.
The Dependency Injection pattern is one of best ways to implement loosely coupled applications. It is much easier to use and more elegant than older approaches, such as dependency lookup via JNDI or container callbacks. Using DI, the framework acts as an object factory to build service objects and injects those service objects to application POJOs based on runtime configuration. From the application developer's point of view, the client POJO automatically obtains the correct service object when you need to use it.
Both Spring and EJB 3.0 provide extensive support for the DI pattern. But they also have some profound differences. Spring supports a general-purpose, but complex, DI API based upon XML configuration files; EJB 3.0 supports injecting most common service objects (e.g., EJBs and context objects) and any JNDI objects via simple annotations.
The EJB 3.0 DI annotations are extremely concise and easy to use. The @Resource
tag injects most common service objects and JNDI objects. The following example shows how to inject the server's default DataSource
object from the JNDI into a field variable in a POJO. DefaultDS
is the JNDI name for the DataSource
. The myDb
variable is automatically assigned the correct value before its first use.
(JNDI仍然可以玩,只不過(guò)lookup也由容器代勞了。精彩!!這對(duì)移植更加方便了)
public class FooDao {
@Resource (name="DefaultDS")
DataSource myDb;
// Use myDb to get JDBC connection to the database
}
In addition to direct field variable injection, the @Resource
annotation in EJB 3.0 can also be used to inject objects via a setter method. For instance, the following example injects a session context object. The application never explicitly calls the setter method--it is invoked by the container before any other methods are called.
@Resource
public void setSessionContext (SessionContext ctx) {
sessionCtx = ctx;
}
For more complex service objects, special injection annotations are defined. For instance, the @EJB
annotation is used to inject EJB stubs and the @PersistenceContext
annotation is used to inject EntityManager
objects, which handle database access for EJB 3.0 entity beans. The following example shows how to inject an EntityManager
object into a stateful session bean. The @PersistenceContext
annotation's type
attribute specifies that the injected EntityManager
has an extended transaction context--it does not automatically commit with the JTA transaction manager, and hence it can be used in an application transaction that spans across multiple threads in a session.
(這里才是EJB的關(guān)鍵。)
@Stateful
public class FooBean implements Foo, Serializable {
@PersistenceContext(
type=PersistenceContextType.EXTENDED
)
protected EntityManager em;
public Foo getFoo (Integer id) {
return (Foo) em.find(Foo.class, id);
}
}
The EJB 3.0 specification defines server resources that can be injected via annotations. But it does not support user-defined application POJOs to be injected into each other.
In Spring, you first need to define a setter method (or constructor with arguments) for the service object in your POJO. The following example shows that the POJO needs a reference to the Hibernate session factory.
public class FooDao {
HibernateTemplate hibernateTemplate;
public void setHibernateTemplate (HibernateTemplate ht) {
hibernateTemplate = ht;
}
// Use hibernateTemplate to access data via Hibernate
public Foo getFoo (Integer id) {
return (Foo) hibernateTemplate.load (Foo.class, id);
}
}
Then, you can specify how the container gets the service object and wire it to the POJO at runtime through a chain of XML elements. The following example shows the XML element that wires a data source to a Hibernate session factory, the session factory to a Hibernate template object, and finally, the template object to the application POJO. Part of the reason for the complexity of the Spring code is the fact that we need to inject the underlying Hibernate plumbing objects manually, where the EJB 3.0 EntityManager
is automatically managed and configured by the server. But that just brings us back to the argument that Spring is not as tightly integrated with services as EJB 3.0 is.
<bean id="dataSource"
class="org.springframework
.jndi.JndiObjectFactoryBean">
<property name="jndiname">
<value>java:comp/env/jdbc/MyDataSource</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm
.hibernate.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="hibernateTemplate"
class="org.springframework.orm
.hibernate.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="fooDao" class="FooDao">
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<!-- The hibernateTemplate can be injected
into more DAO objects -->
Although the XML-based Dependency Injection syntax in Spring is complex, it is very powerful. (正因如此,才和其它框架和遺恨好的集成,這樣,架構(gòu)就有更多選擇。)You can inject any POJO, including the ones defined in your applications, to another POJO. If you really want to use Spring's DI capabilities in EJB 3.0 applications, you can inject a Spring bean factory into an EJB via the JNDI. (這樣INDI也是老樹新花。當(dāng)然JNDI的用處大了,JavaEE不可能丟了他。)In some EJB 3.0 application servers, the vendor might define extra non-standard APIs to inject arbitrary POJOs. A good example is the JBoss MicroContainer, which is even more generic than Spring, as it handles Aspect-Oriented Programming (AOP) dependencies.
Conclusions
Although Spring and EJB 3.0 both aim to provide enterprise services to loosely coupled POJOs, they use very different approaches to archive this goal. Dependency Injection is a heavily used pattern in both frameworks.
With EJB 3.0, the standards-based approach, wide use of annotations, and tight integration with the application server all result in greater vendor independence and developer productivity. With Spring, the consistent use of dependency injection and the centralized XML configuration file allow developers to construct more flexible applications and work with several application service providers at a time.
(各有所長(zhǎng)。EJB還是針對(duì)高端,Spring針對(duì)低端,中間一部分重合,大家兩全其美。
這篇文章在OnJava一篇罵聲,還是開源的同志們比較多阿。)
Acknowledgments
The author would like to thank Stephen Chambers, Bill Burke, and Andy Oliver for valuable comments.