New features in EJB3.1 (Part 4)

 作者:Reza Rahman  文章来源:www.theserverside.com

In the first three articles of this series, I covered optional interfaces for Session beans, Singleton beans, EJB Timer Service enhancements, simplified packaging, asynchronous Session bean invocation and EJB Lite. By popular demand, in this article I will cover WebBeans/EJB 3.1 integration. 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 to provide feedback.

WebBeans and EJB: Together Forever

WebBeans is one of the most exciting JSRs being developed in the Java EE 6 timeframe. The basic value proposition of WebBeans is actually pretty intuitive. If you have developed an application or two using JSF and EJB 3.0, you may have noticed that the JSF backing bean layer is usually pretty thin. In fact, even though you can easily inject EJBs into backing beans using the @EJB annotation, it’s not too far off to classify backing beans as glue-code. WebBeans eliminates this glue-code by allowing you to directly use EJBs as JSF backing beans. We’ll see how great this actually looks in a second.

In addition to effectively integrating the JSF and EJB programming models, WebBeans also adds a number of very cool features including robust annotation-driven DI, further ease-of-use for using the Java EE interceptor model as well as sensible component context management for web applications. This last feature I won’t talk about in great detail since it is requires more of a JSF focus than an EJB focus, but I’ll cover the rest.

WebBeans is largely inspired by JBoss Seam as well as Google Guice. The JSR is being led by Gavin King and Bob Lee is part of the expert group. My guess is that the core Seam code is going to be the basis for the major WebBeans pluggable implementation although it looks like many application servers like Caucho Resin will provide their own implementation as well.

EJBs as JSF Backing Beans

Let’s see what WebBeans is all about by re-factoring an example from EJB 3 in Action. The first substantial Session bean example in EJB 3 in Action is used for adding a bid. The session bean uses JPA to save a Bid entity into the database. Here is how the session bean and the entity would look like in a WebBeans environment:

  1. @Component  
  2. @Stateless  
  3. @Named("placeBid")  
  4. public class PlaceBidBean {  
  5.     @PersistenceContext  
  6.     private EntityManager entityManager;  
  7.       @In  
  8.       private Bid bid;  
  9.     public void addBid() {  
  10.         entityManager.persist(bid);  
  11.     }  
  12. }  
  13. @Component  
  14. @Entity  
  15. @Named("bid")  
  16. public class Bid {  
  17.     private Long bidId;  
  18.     private String bidder;  
  19.     private String item;  
  20.     private Double bidPrice;  
  21.     @Id  
  22.     @GeneratedValue  
  23.     public Long getBidId() {  
  24.         return bidId;  
  25.     }  
  26.     public void setBidId(Long bidId) {  
  27.         this.bidId = bidId;  
  28.     }  
  29.     public String getBidder() {  
  30.         return bidder;  
  31.     }  
  32.     public void setBidder(String bidder) {  
  33.         this.bidder = bidder;  
  34.     }  
  35.     public String getItem() {  
  36.         return item;  
  37.     }  
  38.     public void setItem(String item) {  
  39.         this.item = item;  
  40.     }  
  41.     public Double getBidPrice() {  
  42.         return bidPrice;  
  43.     }  
  44.     public void setBidPrice(Double bidPrice) {  
  45.         this.bidPrice = bidPrice;  
  46.     }  
  47. }  

@Component @Stateless @Named("placeBid") public class PlaceBidBean { @PersistenceContext private EntityManager entityManager; @In private Bid bid; public void addBid() { entityManager.persist(bid); } } @Component @Entity @Named("bid") public class Bid { private Long bidId; private String bidder; private String item; private Double bidPrice; @Id @GeneratedValue public Long getBidId() { return bidId; } public void setBidId(Long bidId) { this.bidId = bidId; } public String getBidder() { return bidder; } public void setBidder(String bidder) { this.bidder = bidder; } public String getItem() { return item; } public void setItem(String item) { this.item = item; } public Double getBidPrice() { return bidPrice; } public void setBidPrice(Double bidPrice) { this.bidPrice = bidPrice; } }

The @Component annotation on both the PlaceBidBean stateless session bean and the Bid JPA entity registers these components with the WebBeans container. @Named annotation assigns names to the components that the WebBeans container knows them by. These names are then used in JSF pages to refer to the components. The @In annotation on the bid instance variable injects a Bid entity into the session bean. It’ll be clearer how this works when you look at the JSP code below that uses the WebBeans components. Figure 1 depicts how the page actually looks like to the bidder adding the bid.

  1. <html>  
  2.   ...  
  3.   <body>  
  4.     <f:view>  
  5.       ...  
  6.       <h:form>  
  7.         <table>  
  8.           <tr>  
  9.             <td>Bidder</td>  
  10.             <td><h:inputText value="#{bid.bidder}"/></td>  
  11.           </tr>  
  12.           <tr>  
  13.             <td>Item</td>  
  14.             <td><h:inputText value="#{bid.item}"/></td>  
  15.           </tr>  
  16.           <tr>  
  17.             <td>Bid Amount</td>  
  18.             <td><h:inputText value="#{bid.bidPrice}"/></td>  
  19.           </tr>  
  20.         </table>  
  21.         ...  
  22.         <h:commandButton type="submit" value="Add Bid"   
  23.             action="#{placeBid.addBid}"/>  
  24.         ...  
  25.       </h:form>  
  26.       ...  
  27.     </f:view>  
  28.   </body>  
  29. </html>  

