Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Kenneth A. Kousen - Making Java Groovy - 2014.pdf
Скачиваний:
47
Добавлен:
19.03.2016
Размер:
15.36 Mб
Скачать

The Spring framework

This chapter covers

Using Groovy classes in Spring applications

Refreshable beans

Inline scripted beans

The Grails BeanBuilder class

Spring AOP with Groovy classes

As Java frameworks go, Spring is one of the most successful. Spring brought the ideas of dependency injection, complex object lifecycle management, and declarative services for POJOs to the forefront of Java development. It’s a rare project that doesn’t at least consider taking advantage of all Spring has to offer, including the vast array of Spring “beans” included in its library. Spring touches almost every facet of enterprise Java development, in most cases simplifying them dramatically.

In this chapter I’ll look at how Groovy interacts with the Spring framework. As it turns out, Groovy and Spring are old friends. Spring manages Groovy beans as easily as it handles Java. Spring includes special capabilities for working with code from dynamic languages, however, which I’ll review here.

167

www.it-ebooks.info

168

CHAPTER 7 The Spring framework

 

POJO

POGO

 

Java

AOP

Closure

Groovy

coercion

 

 

 

 

Refreshable

Inline scripted

 

 

beans

beans

 

 

BeanBuilder

Spock

 

 

Spring

 

 

 

 

Spring + Groovy

Figure 7.1 Guide to the Spring technologies with Groovy. Spring manages POGOs as easily as POJOs, so the examples include Groovy implementations of both normal beans and aspects. Closure coercion is used to implement the RowMapper interface in a JdbcTemplate. Refreshable beans are Groovy source files that can be modified at runtime. Inline scripted beans are included inside XML configuration files. Grails provides a BeanBuilder class for configuring beans. Finally, Spock has a library that allows it to be used with Spring’s test context feature.

Groovy can be used to implement beans or to configure them. In this chapter I’ll try to review all the ways Groovy can help Spring. Figure 7.1 contains a guide to the technologies discussed in this chapter.

To show how Groovy helps Spring, I need to review what Spring is all about and how it’s used and configured. I’ll start with a simple, but non-trivial, sample application. Rather than show all the pieces (which are in the source code repository for the book), I’ll highlight the overall architecture and the Groovy parts.

7.1A Spring application

For all its benefits, Spring is a hard framework to demonstrate to developers unfamiliar with it. The “Hello, World” application in Spring makes you question why you’d ever want it, because it replaces a couple of lines of simple, easy-to-understand, strongly typed Java with several additional lines of code, plus about 20 lines of XML. That’s not exactly a ringing endorsement.

To see the real value of Spring you have to see a real application, even if it’s simplified in various ways. The following application models the service and persistence layers of an account management application. The presentation layer is arbitrary, so the following code could be used in either a client-side or a server-side application. In this case, I’ll demonstrate the functionality through both unit and integration tests.

www.it-ebooks.info

A Spring application

169

JAVA AND GROOVY SPRING BEANS Rather than build the entire application in Java and then convert it to Groovy as in other chapters, to save space this application mixes both languages. The point is that Spring managed beans can be implemented in either Java or Groovy, whichever is most convenient.

Consider an application that manages bank accounts. I’ll have a single entity class representing an account, with only an id and a balance, along with deposit and withdraw methods.

The next listing shows the Account class in Groovy, which has a serious advantage over its Java counterpart: it makes it easy to work with a java.math.BigDecimal.

Listing 7.1 An Account POGO in Groovy that uses BigDecimal

import groovy.transform.EqualsAndHashCode import groovy.transform.ToString

@EqualsAndHashCode(includes=['id'])

AST transformations

@ToString(includeNames=true) class Account {

Integer id BigDecimal balance

def deposit(amount) { balance += amount

}

 

Using operators

def withdraw(amount) {

 

with BigDecimal

 

 

balance -= amount

 

 

 

 

}

 

 

}

Financial calculations are one of the reasons we need java.math.BigDecimal and java.math.BigInteger. Using BigDecimal keeps round-off errors from being sent into an account where it can accumulate over time.1 It’s easy to show how quickly round-off errors can become a problem. Consider the following two lines:

println 2.0d – 1.1d println 2.0 – 1.1

The first line uses doubles, while the second line uses java.math.BigDecimal. The first evaluates to 0.8999999999999999, while the second evaluates to 0.9. In the double case I’ve only done a single calculation and already I have enough error to show up.

When coding in Java working with BigDecimal is awkward because it’s a class rather than a primitive. That means you can’t use your normal +, *, - operators and have to use the class’s API instead.

