New Features in EJB3.1(Part 1)

作者 Reza Rahman 摘自http://www.theserverside.com
This series of articles is a preview of the changes the EJB 3.1 expert group is working on for the next version of the Java EE specification. The idea is to give you a head's up on the changes as well as gather your feedback early so the expert group has the best chance of getting it right. EJB 3.0 brought simplicity to Java EE 5 by moving away from a heavyweight programming model. EJB 3.1 aims to build on those successes by moving further down the path of simplicity as well as adding a handful of much-needed features. In each article in this series, I will let you know about the progress made by the expert group over the next few months.
In this first article, I'll cover the two features that have been discussed in detail so far--making interfaces optional for EJBs and Singleton Beans. I'll also give you a look-ahead into the possible features yet to be discussed. Remember, none of this has been finalized yet; all of this is really just a peek into the inner workings of the JCP so that you have a chance provide early feedback.

EJB Interfaces are Optional

The very first change the expert group covered was removing the last remaining obstacle to making EJBs look just like POJOs in their simplest form by making Session Bean business interfaces optional.

Interface-based programming is clearly a useful technique in writing loosely-coupled, unit-testable applications. That is precisely why both EJB 2.1 and Spring promote the idea of component interfaces. In fact, at least one interface is required even for EJB 3.0 Session Beans (this is not a requirement for EJB 3.0 Message Driven Beans).

The trouble is that component interfaces are just a needless abstraction in a lot of circumstances. I can't remember the times I silently cursed under my breath as I carried out the mechanical task of writing interfaces just because the framework needed it or the architect sitting in some ivory tower mandated it. A lot of times, just a regular Java object is really all you need for your foreseeable needs, especially if you don't really do much unit testing and loose-coupling is just not enough of a concern for the application.

EJB 3.1 allows you to do just that. Now you do not need any interfaces for Session Beans, just like JPA Entities and Message Driven Beans. All you have to do is annotate a POJO with the @Stateless or @Stateful to get a fully functional EJB. Take a look at the example below modified from the Session Bean chapter in EJB 3 in Action:

@Stateless  public   class  PlaceBidBean  {
@PersistenceContext
    
private EntityManager entityManager;
    
public void placeBid (Bid bid) {
    entityManager.persist(bid);
    }

}

To keep things interesting, let's add a few more features to this bargain-basement bean:

@Stateless @WebService
public   class  PlaceBidBean  {
    @PersistenceContext
  
private EntityManager entityManager;
  @WebMethod 
public void placeBid (Bid bid) {
  entityManager.persist(bid);
  }

}

In the above example, we are using JAX-WS 2.0 to expose the simple little placeBid method as a web service. We're also using JPA to persist a Bid entity. PlaceBidBean has the full range of Stateless Session Bean functionality available behind-the-scenes including pooling, thread-safety, component life-cycle, interceptors, security and declarative transactions. For example, as you might have guessed if you are familiar with EJB 3.0, the placeBid method is wrapped in a container-managed JTA transaction by default. RMI-based remoting will also be supported for these beans, although the exact semantics hasn't been solidified yet.

Besides making it easy to get going with simple bean classes, dropping the interface requirements also makes using EJB 3.1 beans in WebBeans easy. Gavin King is leading the WebBeans JSR (JSR 299), largely inspired by the lessons in JBoss Seam. WebBeans is expected to make JSF, JPA and EJB 3.1 virtually seamless (no pun intended) and make Java EE 6 truly feel like a fully integrated stack.

The Singleton Beans are Here

EJB 3.1 Singleton Beans are the middleware equivalent of the GOF Singleton pattern. In the Java EE context, they are primarily used to store application-wide shared data.
Think about all the times you needed to cache some data in memory so you didn't have to do the same old database lookups over and over again from different parts of the application. Neither Stateless Session Beans nor Stateful Session Beans meet this need. While Stateful Session Beans can maintain a session cache, it can't really be shared amongst the application tier components very easily.

