01 maja 2009

Z rozdziału 16. o integracji Spring z Grails z DGG2

W rozdziale 16. "Leveraging Spring" książki "The Definitive Guide to Grails, Second Edition" autorzy przedstawiają, czym jest Spring Framework i w jaki sposób Grails czerpie z jego możliwości. Cytując autorów:

"Spring is a huge framework in itself that provides wrappers and utility classes for pretty much every common problem found using Java technology."

Spring jest podstawą technologiczną dla Grails. Stąd też w Grails możemy korzystać z DI (ang. dependency injection), ale wyróżnikiem Grails jest jego przesłonięcie konfiguracji springowej w XML własnym DSLem zwanym Spring DSL.

Typowym użyciem Springa w aplikacji to utworzenie pliku konfiguracyjnego w XML (można również skorzystać z adnotacji) i skonstruować org.springframework.context.ApplicationContext. Ostatnio pisał o tym Piotr Kochański w Wstrzykiwanie zależności czyli Dependency Injection w 9 minut i 59 sekund. Część 4: wykorzystanie Spring-a.

Zamiast wszechogarniającego XMLa Grails udostępnia własny Spring DSL, co w połączeniu z konwencją ponad konfigurację znacząco upraszcza konfigurację Springa. Wystarczy zdefiniować właściwość def storeService w klasie kontrolera, usługi, znacznika czy klasy dziedzinowej, aby Grails automatycznie przekazał (wstrzelił) egzemplarz klasy usługi com.g2one.gtunes.StoreService.

Samodzielne wyszukiwanie ziaren springowych sprowadza się do implementacji org.springframework.context.ApplicationContextAware, co informuje Grails, aby przekazał (wstrzelił) obiekt typu ApplicationContext, np. (Listing 16-3):
 import org.springframework.context.*

class StoreController implements ApplicationContextAware {
ApplicationContext applicationContext


StoreService getStoreService() { applicationContext.getBean("storeService") }
...
}
Jeśli chcielibyśmy zejść na poziom konfiguracji Spring z XML możemy to zrobić z grails-app/conf/spring/resources.xml.

Konfiguracja Springa ze Grails Spring DSL jest możliwa z pomocą grails.spring.BeanBuilder. Grails wczytuje wszystkie skrypty Groovy w katalogu grails-app/conf/spring. Domyślnie jest tam skrypt resources.groovy, którego głównym elementem jest atrybut beans (odpowiada <beans>), któremu z kolei przypisujemy domknięcie. Zdefiniowanie ziarna springowego w Grails Spring DSL to wykonanie metody o nazwie odpowiadającej nazwie ziarna, której argumentem jest nazwa klasy. Kolejnym argumentem jest domknięcie Groovy z parametrem typu org.springframework.beans.factory.support.AbstractBeanDefinition, np. (Listing 16-5):
 myDataSource(org.apache.commons.dbcp.BasicDataSource) { bean ->
bean.destroyMethod = "close"
driverClassName = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:hsql://localhost:9001"
}
Poza atrybutem destroyMethod, który odpowiada atrybutowi destroy-method elementu <bean> w pliku XML, mamy autowire (true, byName, byType lub byConstructor), abstract, dependsOn, factoryBean, initMethod, parent i scope.

Jeśli ziarno nie posiada bezparametrowego konstruktora możemy je utworzyć podając typ i parametry wejściowe konstruktora, np. helloWorldString(String, "hello world!"). Możemy również przekazać listę parametrów wejściowych konstruktora po nazwie, np. theBeansAlbum(Album, title:"The Bends", genre:"Alternative).

Dowolne ziarno definiowane przez Grails możemy przesłonić przez stworzenie ziarna o tej samej nazwie w grails-app/conf/spring/resources.groovy lub w pliku XML. Grails definiuje ziarna: dataSource (typu javax.sql.DataSource), jspViewResolver (org.springframework.web.servlet.ViewResolver), localeResolver (org.springframework.web.servlet.LocaleResolver), messageSource (org.springframework.context.MessageSource), multipartResolver (org.springframework.web.multipart.MultipartResolver), sessionFactory (org.hibernate.SessionFactory) oraz transactionManager (org.springframework.transaction.PlatformTransactionManager).

Każda wtyczka może definiować własne ziarna w domknięciem doWithSpring, który oprogramowujemy ze składnią BeanBuilder.

Powiązania między ziarnami springowymi definiujemy przez przypisanie lub metody ref (dla ziaren jeszcze nie zdefiniowanych lub które są tworzone przez Grails), np. (Listing 16-8):
 mySessionFactory(org.springframework.orm.hibernate3.LocalSessionFactoryBean) {
dataSource = myDataSource
}
lub dataSource = ref("dataSource").

Wewnętrzne ziarna springowe definiujemy przez zanurzenie definicji ziarna ze Spring DSL w ramach definicji innego ziarna, np. (Listing 16-10):
 mySessionFactory(org.springframework.orm.hibernate3.LocalSessionFactoryBean) {
dataSource = { org.apache.commons.dbcp.BasicDataSource bd ->
...
}
}
Przypisanie zasięgu ziarnom to zdefiniowanie bean.scope w ramach jego domknięcia, np.
 frenchCalendar(calendarBean) { bean ->
bean.scope = "prototype"
}
Mamy do dyspozycji prototype, request, flash, flow, conversation, session oraz singleton.

Pamiętajmy, że konfiguracja Springa jest realizowana przez skrypty Groovy, więc mamy do dyspozycji cały arsenał możliwości Groovy do konfiguracji Grails, np. (Listing 16-13):
 def dataSources = [firstDataSource: 9001, secondDataSource: 9002]

dataSources.each { name, port ->
"$name"(org.apache.commons.dbcp.BasicDataSource) { bean ->
bean.destroyMethod = "close"
...
}
}
Przykład prezentuje wykorzystanie wykonanie metod jako parametrów tekstowych z Groovy.

Następnie autorzy prezentują użycie Spring DSL do zdefiniowania MDB i JMS z Apache ActiveMQ. Przykładowa konfiguracja to (Listing 16-15):
 jmsFactory(org.apache.activemq.ActiveMQConnectionFactory) {
brokerURL = "tcp://localhost:61616"
}

jmsTemplate(org.springframework.jms.core.JmsTemplate) {
connectionFactory = jmsFactory
}
Klasy dziedzinowe to również ziarna springowe, więc przy tej konfiguracji wystarczy zadeklarować właściwość transient jmsTemplate (transient, aby egzemplarz JmsTemplate nie został zserializowany).

Jako podsumowanie jak mocna, a wciąż niezauważalna jest integracja między Spring a Grails warto przywołać zdanie z zakończenia rozdziału (str. 516):

"Grails provides such a clean abstraction over Spring that often users of Grails simply don't know Spring is there."