<html> ... <body> <f:view> ... <h:form> <table> <tr> <td>Bidder</td> <td><h:inputText value="#{bid.bidder}"/></td> </tr> <tr> <td>Item</td> <td><h:inputText value="#{bid.item}"/></td> </tr> <tr> <td>Bid Amount</td> <td><h:inputText value="#{bid.bidPrice}"/></td> </tr> </table> ... <h:commandButton type="submit" value="Add Bid" action="#{placeBid.addBid}"/> ... </h:form> ... </f:view> </body> </html>

Figure 1: Add Bid Page

As you can see, the bidder, item and bid amount fields in the JSP are bound to the corresponding properties of the Bid entity through EL. The names “bid" and “placeBid" in the EL match the values of the @Named annotation placed on the components. When WebBeans first encounters the bean name corresponding to the Bid entity, it creates a new instance of the entity behind the scenes and places it in the underlying page request context. Notice also that the PlaceBidBean.addBid method is bound as the action listener for the button to add the bid. When the button is clicked and the underlying form is submitted, WebBeans binds the values of the form fields to the Bid entity properties. It then injects the populated entity into the bid instance variable of the PlaceBidBean session bean because of the @In annotation. Unlike the Bid entity, the EJB is looked up from JNDI and placed into the request context. When the addBid method is invoked to handle the button click, the EJB uses the JPA entity manager to save the injected entity. Other than the request context, WebBeans also understands the application, session and very innovative “conversation" contexts. For details of these contexts, check out the WebBeans draft specification.

The appeal of the WebBeans programming model in the code above really cannot be understated. WebBeans unifies JSF, EJB and JPA in a way that makes Java EE really feel like one seamlessly integrated platform that just works out of the box and minimizes boilerplate code, much like what you might be used to while working with frameworks like Ruby on Rails. What do you think? Do you think this is compelling? Can you see any pitfalls to this model?

WebBeans Dependency Injection

A large majority of enterprise application components are either service components, DAOs or domain model components. While JPA is ideal for implementing domain model components, service and DAO components are good candidates for EJB. This is because EJBs are transactional and thread-safe by default (a lot of people seem to have the misconception that EJBs are remote-enabled by default as well; this is not true). EJB is even more of an obvious component model if you might use declarative services for security, remoting, web services, messaging, scheduling or asynchronous processing. These services are at our fingertips in the form of decorating an EJB with an annotation or two as needed.

However, there are some components in an application that simply don’t need declarative services, don’t need to be transaction-aware and don’t need implicit thread-safety guarantees. Good examples of such components are utilities or helper components. Because EJB 3.0 did not support dependency injection for non-managed components, it was not possible to use DI for such components without making them EJBs. WebBeans dependency injection solves these problems because the @Component annotation can be applied to any POJO, not just EJBs and JPA entities. Just as the @In annotation was used to inject the Bid entity into the PlaceBidBean stateless session bean, WebBeans can also inject plain components into EJBs.

Let’s take a look at a quick example. Let’s assume that the bid amount entered by the user is rounded up to two decimal places using a utility class before the bid is saved. This can be done as follows:

  1. @Component  
  2. public class MathUtil {  
  3.     ...  
  4.     public static double round(double value, int decimalPlaces) {  
  5.         BigDecimal converter = new BigDecimal(Double.toString(value));  
  6.         converter = converter.setScale(decimalPlaces,   
  7.             BigDecimal.ROUND_HALF_UP);  
  8.         return converter.doubleValue();  
  9.     }  
  10.     ...  
  11. }  
  12. @Component  
  13. @Stateless  
  14. @Named("placeBid")  
  15. public class PlaceBidBean {  
  16.     @PersistenceContext  
  17.     private EntityManager entityManager;  
  18.       @In  
  19.       private Bid bid;  
  20.       @In  
  21.       private MathUtil mathUtil;  
  22.         public void addBid() {  
  23.           bid.setBidPrice(mathUtil.round(bid.getBidPrice(), 2));  
  24.             entityManager.persist(bid);  
  25.     }  
  26. }  
@Component public class MathUtil { ... public static double round(double value, int decimalPlaces) { BigDecimal converter = new BigDecimal(Double.toString(value)); converter = converter.setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP); return converter.doubleValue(); } ... } @Component @Stateless @Named("placeBid") public class PlaceBidBean { @PersistenceContext private EntityManager entityManager; @In private Bid bid; @In private MathUtil mathUtil; public void addBid() { bid.setBidPrice(mathUtil.round(bid.getBidPrice(), 2)); entityManager.persist(bid); } }