1 If you haven’t seen Office Space yet (http://mng.bz/c6o8), you have a real treat ahead of you.

www.it-ebooks.info

170

 

 

 

 

CHAPTER 7 The Spring framework

 

 

 

 

 

 

 

 

 

 

 

 

 

@Service

 

 

<<interface>>

 

@Repository

 

JdbcTemplate

 

AccountService

 

 

AccountDAO

 

JdbcAccountDAO

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Account

 

 

 

 

Embedded

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DB

 

 

 

 

 

 

 

 

 

 

Figure 7.2 A simple account management application. Transactions are demarcated in the service layer. The persistence layer consists of a single DAO class that implements an interface and uses the Spring JdbcTemplate to access an embedded database.

Because Groovy has operator overloading, however, none of that is necessary. I can simply declare the balance to be a BigDecimal, and everything else just works, even if I use the Account class from Java.

One additional comment about the Account: at the moment no constraints are being applied to ensure that the balance stays positive. This is as simple as I can make it, just for exposition purposes.

The overall design for using the Account class is shown in figure 7.2. This is a very simple form of a layered architecture, with transactional support in the service layer and a persistence layer that consists of an interface and a DAO class, discussed shortly.

The persistence layer follows the normal Data Access Object design pattern. The next listing shows a Java interface, called AccountDAO, written in Java.

Listing 7.2 The AccountDAO interface, in Java

package mjg.spring.dao;

import java.util.List;

import mjg.spring.entities.Account;

public interface AccountDAO {

int createAccount(double initialBalance); Account findAccountById(int id); List<Account> findAllAccounts();

void updateAccount(Account account); void deleteAccount(int id);

}

The interface contains typical methods for transferring Account objects to the database and back. There’s a method to create new accounts, update an account, and delete an account; a method to find an account by id; and one to return all the accounts.

The implementation of the interface, using a Groovy class called JdbcAccountDAO, works with the JdbcTemplate from Spring. Rather than show the whole class (which is available in the book source code), let me present just the structure and then emphasize the Groovy aspect afterward. An outline of the class is shown in the following listing.

www.it-ebooks.info

A Spring application

171

Listing 7.3 Implementing the AccountDAO using JdbcTemplate, in Groovy

@Repository

class JdbcAccountDAO implements AccountDAO { JdbcTemplate jdbcTemplate

@Autowired

JdbcAccountDAO(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource)

}

int createAccount(double initialBalance) { ... } void updateAccount(Account account) { ... }

void deleteAccount(int id) { ... }

Account findAccountById(int id) {

String sql = "select * from accounts where id=?" jdbcTemplate.queryForObject(sql,

accountMapper as RowMapper<Account>, id)

}

Template that simplifies JDBC

Closure coercion implementing the interface

List<Account> findAllAccounts() {

String sql = "select * from accounts" jdbcTemplate.query(sql, accountMapper as RowMapper<Account>)

}

def accountMapper = { ResultSet rs, int row ->

new Account(id:rs.getInt('id'),balance:rs.getDouble('balance'))

}

}

The various query methods take an argument of type RowMapper<T>, whose definition is

public interface RowMapper<T> {

T mapRow(ResultSet rs, int rowNum) throws SQLException

}

When you execute one of the query methods in JdbcTemplate, Spring takes the ResultSet and feeds each row through an implementation of the RowMapper interface. The job of the mapRow method is then to convert that row into an instance of the domain class. The normal Java implementation would be to create an inner class called, say, AccountMapper, whose mapRow method would extract the data from the ResultSet row and convert it into an Account instance. Providing an instance of the AccountMapper class to the queryForObject method would then return a single Account. The same instance can be supplied to the query method, which then returns a collection of Accounts.

This is exactly the type of closure coercion demonstrated in chapter 6. A variable called accountMapper is defined and assigned to a closure with the same arguments as the required mapRow method. The variable is then used in both the findAccountById and findAllAccounts methods.

www.it-ebooks.info

172

CHAPTER 7 The Spring framework

There are two uses for Groovy here:

1A Groovy class implemented a Java interface, which makes integration easy and simplifies the code.

2Closure coercion eliminated the expected inner class.

In the example in the book source code I also included the service class referenced in figure 7.2. It uses Spring’s @Transactional annotation to ensure that each method operates in a required transaction. There is nothing inherently Groovy about it, so again I’ll just show an outline of the implementation in the next listing.

Listing 7.4 A portion of the AccountService class in Java

@Service

 

Declarative transactional

 

@Transactional

 

 

 

 

behavior

