Architecture
Einstein once said: "Everything should be as simple as possible, not simpler." Indeed, the pursuit of scientific truth is to simplify the fundamental assumptions of the theory so that we can deal with the real problem of trouble. The same is true of enterprise-level software development.
The key to simplifying enterprise-level software development is to provide an application framework that hides complexity (for example, transaction, security, and sustainability). A well-designed framework component can improve the reuse (reuse) of code, increase development efficiency, and achieve better software quality. However, currently the EJB 2.1 framework component in Java EE 1.4 is generally considered to be poorly designed and overly complex. Java developers are dissatisfied with EJB 2.1, and they have experimented with a variety of other methods for middleware service delivery. Most notably, the following two framework components have aroused great interest and a positive response from developers. They are likely to be a framework component for future enterprise-class Java applications.
· The Spring framework component is a popular, but non-standard, open source framework component. It is primarily developed and controlled by INTERFACE21 Inc. The schema of the Spring framework component is based on the dependency injection (DI) design pattern. Spring can work in isolation or with an existing application server, and it uses XML configuration files heavily.
· The EJB 3.0 framework component is a standard framework component, defined by the Java Community Organization (JCP), and supported by all major EE vendors. The open source and business implementations of the pre-release EJB 3.0 specification can be seen on both JBoss and Oracle. EJB 3.0 uses a lot of Java annotations (annotation).
The core design philosophies of these two framework components are the same: both aim to pass middleware services to loosely coupled, simple legacy Java Objects (POJO). These framework components bundle application services with Pojo by intercepting execution content at run time or injecting service objects into the Pojo. Pojo itself does not care about the process of bundling and has little reliance on framework components. As a result, developers can focus on business logic, and individuals can test their pojo without framing components. In addition, because Pojo does not need to inherit or implement framework component interfaces from framework components, developers have a high degree of flexibility in building inheritance and building applications.
However, although the two design concepts are the same, they use a completely different approach when delivering Pojo services. Although a large number of books and articles have been published to compare spring or EJB 3.0 with EJB 2.1, none of them has really studied the difference between spring and EJB 3.0. In this article, I'll look at the key differences between the spring and EJB 3.0 framework components and discuss their pros and cons. The topics in this article can also be applied to some of the lesser-known enterprise-class middleware framework components because they focus on the loosely coupled pojo design. I hope this article will help you choose the best framework component that meets your needs.
Vendor-Independent (Independence)
One of the most important reasons a developer chooses a Java platform is the vendor-independent nature of the platform. EJB 3.0 is an open, standard, vendor-independent platform. The EJB 3.0 specification is developed and supported by all major open source and commercial vendors in the enterprise-class Java community. The EJB 3.0 Framework component isolates the developer from the Application Server implementation (Implementation). For example, although JBoss's EJB 3.0 implementation is based on Hibernate, and Oracle's EJB 3.0 implementation is based on TopLink, developers do not need to learn special APIs for Hibernate or TopLink. You can have their applications run on JBoss and Oracle. Vendor independence distinguishes the EJB 3.0 framework component from other POJO middleware framework components.
However, many of the EJB 3.0 critics quickly point out that the EJB 3.0 specification has not yet reached the final release version when writing this article. It may take one to two years before EJB 3.0 is adopted by all the mainstream Java EE vendors. However, even if your application server does not naturally (natively) Support EJB 3.0, you can still run EJB 3.0 applications on the server by downloading and installing an "embedded" EJB 3.0 product. For example, the JBoss Embedded EJB 3.0 product is open source and can be run in any j2se-5.0-compliant environment (for example, in a Java application server). It is now in beta testing. Other vendors may soon release their embedded EJB 3.0 offerings, especially for the "Data Sustainability" section of the specification.
On the other hand, spring has been a non-standard technique, and it is still the case in the foreseeable future. Although you can use the Spring framework component with any application server, the spring application is "locked" between spring itself and the specific services that you choose to integrate into spring.
· Although the Spring framework component is an open source project, it still has a proprietary XML format for configuration files and a proprietary programming interface. Of course, this kind of "lock-in" occurs on any non-standard product, and spring is no exception. But it does: the long-term viability of your spring application relies on the spring project itself (or INTERFACE21 Inc, which employs most of the spring core Open). In addition, if you use any spring-specific services, such as the Spring transaction manager or Spring MVC, you are "locked out" in these APIs.
· Spring applications need to know the service providers behind the scenes. For example, for data persistence services, the Spring Framework component uses different DAO and template-assisted classes for JDBC, Hibernate, Ibatis, and JDO. Therefore, if you want to replace a continuous service provider for a spring application, such as switching from JDBC to hibernate, you must refactor your application code and use the new helper class.
Service integration
At a high level, the Spring framework component is located on the application server and service class libraries. Their service integration code, such as data access templates and helper classes, is located in the framework component and exposed to application developers. In contrast, the EJB 3.0 framework component is tightly integrated into the application server, and the service integration code is encapsulated in a standard interface.
As a result, EJB 3.0 vendors can actively optimize the overall performance and developer experience. For example, in the JBoss EJB 3.0 implementation, when using Entitymanager to keep entity bean Pojo, the underlying Hibernate dialog transaction is automatically associated with the JTA transaction of the calling method, which is also committed when the JTA transaction is committed. If you use a simple @persistencecontext annotation (an example later in this article), you can even bundle Entitymanager and its underlying hibernate transaction into an application transaction in a stateful (stateful) conversation bean. The application transaction spans multiple threads in a conversation and is very effective in transactional Web applications such as multiple-page shopping carts. Because of the tight integration of EJB 3.0 framework components, Hibernate and Tomcat in JBoss, these simple and integrated programming interfaces are implemented. A similar level of integration is also achieved between Oracle's EJB 3.0 framework component and its lower toplink continuous service.
Another example of integrated services in EJB 3.0 is cluster (clustering) support. If you deploy EJB 3.0 applications in a server cluster, all fail-through (fail-over), load-balancing, distributed caching, and state replication services are automatically available to the application. The underlying cluster services are hidden behind the EJB 3.0 programming interface and are completely transparent to EJB 3.0 developers.
In spring, the interaction between the Optimization framework component and the service is much more difficult. For example, in order to manage hibernate transactions using the declarative transaction service of spring, you must explicitly configure the spring TransactionManager and Hibernate sessionfactory objects in the XML configuration file. The spring application developer must explicitly manage transactions across multiple HTTP requests. In addition, there is no easy way to use the Cluster service in a spring application.
Flexibility of service integration
Because the service integration code in spring is exposed as part of a programming interface, application developers can flexibly integrate services as needed. This feature allows you to integrate your own "lightweight" application server. The most common way to use spring is to "glue" Tomcat and hibernate together to provide a simple database-driven Web application. In this case, spring itself provides a transactional service, Hibernate provides a continuous (persistence) service-an organization that builds a micro-application server in spring.
EJB 3.0 application servers do not give you the flexibility to select services. In most cases, you get a set of prepackaged features, and you only need a subset of them. However, if the application server is dominated by a pattern of internal design (like JBoss), then you may be able to separate it and remove some unnecessary features. In any case, custom-mature application servers are not a simple thing to do.
Of course, if the scope of the application is beyond a single node, you may need to bundle services from the normal application server (such as resource buffer pools, message queues, and clusters). The spring solution, like any EJB 3.0 solution, is "heavyweight" in terms of overall resource consumption.
In spring, flexible service integration makes it easier to bundle generic (mock) objects (rather than actual service objects) into applications for unit testing outside the container. In an EJB 3.0 application, most of the components are simple pojo, and we can easily test them outside of the container. But for testing objects that involve container services (for example, continuous entitymanager), we recommend testing in containers because they are simpler, stronger, and more accurate than the methods used to clone objects. Comparison of XML with annotations
From an application developer's point of view, Spring's programming interface is primarily based on XML configuration files, and EJB 3.0 is widely used in Java annotations. XML files can express complex relationships, but they are also lengthy and less robust. Annotations are straightforward, but in annotations we find it difficult to express complex or hierarchical structures.
Spring and EJB 3.0 the choice of XML or annotations depends on the architecture behind the two framework components: since annotations can only hold a relatively small amount of configuration information, only the pre-Integrated Framework components (similar to most of the preparatory work already done in the framework component) can be widely used as configuration options. As we have discussed, EJB 3.0 meets this requirement, and spring as a generic DI framework component does not meet this requirement.
Of course, EJB 3.0 and spring are learning about each other's best features, and they all support XML and annotations to some extent. For example, an XML configuration file in EJB 3.0 is an optional overload mechanism that can be used to change the default behavior of annotations. Annotations can also be used to configure certain spring services.
The best way to understand the difference between XML and annotations is through an example. In the next section, we'll see how spring and EJB 3.0 provide the key services for your application.
Declarative Service (declarative services)
Both spring and EJB 3.0 bundle Run-time services, such as transaction, security, logging, messaging, and custom services, onto the application. Because none of these services are directly associated with the application's business logic, they are not managed by the application itself. Instead, these services are transparently applied to the program at run time by a service container (such as spring or EJB 3.0). The developer (or administrator) configures the container and tells the container how/when to apply the service.
EJB 3.0 configures declarative services with Java annotations, and spring uses XML configuration files. In most cases, the EJB 3.0 annotation method is simpler and more elegant for such services. Here is an example of applying a transaction service to the Pojo method in EJB 3.0.
public class Foo {
@TransactionAttribute (transactionattributetype.required)
Public Bar () {
Perform some action ...
}
}
You can also define multiple attributes in a single code fragment and apply multiple services. Here is an example of a transaction and security service that was applied to Pojo in EJB 3.0:
@SecurityDomain ("other")
public class Foo {
@RolesAllowed ({"Managers"})
@TransactionAttribute (transactionattributetype.required)
Public Bar () {
Perform some action ...
}
}
Using XML to specify code attributes and configuring declarative services can result in lengthy and unstable configuration files. Here is an example of using an XML element in a spring application to apply a very simple Hibernate transaction service to the Foo.bar () method:
!--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"
!--need to setup the Sessionfactory Bean
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>
If you add multiple interceptors (interceptor, such as security interceptors) to the same pojo, the complexity of XML increases exponentially. Spring is aware of the limitations of using only XML configuration files, and it now supports the use of Apache common metadata in Java source code to specify transaction attributes. The JDK-1.5 style annotation is also supported in the latest spring 1.2. If you want to use transaction metadata, you need to change the Transactionattributesource bean above to a attributestransactionattributesource example and increase the additional configuration associated with the metadata interceptor.
Class= "Org.springframework.aop.framework.autoproxy
. Defaultadvisorautoproxycreator "/>
Class= "Org.springframework.transaction.interceptor
. Attributestransactionattributesource "
Autowire= "Constructor"/>
Class= "Org.springframework.transaction.interceptor
. Transactioninterceptor "
Autowire= "Bytype"/>
Class= "Org.springframework.transaction.interceptor
. Transactionattributesourceadvisor "
Autowire= "Constructor"/>
Class= "Org.springframework.metadata.commons
. Commonsattributes "/>
The spring metadata simplifies the transactionattributesource element when you have many transaction methods. But it doesn't solve the basic problem of XML configuration Files--verbosity and fragility, or the need to use transaction interceptors, TransactionManager, and Transactionattributesource.
Dependency Injection (Dependency injection)
The key advantage of middleware containers is that they allow developers to build loosely coupled applications. The client of the service needs only to know the interface of the service. Containers initialize service objects with specific implementations and enable clients to access them. This allows the container to switch between different service implementations without having to change the interface or client code.
The dependency injection (DI) pattern is one of the best ways to implement loosely coupled applications. It is easier to use and more elegant than old methods, such as dependency lookups via Jndi or container callbacks. When di is used, the framework component acts as the object factory that establishes the service object and injects these service objects into the application Pojo according to the Run-time configuration. From an application developer's point of view, when clients pojo need to use a service object, they automatically get the object.
Both spring and EJB 3.0 provide extensive support for the DI pattern, but there are some deep differences between them. Spring supports the normal, but complex, Di API;EJB 3.0 based on XML configuration files to support the injection operations of most common service objects (such as EJB and context-relational objects) and Jndi objects through simple annotations.
The EJB 3.0 di annotation is very concise and easy to use. @Resource tags are injected into most common service objects and Jndi objects. The following example shows how to inject the default DataSource object of a server in Jndi into a field variable in Pojo. Defaultds is the name that Jndi uses to represent DataSource. The correct value is automatically assigned to the MYDB variable before it is used for the first time.
public class Foodao {
@Resource (name= "Defaultds")
DataSource myDb;
Using MyDb to get the JDBC connection for a database
}
As a complement to the direct injection of field variables, we can also use the @resource annotation in EJB 3.0 to inject objects by setting (setter) methods. For example, the following example injects a dialog context object. The application has not explicitly invoked the setting method-the method is called first by the container before any other method is called.
@Resource
public void Setsessioncontext (Sessioncontext ctx) {
Sessionctx = CTX;
}
For more complex service objects, some dedicated injection annotations have been defined. For example, @EJB annotation is used to inject an EJB stub, @PersistenceContext annotation is used to inject the Entitymanager object (which handles database access for the EJB 3.0 entity Bean). The following example shows how to inject a Entitymanager object into a stateful conversation bean. The type attribute of the @PersistenceContext annotation indicates that the injected Entitymanager has an extended transactional context relationship-it is not automatically submitted with the JTA transaction manager, so it can be used for application transactions that span multiple threads in a single conversation.
@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 the server resources that can be injected through annotations. However, it does not support user-defined applications Pojo each other.
In spring, you first need to define a setting method (or a constructor with parameters) for the Pojo service object. The following example shows that Pojo needs a pointer to the Hibernate conversation factory.
public class Foodao {
Hibernatetemplate hibernatetemplate;
public void Sethibernatetemplate (Hibernatetemplate ht) {
Hibernatetemplate = HT;
}
Accessing data using the Hibernate template
Public Foo Getfoo (Integer ID) {
Return (Foo) hibernatetemplate.load (Foo.class, id);
}
}
Next, you can specify how the container gets the service object through the XML element chain at run time and bundles it onto the pojo. The following example demonstrates bundling a data source into a hibernate conversation factory, bundling the conversation to a Hibernate template object, and finally tying the template object to the XML element of the application Pojo. Part of the reason this spring code is so complicated is that we have to manually inject the underlying hibernate pipe object, and the EJB 3.0 Entitymanager is automatically managed and configured by the server. But it's back to the discussion that spring doesn't have a tight integration with services like EJB 3.0.
Class= "Org.springframework
. Jndi. Jndiobjectfactorybean ">
Java:comp/env/jdbc/mydatasource
Class= "Org.springframework.orm
. Hibernate. Localsessionfactorybean ">
Class= "Org.springframework.orm
. Hibernate. Hibernatetemplate ">
!--the hibernatetemplate can be injected
Into the more DAO objects-->
Although the xml-based dependency injection syntax in spring is complex, it is powerful. You can inject any pojo (including what you define in your application) into another pojo. If you really want to use spring's dependency injection capability in an EJB 3.0 application, you can inject the spring bean factory into the EJB through Jndi. In some EJB 3.0 application servers, vendors may define additional non-standard APIs to inject arbitrary pojo. A good example of this is JBoss Microcontainer, which is even more common than spring because it handles aspect-oriented programming (AOP) dependencies.
Conclusions
Although the goal of Spring and EJB 3.0 is to provide enterprise-class services for loosely coupled Pojo, they are used in radically different ways to achieve this goal. Dependency Injection (DI) is heavily used in both framework components.
With EJB 3.0, the standards-based approach, the extensive use of annotations, and tight integration with application servers have created strong vendor-agnostic and developer efficiencies. Using spring, the consistent use of dependency injection and centralized XML configuration files allows developers to build more flexible applications and use multiple application services at the same time.