Java EE 6 and Spring 3.0

Java EE 6 and Spring 3.0

I come from a Java EE/JSF background and sometime ago I blogged about Java EE6. Lately, however, I have participated in a project using Spring/MVC 3.0. Though this is a ramp up experience for me on Spring, I feel this is a great time to take advantage of the freshness of the 2 technologies in my mind and write a post on my personal view on the differences and similarities between both.

In this post I'll stick to 3 topics: my overall feeling, some basic functionality and ease of use.

Come along and feel free to leave your comments and suggestions. The source code of the examples can be downloaded HERE.

The example

The example is a CRUD to an User entity persisted to the database via a business bean. There are 2 versions of the CRUD, one with Spring 3.0 the other with Java EE 6.

The technology stack for both versions of the example is:

  Spring version Java EE version
Entity JPA JPA
Business Spring beans EJB 3.1 beans
Controller Spring MVC JSF2/CDI
View JSP/JSTL/Tiles JSF2/Facelets

Despite using different APIs, both versions are as similar as possible to each other, so it can be easy to compare.

The examples are running on JBoss AS 6 M4 and JBoss 5.1 for the Java EE 6 and Spring 3.0 versions respectively.

 

Entity (User.java)

The JPA entity bean code is exactly the same for the Java EE and Spring versions.

 

Business (UserFacade.java / UserFacadeBean.java)

Both versions are basically the same they just use different annotations.

The Java EE version uses the @Stateless annotation:

@Stateless
public class UserFacadeBean implements UserFacade { 
// The remaining code is the exactly equal

The Spring version uses the @Service annotation:

@Service
@Transactional
public class UserFacadeBean implements UserFacade { 
// The remaining code is the exactly equal

One can see the difference in defaults: the Java EE/EJB methods are transactional by default; the Spring bean methods are not transactional by default.

Spring has support for several DAO helpers to abstract from the persistence provider (JPA, JDBC, hibernate, etc) but I believe in simple cases such as this DAO's bring more pain than benefit. EJB's use JPA by default.

You can check other differences between Spring and EJB beans here.

 

Controller and view

This is where the big differences in the show up. Spring uses SpringMVC/JSP/JSTL and Java EE uses Facelets/JSF/CDI.

Let's go through a few of the differences.

1 - URL styles

Spring MVC has REST style URLs out of the box. JSF2 uses regular URLs. An example:

Spring MVC: http://localhost/user/edit/34

JSF2: http://localhost/edit.jsf?userId=34

 

2 - Binding controllers and views - Calling the controller from the view (or how to submit a form)

The Spring approach is to define a handler on the controller via a @RequestMapping annotation, where you define the URL you want to listen. On the JSP page you can either use the HTML <form> tag or the Spring MVC <form:form> tag.

View Controller
<form method="post" action="/user/register">
  <input type="submit" value="Register" />
  ...       
</form>    
public class UserController /* ... */ {