There are various ways of solving this problem now. The simplest way is to use static fields or GOF Singleton pattern POJOs. More complex strategies that can work across an application cluster include using the Servlet container's application scope, JBoss Cache, OSCache, JCS and SwarmCache. Commercial solutions include Tangosol Coherence and Terracotta. While these solutions do work a majority of the time, they have a lot of weaknesses. Some are not thread-safe out of the box (static fields, Singleton POJOs), some are not transactional (Singleton POJOs, the Servlet application scope, OSCache, JCS, SwarmCache), none are remoteable and none have any security mechanisms. Other than the simplest solutions, all of them require the use of non-standard, third-party code. Enter EJB 3.1 Singletons.

The container is guaranteed to maintain a single shared instance of an EJB 3.1 Singleton. This means that Singletons can cache state across the application tier. Because it is an EJB, Singletons have all the middleware services you might expect--declarative transaction management, security, remoting, concurrency management, dependency injection, component life-cycle callbacks, interceptors and so on. Like all other EJBs, Singletons are simply annotated POJOs. Here is a simple example:

 

@Singleton
public   class  DiscountRateBean  {
    @PersistenceContext
    
private EntityManager entityManager;
    
private Rate rate;
    @PostConstruct
    
private void init() {
    rate 
= entityManager.find(Rate.class1);
    }

    @PreDestroy
    
private void destroy() {
    entityManager.merge(rate);
    }

    
public void setRate(Rate rate) {
    
this.rate = rate;
    }

    
public Rate getRate() {
    
return rate;
    }

}

 

The example is using JPA and bean life-cycle callbacks to load data on startup and save the data when the bean is destroyed (usually when the application is being shut-down). Any bean calling getRate and setRate is guaranteed access to the shared data stored in a single instance of DiscountRateBean. You'll notice an interesting problem--any updates will be lost if the container does not get a chance to call the pre-destroy callback. We'll see how to minimize this problem using cron-like timers in a future article.

By default, all Singleton methods are made thread-safe and transactional. This means that all multithreaded access to the bean is serialized and all methods are assumed to have a REQUIRED transaction attribute (do you think that these are sensible defaults? If not, why?). You can change transactional behavior using the @TransactionManagement and @TransactionAttribute annotations just like you would do for a Stateful or Stateless Session Bean. If you've ever done this sort of thing for a relatively large scale application, you know having both the getRate and setRate methods serialized can be a serious performance bottleneck. In reality, you simply need a read-lock on the getRate method while a read-write lock should be placed on the setRate method. You can do this by using the @ConcurrencyAttribute like so:

 

@Singleton
public   class  DiscountRateBean  {
    @PersistenceContext
    
private EntityManager entityManager;
    
private Rate rate;
    @PostConstruct
    
private void init() {
    rate 
= entityManager.find(Rate.class1);
    }

    @PreDestroy
    
private void destroy() {
    entityManager.merge(rate);
    }

    @ConcurrencyAttribute(READ_WRITE_LOCK)
    
public void setRate(Rate rate) {
    
this.rate = rate;
    }

    @ConcurrencyAttribute(READ_LOCK)
    
public Rate getRate() {
    
return rate;
    }

}


For those who are familiar with Doug Lea's work, the READ_LOCK and READ_WRITE_LOCK terminology comes from java.util.concurrent. There are some application shared data that are just read-only. Any locking is really unnecessary in such cases. In such a case, you can create an unsynchronized Singleton like this:

@Singleton
@ConcurrencyAttribute(NO_LOCK) 
//  READ_LOCK would also work...
public   class  DiscountRateBean  {
    @PersistenceContext
    
private EntityManager entityManager;
    
private Rate rate;
    @PostConstruct
    
private void init() {
    rate 
= entityManager.find(Rate.class1);
    }

    
public Rate getRate() {
    
return rate;
    }

}

One alternative to @ConcurrencyAttribute(READ_LOCK), @ConcurrencyAttribute(READ_WRITE_LOCK) and @ConcurrencyAttribute(NO_LOCK) being forwarded is @ConcurrencyReadLock, @ConcurrencyReadWriteLock and @ConcurrencyNoLock. To keep consistency with these low-level annotations, @TransactionAttribute would be broken up into @TransactionRequired, @RequiresNewTranscation, @TransactionNotSupported and so on. Some folks have pointed out that this mode of thinking begins to get into "annotation bloat" and adds a new source of complexity. This annotation granularity is also not consistent with Spring and C#.NET declarative transactions. Supporters of this model point out that it is easier to type than @ConcurrencyAttribute(READ_WRITE_LOCK), etc. What do you think?

