SpringOne 2GX 2011

Chicago, October 25-28, 2011

Using Groovy, Spring and JavaConfig

Posted by: Thomas Risberg on 2007-04-01 00:15:00.0

Last week Romain Guy blogged about using Groovy and JPA together. This was followed by a blog entry from Guillaume Laforge where he combined Groovy and Guice. Both of these examples require the new support for annotations that can be found in recent SNAPSHOT builds of Groovy 1.1. So how about trying this out for an application configured using Spring? Well, that's what I have been playing around with recently and it's worked very well so far.

I must say that I was pleasantly surprised by how well this worked, considering that the annotation support in Groovy is very recent. Not sure how well this approach would work in a web application since we would have to find a way of either compiling the Groovy classes or loading them dynamically maybe using Spring's dynamic language support. I'll get back to this in the near future. For now, what I have been experimenting with works in a standalone application started using the "groovy" script provided in the Groovy download. See these instructions for how to install Groovy 1.0 and Romain's blog entry for instructions how to upgrade to the Groovy 1.1 SNAPSHOT.

The application I built is deliberately very simple, but it's a multi-layered application so we get to use the common techniques used for more complex applications like dependency injection, transaction support, data access support etc. It all starts with a class called MyApp.groovy. It has a reference to a component named MyService with a method called AddAPerson. This method takes a Person object as the only parameter. This is the entire application for now, but to get all this working we are going to need quite a few pieces.

MyApp.groovy

class MyApp {

    def MyService service

    void run() {
        assert service != null
        Person p = new Person()
        p.name = "Thomas"
        service.addAPerson(p)
    }

}

So what does the service look like? Well, it's again very simple - just one method that calls into the data repository layer to persist the Person object. In the implementation class I'm using the @Transactional annotation from Spring to provide transaction support for the service methods. We will see later how this is applied in the Spring configuration. Since we are using Groovy, there is no need to provide setters/getters for the properties. This makes the code much cleaner and we can still inject the dependencies using regular Spring bean configuration.

MyService.groovy

public interface MyService {

    void addAPerson(Person p)

}

MyServiceImpl.groovy

import org.springframework.transaction.annotation.Transactional

@Transactional
class MyServiceImpl implements MyService {

    def PersonRepository personRepository

    void addAPerson(Person p) {

        personRepository.add(p)

    }

}

Moving on to the data access layer I have defined a repository interface for the add(Person p) method.

PersonRepository.groovy

interface PersonRepository {

    void add(Person p)

}

To implement the data access I decided to again use JPA since it worked well for Romain. Here is the repository implementation followed by the Person class with the JPA annotations. There are two annotations in the repository implementation. The first is @Repository which is a Spring annotation providing exception translation for data access code. In this case any JPA exceptions will be translated to a corresponding exception from the Spring DataAccessException hierarchy. This is beneficial since you can rely on only dealing with one type of exceptions even if you have a mix of data access technologies in your data access layer. The second annotation is @PersistenceContext and that is a JPA annotation used to inject an EntityManager.

PersonRepositoryImpl.groovy

import javax.persistence.*
import org.springframework.stereotype.Repository;

@Repository
class PersonRepositoryImpl implements PersonRepository {

    @PersistenceContext
    EntityManager entityManager

    void add(Person p) {
        entityManager.persist(p)
    }

}

Person.groovy

import javax.persistence.*

@Entity
class Person {

    @Id @GeneratedValue
    Long id

    String name

}

This is all the code we need for this application for now. Next we will look at what is needed to wire this application up and execute it. Keep in mind that the amount of code needed for configuration seems larger than the application code for now, but if we built more functionality for the application itself we would still have about the same amount of configuration code.

First we need a class that will bootstrap the whole application - let's call this class MyAppStart.groovy. It's a simple script that prints the Groovy version we are using, loads up a Spring application context containing the data access configuration. This context is used as the parent context for the application context that wires up the application and the service layer. Then the "myApp" bean is retrieved and we run the application. To keep things interesting I decided to use the new, still experimental, Spring JavaConfig for this second context. Spring JavaConfig let's you configure Spring using Java and annotations rather than XML. Craig Walls did blog about using Spring JavaConfig and Guice recently.

MyAppStart.groovy

import org.springframework.context.*
import org.springframework.context.support.*
import org.springframework.context.java.*

println "Groovy " + org.codehaus.groovy.runtime.InvokerHelper.getVersion()

ApplicationContext pac = new ClassPathXmlApplicationContext("spring-data-access.xml")
AnnotationApplicationContext aac = new AnnotationApplicationContext(pac)
aac.setConfigClasses((Class[])[MyConfig.class])
aac.refresh()

app = aac.getBean("myApp")
app.run()