  @RequestMapping(value = "/user/register", 
                  method = RequestMethod.POST)
public String registerSetupPost(/* ... */) {
// ....

 

With Java EE (JSF2/CDI) you expose the controller name via @Named annotation. On the JSF page you use the <h:commandButton> JSF tag with the name of the controller method you want to call.

View Controller
<h:form>
  <h:commandButton value="Register"
    action="#{userController.registerSetup}" />
  ...
</h:form>
@Named
public class UserController /* ... */ {

  public String registerSetup() {
  //...

In summary: With Spring MVC it's the controller that defines the URL it listens. With Java EE it's the page that defines the controller method to call.

 

3 - Binding controllers and views - Rendering a view

With Spring, the controller specifies the attributes to push to the JSP page via the model.addAttribute method and the return string defines the next page to be displayed.

Controller View
public class UserController /* ... */ {

  @RequestMapping(...)
public String registerSetupPost(/* ... */) {
// ..... model.addAttribute("user", user);
return "/user/register_confirm";
<!-- File: /user/register_confirm.jsp -->
Name: ${user.fullName}
<br />
Email: ${user.email}

 

With Java EE, the JSF page pulls the data it needs from the controller attributes.
Just like the Spring version, the next page to be displayed is defined on the controller return string.

Controller View
@Named
public class UserController /* ... */ {
  User user = new User();

  public String registerSetup() {
// ..... return "register_confirm";

<!-- File: register_confirm.xhtml -->
Name: #{userController.user.fullName}
<br />
Email: #{userController.user.email}

This JSF example uses implicit navigation, but JSF also has a complex navigation system where rules can be defined to change the page flow.

 

4 - Passing state between requests

Here we see a fundamental difference between Spring MVC and JSF. The Spring approach is geared towards stateless interactions, the Java EE/CDI/JSF2 approach facilitates the use and management of server side state.

In this example the user registration has 2 screens, one to input the user data (register page) the other to confirm the creation (register_confirm page). The user only has to be persisted in the database after the confirmation, however the user state has to be passed between the 2 pages.

There are imagine several approaches to do this, including:

  • HTTP session
  • Hidden fields
  • Cookies
  • Inserting the user in the database after the first page (maybe with an inactive attribute).
  • etc

All these approaches have advantages and disadvantages, regarding performance, memory leaks, client/server traffic, possibility to use several tabs/browser windows, etc.

Here I decided to use the (arguably) simpler approaches available to each framework. Hidden fields with Spring and CDI conversation with Java EE.

The Spring version writes hidden fields on the register_confirm page to keep the user information posted on the register page.

Controller View
@RequestMapping(value = "register", 
                method = RequestMethod.POST)
public String registerSetupPost(
    User user, /* ... */ Model model) {

  //....
  model.addAttribute("user", user);
  return "/user/register_confirm";
}
@RequestMapping(value = "register_confirm", 
                method = RequestMethod.POST)
public String registerConfirmPost(
    User user, /* ... */ Model model) {

  uf.persist(user);
  // .....
}
<!-- register_confirm.jsp -->
<form:form modelAttribute="user"
  method="post" 
  action="register_confirm">

Name: ${user.fullName}<br />
Email: ${user.email}<br />
Password: ${user.password}<br />
<input type="submit" value="Confirm" />
<form:hidden path="fullName" />
<form:hidden path="email" />
<form:hidden path="password" />
</form:form>

With Java EE we can use the CDI conversation scope, which keeps the state in the server but it has major differences to the regular use of HTTP session:

  • Lifespan of objects in the session can be controlled -> session does not grow endlessly
  • There can be different conversations open for each tab/browser windows -> allows the use of several tabs or browser windows with different state

In this example a conversation is started when the register setup page is submitted and it is finished when the register_confirm page is submitted.

Controller View
public String registerSetup() {
  conversation.begin();
  return "register_confirm";
}
public String registerConfirm() {
  uf.persist(user);
  conversation.end();
}
<!-- register_confirm.xhtml -->
<h:form>
Name: #{userController.user.fullName}<br />
Email: #{userController.user.email}<br />
Password: #{userController.user.password}<br />
<h:commandButton action="#{userController.registerConfirm}" value="Confirm" />
</h:form>

 

5 - JSR303 Validation

Both Spring and Java EE include JSR303 validation.

With Spring, form validation is enabled by using the @Valid annotation on the object and checking BindingResult.

Controller
public String registerConfirmPost(@Valid User user, BindingResult result, Model model) {
if (result.hasErrors()) {
return null;
}
uf.persist(user);
// ...

With Java EE, validation is enabled by default, no configuration is necessary.

6 - JSP+Tiles vs Facelets

Spring MVC uses JSP and optionally tiles for templating. Java EE uses facelets.

Both frameworks have great templating and composition features. I won't go i much detail here but you may find a good article HERE

 

Conclusions

We've seen some of the differences between Java EE and Spring. Both are great frameworks with great features. My feeling is that they are fairly easy to learn and use (at least much easier than they were a few years ago).

Spring, being a single vendor solution has the advantage of moving faster and having new features earlier than standards based solutions. The flip side is exactly that it is not an open standard (like Java EE) so projects using it will be locked in to the Spring vendor.

By now only Glassfish application server supports Java EE 6 and JBoss has plans to release its app server with Java EE 6 by the end of this year.

Please feel free to leave your comments!

 

22 comments:

  1. So Glassfish is the only JEE6-compliant vendor at this point in time. Interesting.

    ReplyDelete
  2. It'll be a little bit more perfect if you have ajax features in your comparison. For example, to refresh a div in JSF, we only have to specify its id somewhere (sorry I didn't use JSF for a long time). In Spring MVC + jQuery/YUI/... that div content must be written in a separate jsp page which will be rendered after an ajax request is made. Then the content of this page will be inserted into the div like $(div).html(content_of_that_separate_jsp_page). I'm not sure if there's any other way to do it in Spring MVC + javascript.

    ReplyDelete
  3. @Mike Funk: What's about JBoss 6.0.0 M4, Geronimo 3.0 M1 and OpenEJB they're all JEE6 compliant.

    ReplyDelete
  4. Thanks for the feedback.

    Toi: Regarding AJAX, it would be great to include it in the POST - unfortunately to this point I didn't use it with Spring MVC. If I use it in future projects, I'll be sure to update the article.

    Mike: JBoss AS 6 and Apache Geronimo working to be Java EE 6 compliant, but are not yet final releases.
    Jboss plans for the final release of JBoss AS 6 on December 2010. Apache Geronimo should also be soon enough.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. The conversation scope fits naturally with Hibernate (JPA) and CDI standardized from SEAM is the most beautiful pieces in the new JEE6 bundle. Gavin is the King.... game over

    ReplyDelete
  7. Interesting post, I dont agree to the locking yurself with Spring since its failry easy to migrate from it.

    ReplyDelete
  8. Franck: I totally agree with you. CDI conversations are really amazing.

    ReplyDelete
  9. Web Development: What I mean is that if you have a big enterprise Spring project with paid vendor support (from Springsource) and you decide to move away from from Springsource, then you have to change the implementation to another framework, which will be a pain (to say the least) in a big project.

    On the other hand if you have a big enterprise Java EE project with support from ... say Oracle AS and decide to change to JBoss AS, you will also have to change some XML configuration files, probably some classes and test everything really well. But the pain will be significantly lower because the APIs and XML descriptors are standardized.

    ReplyDelete
  10. I so much agree with trincao. You often hear the claim that Spring is easy to migrate, since it can run on both Jetty and Tomcat. In both cases however you're still stuck with Springsource.

    I't basically the same as saying that even your own closed source fully proprietary framework is easy to migrate from, since it can run on both the Oracle JDK and the IBM JDK.

    ReplyDelete
  11. Informative article

    ReplyDelete
  12. Thank You for this article! It is great and it helped me A LOT!

    ReplyDelete
  13. You're welcome. It's always good to know!

    ReplyDelete
  14. One of these days I'll understand why so many sites give unrunnable examples...

    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
    java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:184)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3972)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4467)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:546)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:637)
    at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:563)
    at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:498)
    at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1277)
    at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:321)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
    at org.apache.catalina.core.StandardService.start(StandardService.java:519)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:581)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1361)
    ... 26 more

    ReplyDelete
  15. Have you used the same environment described in the post? JBoss 5.1 for the spring example JBoss 6.0M4 for the JEE example.

    ReplyDelete
  16. I disagree about the comments that using Spring causes lockin. Spring actually tries to prevent you from lockin. You can use standard annotations like @Named, @Inject, @ApplicationScoped, etc. You are free to choose Spring MVC or JSF. Also with Spring you can use all JavaEE 6 goodies in a JavaEE 5 container and later move to JavaEE 6 with ease.

    By the way, I do not understand "EJBs have transaction built-in, but Spring beans don't". Do any other beans in a JavaEE 6 server have easy transaction facilities like EJBs? Or only Spring does that? Which transaction definition is stronger, EJB version or Spring version? Spring does things years before, than does a quick update to standards and people still say things like lockin.

    ReplyDelete
  17. Hi Berserk,
    This post is not intended to start a flame war - it's about my experience with both technology stacks.
    Regarding the "lock in": as you very well mention, Spring provides a mechanism to integrate with Java EE, but if you choose to go the Spring way and use Spring annotations like @Service, @Transaction etc, or use Spring MVC the only implementation available is the Springsource implementation (to my knowledge - correct me if I'm wrong).

    I did not say "EJBs have transaction built-in, but Spring beans don't" I said the is a difference in default options "Java EE/EJB methods are transactional by default; the Spring bean methods are not transactional by default"

    ReplyDelete
  18. One of most practical views between Java EE and Spring. though Spring is more favorite to me than EE but I really admire your way of describing differences and similarity between both.

    Javin
    10 tips on java debugging with eclipse

    ReplyDelete
  19. Thanks for sharing your info. I really appreciate your efforts and I will be waiting for your further write ups thanks once again.

    ReplyDelete
  20. Java is platform independent language which means you can write your application on one computer and can run that application on all computers. Java support is built into all major OS and popular web browsers, making it available on virtually every Internet-connected computer worldwide. Even electronic devices like mobile phones, set-top boxes and PDAs nowadays come with inbuilt Java applications.

    ReplyDelete
  21. Standards are great for software development, but many times vendor specific extensions make the difference. Oracle's extensions to sql or hibernate's extension to JPA or Primefaces extension to JSF standard are good examples.

    Sometimes, the standard itself is prepared to be extended, but as long as you use those extensions, you are making things in a non-standard way. If you use JBoss Seam 3, for example, you are using JAVAEE6, but you are also using some jboss specific features (those features that make seam excellent instead of simply good), which makes it difficult to swap to another framework (unless you are willing forget about the fancy features and work only with pure standard ones)

    What I mean is that standarization is only one of the many reasons why a piece of software may be chosen, but the standard is not always the best. For example, I feel much more confortable using @Controller and @Scope to declare JSF managed beans instead of the standard @SessionScoped, @ViewScoped JSF annotations, because that way I am able to inject narrower scope beans into broader scope beans (ie, request beans inside session beans). That couples my code to spring annotations, but sometimes the price is worth paying.

    ReplyDelete
  22. Very good compare of the essential - form submittal, return of data and the use of jsp vs facelets.
    I lean towards JSF more just because of the more mature primefaces component library. More pizazzz to pages is easier. I could have said in 2008 or so that I don't like jsp with SpringMVC for view, because of no templating, but right now you could with Tiles in view layer of SpringMVC(Akin to JSF with the default template with faclets and the building of newer GUIs off of that)

    The only bad about JSF is the lack of easy support for REST. SpringMVC provides a zero-config RESTful exposure of its controllers out-of-the-box. However, since I use Spring, I have an easy way out by exposing my services and using CXF for either REST or WS access to my island of logic.
    Also with JSF+primefaces and springMC we can easily port apps to the mobile world.

    So in summary just because of Primefaces components I will always be for the usage of JSF and not SpringMVC. SpringMVC widgets are basic!

    ReplyDelete