It may also be entirely possible that you want to manage Singleton concurrency yourself and want the container to be uninvolved other than providing middleware services. This is supported through what is being called bean managed concurrency (a similar idea to bean managed transactions). Here is an example:

 

@Singleton
@ConcurrencyManagement(BEAN)
public   class  DiscountRateBean  {
    @PersistenceContext
    
private EntityManager entityManager;
    
private Rate rate;
    @PostConstruct
    
private void init() {
    rate 
= entityManager.find(Rate.class1);
    }

    @PreDestroy
    
private void destroy() {
    entityManager.merge(rate);
    }

    
public synchronized void setRate(Rate rate) {
    
this.rate = rate;
    }

    
public synchronized Rate getRate() {
    
return rate;
    }

}

 

Notice this time we are managing concurrency ourselves using the synchronized keyword. Since the container will not interfere, you are free to use whatever concurrency management mechanism you like, including but not limited to using the full power of the java.util.concurrent package. For now, the new concurrency management features are limited to EJB 3.1 Singletons, but they could be expanded to cover other bean types.

Singletons will also give you control over lazy/eager loading as well as explicit Singleton dependency management to address load ordering. We won't discuss those features here although you are welcome to comment on that too. Although the specification does not cover clustering support, it is very likely that most vendors will make Singletons cluster-safe (just like Stateful Session Beans).

More EJB 3.1 Features

The two features discussed here are just the tip of the iceberg. There are a number of other very interesting features listed on the JSR agenda. Here are some of the most interesting ones:

  1. Support for direct use of EJBs in the servlet container, including simplified packaging options. The current thought is to allow EJBs in the WEB-INF/classes directory while allowing ejb-jar.xml to reside in the WEB-INF directory, just like the web.xml file. In a similar vein, you would be able to place an EJB jar into the WEB-INF/lib directory.
  2. EJB Timer Service enhancements to support cron-like scheduling, deployment-time timer creation, and Stateful Session Bean timed objects.
  3. Support for stateful web services via Stateful Session Bean web service endpoints.

In addition to these, there are a handful of features that are currently not on the JSR agenda but could be really great:

  1. Further simplification of JMS, JavaMail and database injected resources. For example, you should be able to inject MessageSenders, not just Queues and ConnectionFactory references. Gavin King is a major proponent of this enhancement.
  2. A Service Provider Interface (SPI) for EJB. This will make a number of innovative third-party integrations possible such as iBATIS, Spring, Acegi, Quartz or even Groovy Beans. Bill Burke, Mike Keith and I strongly support this feature.
  3. The ability to add EJB support to a lightweight servlet container like Tomcat. This would be similar to what is already available in the open source world in the form of Embedded JBoss, OpenEJB and EasyBeans. I haven't brought this feature up to the group yet, but will if you think it is good.
  4. The ability to use local transactions in addition to JTA in EJB. I think this would be very useful for smaller web applications that really don't need JTA.

A feature forwarded by Adam Bien, the standardization of JNDI mapping, is also particularly interesting as it will go a long way to enhancing portability across containers. What are your thoughts on this last list? Are the features useful? Should they be pushed harder? Are they more useful than what is on the current agenda?

Your Help is Needed

As you might be able to see, the expert group is trying hard to add useful features to EJB 3.1. We can only go so far on our own though... we need your feedback to tell us if we are on the right track or if we should be pursuing other paths. You can send your feedback directly to the JCP at [email protected]. Feel free to CC me at [email protected] so I am in the loop. In the coming months, I'll try to present more features to you as we discuss them on the expert group. Until then, wish us good luck!

References

  1. JSR 318: Enterprise JavaBeans 3.1
  2. JSR 299: Web Beans
  3. JSR 316: Java EE 6

你可能感兴趣的:(New Features in EJB3.1(Part 1))