The "spring-data-access.xml" context file is a standard JPA configuration with the following entries

  • personRepository - a singleton bean for the PersonRepository implementation
  • PersistenceExceptionTranslationPostProcessor - the bean responsible for handling the @Repository annotation
  • PersistenceAnnotationBeanPostProcessor - the bean handling the dependency injection for the @PersistenceContext annotation
  • entityManagerFactory - the factory used to create the EntityManager used by the previous bean. Here we specify Hibernate (using Hibernate 3.2 with JPA support from Hibernate-Annotations and Hibernate-EntityManager projects) as the persistence provider and supply any properties needed for DDL generation and the database dialect. (The H2 database is my current favorite - comes with a very nice web based management console app)
  • transactionManager - the TransactionManager used for the JPA implementation
  • dataSource - a DataSource and connection pool
  • propertyConfigurer - the bean responsible for replacing the jdbc property placeholders with values from jdbc.properties file

spring-data-access.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

  <bean name="personRepository" class="PersonRepositoryImpl" autowire="byName"/>

  <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

  <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

  <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="persistenceUnitName" value="test"/>
    <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="showSql" value="false"/>
        <property name="generateDdl" value="true"/>
        <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect"/>
      </bean>
    </property>
  </bean>

  <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
  </bean>

  <bean id="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="${jdbc.driverClassName}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    <property name="minPoolSize" value="2"/>
    <property name="maxPoolSize" value="15"/>
    <property name="maxStatements" value="50"/>
  </bean>

  <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="jdbc.properties"/>
  </bean>

</beans>

To complete the JPA configuration we also need a META-INF/persistence.xml file. The one I'm using is minimal, just containing the list of persistent classes and the persistence unit name and transaction type.

META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
    <class>Person</class>      
  </persistence-unit>
</persistence>

Now to the more interesting configuration for the application and the service layer. Like I mentioned earlier I am using the Spring JavaConfig support here. The class is annotated with @Configuration to indicate that this is indeed a spring application context configuration. Three beans are defined using the @Bean annotation. The first one is the "myApp" bean that is the application itself and it gets the service injected. Second bean is "myService" and it is auto-wired by type which will result in the "personRepository" (from the data access application context loaded above) being injected. Third bean is the "transactionInterceptor" that also has a @SpringAdvice annotation defining the pointcut for where it will be applied. This is defined using AspectJ pointcut syntax as any class or method annotated with the @Transactional annotation.

MyConfig.groovy

import org.springframework.beans.factory.annotation.*
import org.springframework.beans.factory.java.template.*
import org.springframework.transaction.*
import org.springframework.transaction.annotation.*
import org.springframework.transaction.interceptor.*

@Configuration
class MyConfig extends ConfigurationSupport {

    @Bean
    MyApp myApp() {
        MyApp myApp = new MyApp()
        myApp.service = myService()
        return myApp
    }

    @Bean(autowire = Autowire.BY_TYPE)
    MyService myService() {
        MyServiceImpl myService = new MyServiceImpl()
    }

    @Bean
    @SpringAdvice(
        "execution(public * ((@org.springframework.transaction.annotation.Transactional *)+).*(..)) || \
         execution(* *(..)) && @annotation(org.springframework.transaction.annotation.Transactional)")
    TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor txInterceptor = new TransactionInterceptor(
            (PlatformTransactionManager) getBean("transactionManager"),
            new AnnotationTransactionAttributeSource())
    }

}

OK, almost done, I promise. We just need a script to set up the classpath and run the application.

HIBERNATE_CLASSPATH=../hibernate/commons-collections-2.1.1.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/cglib-nodep-2.1_3.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/dom4j-1.6.1.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/hibernate-annotations.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/hibernate-entitymanager.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/hibernate3.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/javassist.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/jboss-archive-browsing.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/jta.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/ejb3-persistence.jar
HIBERNATE_CLASSPATH=$HIBERNATE_CLASSPATH:../hibernate/c3p0-0.9.0.4.jar
APP_CLASSPATH=../lib/spring.jar
APP_CLASSPATH=$APP_CLASSPATH:../lib/spring-javaconfig.jar
APP_CLASSPATH=$APP_CLASSPATH:../lib/aspectjweaver.jar
APP_CLASSPATH=$APP_CLASSPATH:../lib/aspectjrt.jar
APP_CLASSPATH=$APP_CLASSPATH:../lib/h2.jar
groovy -cp $APP_CLASSPATH:$HIBERNATE_CLASSPATH:. MyAppStart
NOTE: Since Hibernate depends on CGLIB which depends on ASM 1.5.3 and Groovy ships with ASM 2.2 there is a bit of a conflict there. This is easiest solved by using the cglib-nodep-2.1_3.jar from the Spring distribution and leaving out the asm and cglib jars from the Hibernate distribution

Oh, and just to "prove" that this actually works - here are some of the log entries when I run this application:

Groovy 1.1-SNAPSHOT
2007-04-01 00:46:50,246 INFO [org.springframework.core.CollectionFactory] - JDK 1.4+ collections available
2007-04-01 00:46:50,279 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring-data-access.xml]
...
2007-04-01 00:46:50,777 INFO [org.springframework.context.support.ClassPathXmlApplicationContext] - 7 beans defined in application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=1688215]
2007-04-01 00:46:50,861 INFO [org.springframework.beans.factory.config.PropertyPlaceholderConfigurer] - Loading properties file from class path resource [jdbc.properties]
...
2007-04-01 00:46:51,196 INFO [org.hibernate.ejb.Version] - Hibernate EntityManager 3.2.1.GA
2007-04-01 00:46:51,208 INFO [org.hibernate.cfg.annotations.Version] - Hibernate Annotations 3.2.1.GA
2007-04-01 00:46:51,212 INFO [org.hibernate.cfg.Environment] - Hibernate 3.2.2
2007-04-01 00:46:51,215 INFO [org.hibernate.cfg.Environment] - hibernate.properties not found
2007-04-01 00:46:51,216 INFO [org.hibernate.cfg.Environment] - Bytecode provider name : cglib
2007-04-01 00:46:51,221 INFO [org.hibernate.cfg.Environment] - using JDK 1.4 java.sql.Timestamp handling
2007-04-01 00:46:51,287 INFO [org.hibernate.ejb.Ejb3Configuration] - Processing PersistenceUnitInfo [
        name: test
        ...]
2007-04-01 00:46:51,319 INFO [org.hibernate.cfg.Configuration] - Reading mappings from resource : META-INF/orm.xml
2007-04-01 00:46:51,320 INFO [org.hibernate.ejb.Ejb3Configuration] - [PersistenceUnit: test] no META-INF/orm.xml found
2007-04-01 00:46:51,356 INFO [org.hibernate.cfg.AnnotationBinder] - Binding entity from annotated class: Person
2007-04-01 00:46:51,381 INFO [org.hibernate.cfg.annotations.EntityBinder] - Bind entity Person on table Person
2007-04-01 00:46:51,476 INFO [org.hibernate.connection.ConnectionProviderFactory] - Initializing connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
2007-04-01 00:46:51,480 INFO [org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider] - Using provided datasource
...
2007-04-01 00:46:51,663 INFO [org.hibernate.cfg.SettingsFactory] - RDBMS: H2, version: 1.0 (2007-01-30)
2007-04-01 00:46:51,663 INFO [org.hibernate.cfg.SettingsFactory] - JDBC driver: H2 JDBC Driver, version: 1.0 (2007-01-30)
2007-04-01 00:46:51,674 INFO [org.hibernate.dialect.Dialect] - Using dialect: org.hibernate.dialect.H2Dialect
...
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.TableMetadata] - table found: KICKSTART.PUBLIC.PERSON
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.TableMetadata] - columns: [name, id]
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.TableMetadata] - foreign keys: []
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.TableMetadata] - indexes: [primary_key_1]
2007-04-01 00:46:51,997 INFO [org.hibernate.tool.hbm2ddl.SchemaUpdate] - schema update complete
...
2007-04-01 00:46:52,397 INFO [org.springframework.context.java.AnnotationApplicationContext] - 4 beans defined in application context [org.springframework.context.java.AnnotationApplicationContext;hashCode=4136327]
...
2007-04-01 00:46:52,865 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Initializing transaction synchronization
2007-04-01 00:46:52,866 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] - Getting transaction for [MyServiceImpl.addAPerson]
...
2007-04-01 00:46:52,893 DEBUG [org.hibernate.SQL] - insert into Person (id, name) values (null, ?)
...
2007-04-01 00:46:52,937 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] - Completing transaction for [MyServiceImpl.addAPerson]
2007-04-01 00:46:52,940 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Clearing transaction synchronization

About Thomas Risberg

Thomas Risberg

Thomas has been a developer on the Spring Framework project since early 2003, contributing to enhancements of the JDBC framework portion.

Thomas currently works as a consultant for SpringSource specializing in Java EE and database projects. He has been involved with developing database applications, both as a DBA and as an application developer for over 20 years, using a wide variety of languages and databases.

Thomas is co-author of "Professional Java Development with the Spring Framework" together with Rod Johnson, Juergen Hoeller, Alef Arendsen, and Colin Sampaleanu, published by Wrox in 2005.

More About Thomas »

NFJS, the Magazine

2011-10-01 00:00:00.0 Issue Now Available
  • Plugging into Gradle Plugins

    by Tim Berglund
  • Enterprise Integration Agility

    by Jeremy Deane
  • Relax with Couch DB

    by Johnny Wey
  • Sass...CSS Evolved

    by Mark Volkmann
Learn More »