under the hood: using spring in grails
DESCRIPTION
From GR8Conf EU 2012. Read the presentation abstract here: http://gr8conf.eu/Presentations/Under-the-Hood-Spring-in-GrailsTRANSCRIPT
Burt BeckwithSpringSource
Under the Hood:Using Spring in Grails
2CONFIDENTIAL 2CONFIDENTIAL
Who Am I
3CONFIDENTIAL 3CONFIDENTIAL
Who Am I
Java developer for over 13 years
Background in Spring, Hibernate, Spring Security
Grails developer for 5 years
SpringSource employee on the Grails team
Created or reworked over 40 Grails plugins
http://burtbeckwith.com/blog/
https://twitter.com/#!/burtbeckwith
4CONFIDENTIAL 4CONFIDENTIAL
Spring Overview
Main functions of Spring
• Bean container: ApplicationContext and BeanFactory
• Dependency Injection (DI) and Inversion of Control (IoC)
• Proxies
• Transactions
• Security
• Caching
• Event publishing and listening
• Exception conversion
5CONFIDENTIAL 5CONFIDENTIAL
Bean PostProcessors
6CONFIDENTIAL 6CONFIDENTIAL
Bean PostProcessors
o.s.b.factory.config.BeanPostProcessor
• Object postProcessBeforeInitialization(Object bean,
String beanName)
• Object postProcessAfterInitialization(Object bean,
String beanName)
7CONFIDENTIAL 7CONFIDENTIAL
Bean PostProcessors
o.s.b.factory.config.BeanFactoryPostProcessor
• void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory)
8CONFIDENTIAL 8CONFIDENTIAL
Bean PostProcessors
o.s.b.factory.support.BeanDefinitionRegistryPostProcessor
• extends BeanFactoryPostProcessor
• void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry
registry)
9CONFIDENTIAL 9CONFIDENTIAL
Cloud Support Plugin (cloudfoundry, heroku)
dataSourceBean.driverClassName = updatedValues.driverClassName
dataSourceBean.url = updatedValues.url + suffix
dataSourceBean.username = updatedValues.userName
dataSourceBean.password = updatedValues.password
10CONFIDENTIAL 10CONFIDENTIAL
Alternate approach to BeanDefinition modification
def doWithSpring = {
def mybeanDef = delegate.getBeanDefinition('mybean')
mybeanDef.beanClass = NewClass
mybeanDef.propertyValues.add("order", application.config.plugin?.rendering?.order ?: 42)}
● Use loadAfter = ['plugin1', 'plugin2'] to
ensure the bean is loaded
● Only valid in a plugin, not the app's resources.groovy
11CONFIDENTIAL 11CONFIDENTIAL
Bean Aliases
12CONFIDENTIAL 12CONFIDENTIAL
Aliases
As of Grails 2.1 aliases work fully
• You can create aliases pre2.1 but only if defined in the same
resources.groovy or plugin (doWithSpring)
beans = { springConfig.addAlias 'alias', 'realBean'}
13CONFIDENTIAL 13CONFIDENTIAL
Aliases
The cache plugin registers the alias cacheOperationSource for
the bean registered as
org.springframework.cache.annotation.AnnotationCacheOperationSource#0
Can use to have multiple implementations of a bean and choose
one via configuration at startup, e.g. perenvironment or some
other rule
14CONFIDENTIAL 14CONFIDENTIAL
Spring MVC Controllers
15CONFIDENTIAL 15CONFIDENTIAL
Spring MVC
New in Grails 1.2
Annotate src/java or src/groovy classes with @Controller
Add all packages to the grails.spring.bean.packages list in
Config.groovy
• e.g.grails.spring.bean.packages = ['gr8conf.testapp.foo']
16CONFIDENTIAL 16CONFIDENTIAL
Spring MVC
Annotate methods with
@o.s.w.bind.annotation.RequestMapping
@RequestMapping("/mvc/hello.dispatch")public ModelMap handleRequest() { return new ModelMap() .addAttribute("text", "some text") .addAttribute("cost", 42) .addAttribute("config", grailsApplication.getConfig().flatten()));}
17CONFIDENTIAL 17CONFIDENTIAL
Spring MVC
class UrlMappings {
static mappings = { …
"/mvc/hello"(uri:"/mvc/hello.dispatch")
"/mvc/other"(uri:"/mvc/other.dispatch") }}
@RequestMapping URI value must end in .dispatch
Add entries in UrlMappings to create more natural URLs
18CONFIDENTIAL 18CONFIDENTIAL
Spring MVC
Use @Autowired for dependency injection (on fields in Groovy
classes, on setters or constructors in Java)
private GrailsApplication grailsApplication;
@Autowiredpublic void setGrailsApplication(GrailsApplication app) { grailsApplication = app;}
19CONFIDENTIAL 19CONFIDENTIAL
Transactions
20CONFIDENTIAL 20CONFIDENTIAL
Utility Methods
import o.s.t.interceptor.TransactionAspectSupportimport o.s.t.support.TransactionSynchronizationManager
for (sc in grailsApplication.serviceClasses) { def metaClass = sc.clazz.metaClass
…}
21CONFIDENTIAL 21CONFIDENTIAL
Utility Methods
// returns TransactionStatusmetaClass.getCurrentTransactionStatus = { >
if (!delegate.isTransactionActive()) {return null
}TransactionAspectSupport.currentTransactionStatus()
}
22CONFIDENTIAL 22CONFIDENTIAL
Utility Methods
// void, throws NoTransactionExceptionmetaClass.setRollbackOnly = { >
TransactionAspectSupport.currentTransactionStatus() .setRollbackOnly()}
23CONFIDENTIAL 23CONFIDENTIAL
Utility Methods
// returns booleanmetaClass.isRollbackOnly = { >
if (!delegate.isTransactionActive()) {return false
}delegate.getCurrentTransactionStatus().isRollbackOnly()
}
24CONFIDENTIAL 24CONFIDENTIAL
Utility Methods
// returns booleanmetaClass.isTransactionActive = { >
TransactionSynchronizationManager .isSynchronizationActive()}
25CONFIDENTIAL 25CONFIDENTIAL
Proxies
26CONFIDENTIAL 26CONFIDENTIAL
A Simple Proxied Bean – The interface
package gr8conf.eu.spring;
public interface ThingManager {
void method1();
int methodTwo(boolean foo);}
27CONFIDENTIAL 27CONFIDENTIAL
A Simple Proxied Bean – The implementation
package gr8conf.eu.spring;
public class ThingManagerImpl implements ThingManager {
public void method1() {System.out.println("You called method1");
}
public int methodTwo(boolean foo) {System.out.println("You called methodTwo");return 42;
}}
28CONFIDENTIAL 28CONFIDENTIAL
A Simple Proxied Bean – The FactoryBean
package gr8conf.eu.spring;
public class ThingManagerProxyFactoryBean implements FactoryBean<ThingManager>, InitializingBean {
private ThingManager managerProxy; private ThingManager target;
public ThingManager getObject() { return managerProxy; }
public Class<ThingManager> getObjectType() { return ThingManager.class; }
public boolean isSingleton() { return true; }
29CONFIDENTIAL 29CONFIDENTIAL
A Simple Proxied Bean – The FactoryBean
public void setTarget(ThingManager manager) { target = manager;}
public void afterPropertiesSet() {
Assert.notNull(target, "The proxied manager must be set");
Class<?>[] interfaces = { ThingManager.class };
InvocationHandler invocationHandler = new InvocationHandler() { public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
30CONFIDENTIAL 30CONFIDENTIAL
A Simple Proxied Bean – The FactoryBean
System.out.println("Before invoke " + m.getName());
Object value = m.invoke(target, args);
System.out.println("After invoke " + m.getName());
return value; } };
managerProxy = (ThingManager)Proxy.newProxyInstance( ThingManager.class.getClassLoader(), interfaces, invocationHandler); }}
31CONFIDENTIAL 31CONFIDENTIAL
A Simple Proxied Bean – resources.groovy
import gr8conf.eu.spring.ThingManagerimport gr8conf.eu.spring.ThingManagerImplimport gr8conf.eu.spring.ThingManagerProxyFactoryBean
beans = {
realThingManager(ThingManagerImpl) { // properties, etc. }
thingManager(ThingManagerProxyFactoryBean) { bean > // bean.scope = ... // bean.lazyInit = true // target = ref('realThingManager') }}
32CONFIDENTIAL 32CONFIDENTIAL
A Simple Proxied Bean – resources.groovy
import gr8conf.eu.spring.ThingManagerimport gr8conf.eu.spring.ThingManagerImplimport gr8conf.eu.spring.ThingManagerProxyFactoryBean
beans = {
thingManager(ThingManagerProxyFactoryBean) { bean > // bean.scope = ... // bean.lazyInit = true
target = { ThingManagerImpl thing > // properties, etc. } }}
33CONFIDENTIAL 33CONFIDENTIAL
Want More Information?
35CONFIDENTIAL 35CONFIDENTIAL
Thank You