In this case, a new instance of MathUtil will be created and injected into the EJB as needed. Although plain WebBeans components cannot directly use EJB declarative services, they can use DI themselves, as well as life-cycle callbacks ( @PostConstruct/ @PreDestroy) and interceptors.

The @In and @Component annotations are really just the tip of the ice-berg. WebBeans brings a whole host of other DI features into Java EE, including Guice style producer methods and binding annotations. Note that all EJB types including message driven beans can use WebBeans features.

WebBeans Interceptor Enhancements

Another major WebBeans feature very relevant to EJB is a very cool extension to the existing interceptor model. You can currently apply interceptors to EJBs using the @Interceptor and @Interceptors annotations. While this structure is both pretty easy to understand and intuitive, it is not very flexible. WebBeans introduces a little more flexibility to interceptors without making things more complex by introducing an annotation-based indirection. The easiest way to understanding this is though code. Let’s assume that we want to apply an auditing interceptor to the PlaceBidBean EJB. This is how we could do it:

  1. @InterceptorBindingType  
  2. @Target({TYPE, METHOD})  
  3. @Retention(RUNTIME)  
  4. public @interface Audited {}  
  5. @Audited @Interceptor  
  6. public class AuditInterceptor {  
  7.     @AroundInvoke  
  8.     public Object audit(InvocationContext context) throws Exception {  
  9.         System.out.println("Entering: "  
  10.             + context.getMethod().getName());  
  11.         System.out.println("  with args: "  
  12.             + context.getParameters());  
  13.         return context.proceed();  
  14.     }  
  15. }  
  16. @Component  
  17. @Stateless  
  18. @Named("placeBid")  
  19. public class PlaceBidBean {  
  20.     @PersistenceContext  
  21.     private EntityManager entityManager;  
  22.       @In  
  23.       private Bid bid;  
  24.       @Audited  
  25.     public void addBid() {  
  26.         entityManager.persist(bid);  
  27.     }  
  28. }  

@InterceptorBindingType @Target({TYPE, METHOD}) @Retention(RUNTIME) public @interface Audited {} @Audited @Interceptor public class AuditInterceptor { @AroundInvoke public Object audit(InvocationContext context) throws Exception { System.out.println("Entering: " + context.getMethod().getName()); System.out.println(" with args: " + context.getParameters()); return context.proceed(); } } @Component @Stateless @Named("placeBid") public class PlaceBidBean { @PersistenceContext private EntityManager entityManager; @In private Bid bid; @Audited public void addBid() { entityManager.persist(bid); } }

The @InterceptorBindingType annotation on the @Audited annotation is used to declare the fact that it is to be bound to an interceptor. Unlike the EJB 3.0 interceptor model, WebBeans uses the @Interceptor annotation to bind one or more annotations to a given interceptor. So the @Audited and @Interceptor annotations placed on AuditInterceptor means that the @Audited annotation placed on a component or method binds it to the interceptor. As a result, when the placeBid method is invoked, the AuditInterceptor is triggered and the audit method executes.

In addition to adding a level of indirection and flexibility, I think this extension really improves code readability. What do you think? Should this extension be adopted into the EJB specification itself or be applied to Java EE as a whole?

Still in the Works

Activity on the expert groups had understandably slowed down a bit due to JavaOne. However, things are picking back up strongly again. There are still a number of very interesting topics that are being actively discussed:

  1. The standardization of JNDI mappings is being simultaneously discussed in the Java EE 6 and EJB 3.1 expert groups. Looks like some very neat things will happen very soon in this area.
  2. Support for using EJB 3.1 in Java SE environments (e.g. unit tests) is still in the works. The fact that Embedded GlassFish has followed the OpenEJB, EasyBeans and Embedded JBoss path is a strong indication to me that this is solidly on track.
  3. A great extensibility mechanism proposal is being discussed in the Java EE 6 expert group. Looks like this might eventually end up in the EJB expert group as well. If this goes through, it will be a breeze to add non-standard third party declarative, annotation-based services to Java EE.

What are your thoughts on these features? If you think they are important, voice your opinion by emailing the expert groups. The EJB 3.1 expert group email address is [email protected] while the WebBeans expert group email address is [email protected]. Feel free to copy me at [email protected]. In the meanwhile, I’ll keep you informed as things settle down on the remaining features in the next and possibly last article in this series.

References

  1. JSR 316: Java EE 6, http://jcp.org/en/jsr/detail?id=316.
  2. JSR 318: Enterprise JavaBeans 3.1, http://jcp.org/en/jsr/detail?id=318.
  3. JSR 299: Web Beans, http://jcp.org/en/jsr/detail?id=299.
  4. Seam, http://www.seamframework.org.
  5. Google Guice, http://code.google.com/p/google-guice/.

你可能感兴趣的:(Interceptor,ejb,features,Annotations,Components,webbeans)