public class AccountService {

 

 

 

 

 

@Autowired

 

 

 

Injecting

private AccountDAO dao;

 

 

 

 

 

 

the DAO

public double getAccountBalance(int id) { ... }

public double depositIntoAccount(int id, double amount) { ... } public double withdrawFromAccount(int id, double amount) { ... }

public boolean transferFunds(int fromId, int toId, double amount) { Account from = dao.findAccountById(fromId);

Account to = dao.findAccountById(toId); from.withdraw(amount); to.deposit(amount); dao.updateAccount(from); dao.updateAccount(to);

return true;

}

}

The @Autowired annotation is used by Spring to plug in (inject) an instance of a class implementing the AccountDAO interface into the service class. See the Spring documentation2 for more details on autowiring.

The service implementation is in Java mostly because there’s no great advantage to implementing it in Groovy, though I could easily have done so.

The last piece of the puzzle is the Spring bean configuration file. The configuration in the book source code uses a combination of XML and a component scan for the repository and service classes. Again, nothing in it uses Groovy, so I won’t present it here. For the record, the sample uses Spring’s <embedded-database> tag to set up a sample H2 database in memory that is reinitialized on each run. The rest is as described.

Returning now to Groovy, I want to show the Gradle build file in the next listing.

2 http://mng.bz/m9M3

www.it-ebooks.info

A Spring application

173

Listing 7.5 The Gradle build file for the account application

apply plugin:'groovy' apply plugin:'eclipse'

repositories { mavenCentral()

}

def springVersion = '3.2.2.RELEASE' def spockVersion = '0.7-groovy-2.0'

dependencies {

compile "org.codehaus.groovy:groovy-all:2.1.5"

compile "org.springframework:spring-context:$springVersion" compile "org.springframework:spring-jdbc:$springVersion" runtime "com.h2database:h2:1.3.172"

runtime "cglib:cglib:2.2"

testCompile "org.springframework:spring-test:$springVersion" testCompile "org.spockframework:spock-core:$spockVersion" testCompile "org.spockframework:spock-spring:$spockVersion"

}

The build file is typical of projects presented in this book so far. It declares both the Groovy and Eclipse plugins. It uses Maven central for the repository. The dependencies include Groovy and Spock, as usual. Spring is added by declaring the springcontext and spring-jdbc dependencies. Those dependencies wind up adding several other Spring-related JARs. The h2database dependency is used for the H2 driver needed by the embedded database.

One interesting addition is the spock-spring dependency. Spring includes a powerful testing framework of its own, which is based on JUnit and automatically caches the Spring application context. The spock-spring dependency lets Spock tests work with the Spring testing context.

The first test class is a Spock test for the JdbcAccountDAO. The following listing shows some of the tests from the complete set.

Listing 7.6 Spock tests for the JdbcAccountDAO implementation

import spock.lang.Specification;

@ContextConfiguration("classpath:applicationContext.xml")

@Transactional

class JdbcAccountDAOSpec extends Specification { @Autowired

JdbcAccountDAO dao

def "dao is injected properly"() { expect: dao

}

def "find 3 accounts in sample db"() { expect: dao.findAllAccounts().size() == 3

}

www.it-ebooks.info

174

CHAPTER 7 The Spring framework

def "find account 0 by id"() { when:

Account account = dao.findAccountById(0)

then: account.id == 0

account.balance == 100.0

}

// tests for other methods as well

}

The @ContextConfiguration annotation tells the test runner how to find the Spring bean configuration file. Adding @Transactional means that each test runs in a required transaction that (and this is the cool part) rolls back automatically at the end of each test, implying that the database is reinitialized at the beginning of each test. The DAO is autowired into the test class. The individual tests check that all the methods in the DAO work as expected.

The next listing shows the tests for the service class, which includes using the old method from Spock described in chapter 6 on testing.

Listing 7.7 Spock tests for the service class

import spock.lang.Specification

@ContextConfiguration("classpath:applicationContext.xml")

@Transactional

class AccountServiceSpec extends Specification { @Autowired

AccountService service

def "balance of test account is 100"() {

expect: service.getAccountBalance(0) == 100.0

}

// ... other tests as necessary ...

def "transfer funds works"() { when: service.transferFunds(0,1,25.0)

then: service.getAccountBalance(0) ==

old(service.getAccountBalance(0)) - 25.0 service.getAccountBalance(1) ==

old(service.getAccountBalance(1)) + 25.0

}

}

As before, the annotations let the Spock test work with Spring’s test framework, which caches the application context. I used the old operation from Spock to check changes in the account balance after a deposit or withdrawal. No other additions are needed to use Spock with the Spring test context.

This application, though simple, illustrates a lot of Spring’s capabilities, from declarative transaction management to autowiring to simplified JDBC coding to effective

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]