Configuration Enhancements in Spring 3.1

Configuration Enhancements in Spring 3.1

As presented with Rossen Stoyanchev at SpringOne/2GX 2011 in Chicago, IL

21874091836ab7d3a769c1a89a0702f3?s=128

Chris Beams

October 26, 2011
Tweet

Transcript

  1. Configuration Enhancements in Spring 3.1 Chris Beams SpringSource, VMware Rossen

    Stoyanchev SpringSource, VMware
  2. CHRIS BEAMS

  3. ROSSEN STOYANCHEV

  4. YOU

  5. time machine

  6. Spring 1.0

  7. <beans/>

  8. <beans> </beans>

  9. <beans> <bean id="foo" class="com.foo.Foo"/> </beans>

  10. <beans> <bean id="foo" class="com.foo.Foo"/> <bean id="bar" class="com.foo.Bar"> <property name="foo" ref="foo"/>

    </bean> </beans>
  11. <beans> <bean id="foo" class="com.foo.Foo"/> <bean id="bar" class="com.foo.Bar"> <property name="foo" ref="foo"/>

    </bean> <bean .../> </beans>
  12. None
  13. <beans> <bean id="foo" class="com.foo.Foo"/> <bean id="bar" class="com.foo.Bar"> <property name="foo" ref="foo"/>

    </bean> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> </beans>
  14. None
  15. <beans> <bean id="foo" class="com.foo.Foo"/> <bean id="bar" class="com.foo.Bar"> <property name="foo" ref="foo"/>

    </bean> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> </beans>
  16. <beans> <bean id="foo" class="com.foo.Foo"/> <bean id="bar" class="com.foo.Bar"> <property name="foo" ref="foo"/>

    </bean> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <bean .../> <import resource="more-beans.xml"/> </beans>
  17. "Spring beans XML"

  18. Spring beans XML simple general purpose flexible

  19. But...

  20. Spring beans XML verbose not type-safe special tooling

  21. Spring 2.0

  22. <namespace:*/>

  23. e.g. <tx:annotation-driven/> <aop:aspectj-autoproxy/> <jee:jndi-lookup/> <util:list/>

  24. "Spring XML namespaces"

  25. Spring XML namespaces concise powerful easier to use

  26. But...

  27. Spring XML namespaces opaque non-extensible difficult to write your own

  28. Spring 2.5

  29. @Autowired

  30. @Component

  31. <context:component-scan/>

  32. "Annotation-Driven Injection"

  33. Annotation-Driven Injection really concise, convenient now widely used especially for

    MVC @Controllers
  34. But...

  35. Annotation-Driven Injection can't wire up third-party code ambiguities can arise

    (enter @Qualifier) still requires xml to bootstrap
  36. I mean, if you've got this package com.win; @Component public

    class TehAwesome { @Autowired public void setStuff(Stuff stuff) { ... } }
  37. I mean, if you've got this package com.win; @Component public

    class TehAwesome { @Autowired public void setStuff(Stuff stuff) { ... } } ... then this is kind of ironic, right? <context:component-scan base-package="com.win"/>
  38. (we'll get back to that in a bit)

  39. Spring 3.0

  40. @Configuration

  41. @Bean

  42. (quick refresher)

  43. bean definition @Configuration public class AppConfig { @Bean public QuoteService

    quoteService() { RealTimeQuoteService quoteService = ...; return quoteService; } }
  44. bean definition // @Configuration classes =~ <beans/> documents @Configuration public

    class AppConfig { @Bean public QuoteService quoteService() { RealTimeQuoteService quoteService = ...; return quoteService; } }
  45. bean definition @Configuration public class AppConfig { // @Bean methods

    ~= <bean/> elements @Bean public QuoteService quoteService() { RealTimeQuoteService quoteService = ...; return quoteService; } }
  46. bean definition @Configuration public class AppConfig { @Bean public QuoteService

    quoteService() { RealTimeQuoteService quoteService = // instantiate return quoteService; } }
  47. bean definition @Configuration public class AppConfig { @Bean public QuoteService

    quoteService() { RealTimeQuoteService quoteService = ...; // configure return quoteService; } }
  48. bean definition @Configuration public class AppConfig { @Bean public QuoteService

    quoteService() { RealTimeQuoteService quoteService = ...; return quoteService; // object managed by Spring } }
  49. bean definition @Import(OtherConfig.class) // =~ <import/> @Configuration public class AppConfig

    { @Bean public QuoteService quoteService() { RealTimeQuoteService quoteService = ...; return quoteService; } }
  50. bean definition @Import(OtherConfig.class) @Configuration public class AppConfig { @Autowired QuoteSource

    quoteSource; // from OtherConfig @Bean public QuoteService quoteService() { RealTimeQuoteService quoteService = ...; return quoteService; } }
  51. bean definition @Import(OtherConfig.class) @Configuration public class AppConfig { @Autowired QuoteSource

    quoteSource; @Bean public QuoteService quoteService() { RealTimeQuoteService quoteService = ...; quoteService.setQuoteSource(quoteSource); // inject return quoteService; } }
  52. bootstrap and use public class Main { public static void

    main(String... args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); QuoteService quoteService = ctx.getBean(QuoteService.class); System.out.println(quoteService.currentValue("AAPL")); } }
  53. bootstrap and use public class Main { public static void

    main(String... args) { // bootstrap the Spring container ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); QuoteService quoteService = ctx.getBean(QuoteService.class); System.out.println(quoteService.currentValue("AAPL")); } }
  54. bootstrap and use public class Main { public static void

    main(String... args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); // retrieve the bean we want to use in type-safe fashion QuoteService quoteService = ctx.getBean(QuoteService.class); System.out.println(quoteService.currentValue("AAPL")); } }
  55. bootstrap and use public class Main { public static void

    main(String... args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); QuoteService quoteService = ctx.getBean(QuoteService.class); // use the bean however desired System.out.println(quoteService.currentValue("AAPL")); } }
  56. "Java Configuration"

  57. Java Configuration type-safe, object-oriented can configure any component complete programmatic

    control no special tooling required
  58. But...

  59. Java Configuration no equivalent to <namespace:*/> xml still required for

    tx mgmt, aop, etc no support in TestContext framework
  60. back to the future

  61. Spring 3.1

  62. Environment API

  63. PropertySource API

  64. <beans profile="dev"/>

  65. @Profile("dev")

  66. nested <beans/>

  67. c: namespace

  68. Environment blog post @Profile blog post PropertySource blog post InfoQ

    talk on all the above
  69. testing support

  70. "TestContext Framework"

  71. TestContext Framework Since Spring 2.5 Eases integration testing with Spring

    Support for JUnit and TestNG
  72. @RunWith(SpringJUnit4ClassRunner.class)

  73. @ContextConfiguration

  74. 2.5 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("app-context.xml") public class MyTest { @Autowired MyBean bean;

    @Test public void testXyz() { } }
  75. 2.5 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from app-context.xml @ContextConfiguration("app-context.xml")

    public class MyTest { @Autowired MyBean bean; @Test public void testXyz() { } }
  76. 2.5 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("app-context.xml") public class MyTest { @Autowired MyBean bean;

    // injected from app-context.xml @Test public void testXyz() { } }
  77. 2.5 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("app-context.xml") public class MyTest { @Autowired MyBean bean;

    @Test public void testXyz() { // assertions against bean } }
  78. 3.1 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=AppConfig.class) public class MyTest { @Autowired MyBean bean;

    @Test public void testXyz() { // assertions against bean } }
  79. 3.1 @RunWith(SpringJUnit4ClassRunner.class) // ApplicationContext will be loaded from AppConfig @ContextConfiguration(classes=AppConfig.class)

    public class MyTest { @Autowired MyBean bean; @Test public void testXyz() { // assertions against bean } }
  80. 3.1 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=AppConfig.class) public class MyTest { @Autowired MyBean bean;

    // injected from AppConfig @Test public void testXyz() { // assertions against bean } }
  81. TestContext Framework Fully updated for Spring 3.1 Support for @Configuration,

    @Profile
  82. Much more on testing at Sam's and Rossen's talk on

    Friday at 10:15 Updated TestContext reference documentation Testing with @Configuration classes blog post
  83. @Enable*

  84. @Enable* (and friends)

  85. e.g. @EnableTransactionManagement @EnableAspectJAutoProxy @EnableLoadTimeWeaving @EnableScheduling @EnableAsync @EnableWebMvc @ComponentScan

  86. Simple.

  87. I mean, if you've got this package com.win; @Component public

    class TehAwesome { @Autowired public void setStuff(Stuff stuff) { ... } }
  88. I mean, if you've got this package com.win; @Component public

    class TehAwesome { @Autowired public void setStuff(Stuff stuff) { ... } } ... then this is kind of ironic ... <context:component-scan base-package="com.win"/>
  89. I mean, if you've got this package com.win; @Component public

    class TehAwesome { @Autowired public void setStuff(Stuff stuff) { ... } } ... but this makes a lot more sense. @ComponentScan("com.win")
  90. and if you've got this package com.win; @Repository public class

    WidgetRepo { @Transactional public void add(Widget widget) { ... } }
  91. and if you've got this package com.win; @Repository public class

    WidgetRepo { @Transactional public void add(Widget widget) { ... } } ... then why do this ... <tx:annotation-driven/>
  92. and if you've got this package com.win; @Repository public class

    WidgetRepo { @Transactional public void add(Widget widget) { ... } } ... when you could do this? @EnableTransactionManagement
  93. Example

  94. Example @EnableScheduling

  95. (Trivial) Example @EnableScheduling

  96. some component package com.startup; public class ChatterBox { public void

    saySomething() { System.out.println(randomWord()); } }
  97. some component package com.startup; public class ChatterBox { @Scheduled(fixedRate=1000) public

    void saySomething() { System.out.println(randomWord()); } }
  98. some component package com.startup; public class ChatterBox { // (@Scheduled

    has been @Scheduled(fixedRate=1000) // around since Spring 3.0) public void saySomething() { System.out.println(randomWord()); } }
  99. some component package com.startup; public class ChatterBox { // "hey

    Spring, call this @Scheduled(fixedRate=1000) // method every second" public void saySomething() { System.out.println(randomWord()); } }
  100. configuration package com.startup.config; @Configuration public class Config { @Bean public

    ChatterBox chatterBox() { return new ChatterBox(); } }
  101. configuration package com.startup.config; @Configuration @EnableScheduling public class Config { @Bean

    public ChatterBox chatterBox() { return new ChatterBox(); } }
  102. configuration package com.startup.config; @Configuration @EnableScheduling // look for and process

    @Scheduled public class Config { @Bean public ChatterBox chatterBox() { return new ChatterBox(); } }
  103. configuration package com.startup.config; @Configuration @EnableScheduling public class Config { @Bean

    public ChatterBox chatterBox() { return new ChatterBox(); // saySomething() method } // called once per second }
  104. configuration package com.startup.config; @Configuration @EnableScheduling // ok, so what does

    this do? public class Config { @Bean public ChatterBox chatterBox() { return new ChatterBox(); } }
  105. EnableScheduling.java package org.springframework.scheduling.annotation; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(SchedulingConfiguration.class) @Documented public @interface EnableScheduling

    { }
  106. EnableScheduling.java package org.springframework.scheduling.annotation; @Import(SchedulingConfiguration.class) // this is the key =>

    public @interface EnableScheduling { }
  107. SchedulingConfiguration.java package org.springframework.scheduling.annotation; @Configuration public class SchedulingConfiguration { @Bean(name=SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)

    public ScheduledAnnotationBeanPostProcessor sabpp() { return new ScheduledAnnotationBeanPostProcessor(); } }
  108. SchedulingConfiguration.java package org.springframework.scheduling.annotation; @Configuration public class SchedulingConfiguration { // it's

    just registering the @Scheduled bean // post processor on your behalf public ScheduledAnnotationBeanPostProcessor sabpp() { return new ScheduledAnnotationBeanPostProcessor(); } }
  109. moving parts @Import now supported at annotation level @Enable* is

    just a naming convention @Configuration classes shipped out of the box
  110. design goal Build on existing concepts "@Configuration all the way

    down"
  111. but what about... transparency extensibility flexibility

  112. but what about... transparency ... check extensibility flexibility

  113. but what about... transparency ... check extensibility ... ? flexibility

  114. but what about... transparency ... check extensibility ... ? flexibility

    ... ?
  115. (Not-so-trivial) Example

  116. (Not-so-trivial) Example @EnableWebMvc

  117. Spring MVC Configuration (Quick Refresher)

  118. (1) Default Configuration I.e. blank <name>-servlet.xml DispatcherServlet.properties HandlerMapping, HandlerAdapter ..

    and other types to instantiate by default Add own type .. turns off default types
  119. Default Config Experience Intentionally minimalistic Fairly neutral Flexible Can be

    verbose for common tasks
  120. (2) MVC Namespace Minimize boilerplate for @MVC Targets the 80%

    use case More opinionated Serves as starting point
  121. MVC Namespace Experience Does a number of useful things Minimizes

    boilerplate Transparent .. ? Flexible .. ?
  122. What's Behind... <mvc:annotation-driven /> Find BeanDefinitionParser Read meta-data code How

    to customize? BeanPostProcessor works .. not obvious!
  123. Ease-of-use vs. Control A good starting point is important But

    transparency is key in MVC config So is flexibility
  124. (3) MVC Java Config (Spring 3.1) Designed with preivous experiences

    in mind Match MVC namespace capabilities More transparent More flexible
  125. MVC Java Config Example (Spring 3.1) // Equivalent to <mvc:annotation-driven/>

    @EnableWebMvc @Configuration public class WebConfig { }
  126. MVC Java Config Example (Spring 3.1) // Equivalent to <mvc:annotation-driven/>

    @EnableWebMvc // <-- What's behind ? @Configuration public class WebConfig { }
  127. @EnableWebMvc @Retention(RetentionPolicy.RUNTIME) @Import(DelegatingWebMvcConfiguration.class) @Target(ElementType.TYPE) public @interface EnableWebMvc { }

  128. @EnableWebMvc @Import(DelegatingWebMvcConfiguration.class) public @interface EnableWebMvc { }

  129. <!-- 1. Change ApplicationContext type --> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.

    AnnotationConfigWebApplicationContext </param-value> </context-param> <!-- 2. Point to package with @Configuration classes --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> org.example.somepackage </param-value> </context-param>
  130. Built-in Customizations Implement WebMvcConfigurer Or extend WebMvcConfigurerAdapter Simple, discoverable config

    API Matches MVC namespace
  131. @EnableWebMvc @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override protected

    void addFormatters(FormatterRegistry registry) { // ... } @Override public void addInterceptors(InterceptorRegistry reg){ // Equivalent to <mvc:interceptors> } @Override public void addViewControllers(ViewControllerRegistry reg) { // Equivalent to <mvc:view-controller> } }
  132. Demo https://github.com/SpringSource/greenhouse src/main/webapp/WEB-INF/web.xml com.springsource.greenhouse.config WebMvcConfigurer

  133. Beyond The Common Case WebMvcConfigurer targets the 80% A good

    starting point Like the MVC namespace Transparency .. ? Flexibility .. ?
  134. See Provided Config Inspect WebMvcConfigurationSupport The config imported via @EnableWebMvc

    Class javadoc lists created beans Or read @Bean methods
  135. Switch To Advanced Config Remove @EnableWebMvc Extend WebMvcConfigurationSupport Override the

    same methods as in WebMvcConfigurer Also override any @Bean methods!
  136. @Configuration public class WebConfig extends WebMvcConfigurationSupport { @Override public void

    addInterceptors(InterceptorRegistry reg){ // Equivalent to <mvc:interceptors> } @Override @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { // Create or let "super" create and customize // RequestMappingHandlerAdapter ... } }
  137. Demo https://github.com/rstoyanchev/spring-mvc-31-demo org.springframework.samples.mvc31.config WebMvcConfigurationSupport

  138. @Enable*

  139. benefits easy to see what's going on easy to write

    your own scales smoothly from simple to sophisticated
  140. "Java Configuration" (completed)

  141. Java Configuration (3.1) completes the vision can configure all major

    container features mix and match styles as desired
  142. But...

  143. But... ?

  144. No buts.

  145. No buts. can be adopted incrementally but can go all

    the way which means...
  146. None
  147. None
  148. XML IS OVER!

  149. XML IS OVER! for dependency injection

  150. XML IS OVER! for enabling features of the Spring container

  151. XML IS OVER! for Hibernate integration, too

  152. Hibernate 4 support Spring 3.1 RC1 builds against Hib 4.0.0.CR4

    3.1 GA will sync with 4.0 Final new orm.hibernate4 packaging
  153. Familiar? <bean id="sessionFactory" class="org.sfwk.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource"/> <property name="mappingResources"> <list>

    <value>Person.hbm.xml</value> <value>Account.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.HSQLDialect </value> </property> </bean>
  154. Or even <bean id="sessionFactory" class="org.sfwk.orm.hib3.AnnotationSessionFactoryBean"> <property name="dataSource" ref="myDataSource"/> <property name="annotatedClasses">

    <list> <value>com.foo.Person</value> <value>com.foo.Account</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.HSQLDialect </value> </property> </bean>
  155. Or even <bean id="sessionFactory" class="org.sfwk.orm.hib3.AnnotationSessionFactoryBean"> <property name="dataSource" ref="myDataSource"/> <property name="annotatedClasses">

    <list> <value>com.foo.Person</value> <!-- again the irony --> <value>com.foo.Account</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.HSQLDialect </value> </property> </bean>
  156. We can do better than that.

  157. @Bean public SessionFactory sessionFactory() { return new LocalSessionFactoryBuilder(dataSource()) .addAnnotatedClasses(Person.class, Account.class)

    .buildSessionFactory(); }
  158. LocalSessionFactoryBuilder Extends Hibernate's own Configuration class only in the new

    orm.hibernate4 package
  159. Also...

  160. standalone Hibernate exception translator @Bean public SessionFactory sessionFactory() { return

    new LocalSessionFactoryBuilder(dataSource()) .addAnnotatedClasses(Person.class, Account.class) .buildSessionFactory(); } @Bean public PersistenceExceptionTranslator exTranslator() { return new HibernateExceptionTranslator(); }
  161. XML IS OVER!

  162. XML IS OVER! and not just Spring XML

  163. JPA

  164. persistence.xml <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="..." xsi:schemaLocation="..." version="2.0"> <persistence-unit name="sample"> <jta-data-source>java:/DefaultDS</jta-data-source> <properties>

    <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> </properties> </persistence-unit> </persistence>
  165. say goodbye.

  166. @Bean public LocalContainerEntityManagerFactoryBean emf() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();

    emf.setDataSource(dataSource()); emf.setPersistenceXmlLocation( "classpath:META-INF/persistence.xml"); return emf; }
  167. @Bean public LocalContainerEntityManagerFactoryBean emf() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();

    emf.setDataSource(dataSource()); emf.setPersistenceXmlLocation( "classpath:META-INF/persistence.xml"); // no more! return emf; }
  168. @Bean public LocalContainerEntityManagerFactoryBean emf() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();

    emf.setDataSource(dataSource()); emf.setPackagesToScan("com.win.entity"); return emf; }
  169. @Bean public LocalContainerEntityManagerFactoryBean emf() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();

    emf.setDataSource(dataSource()); // scan classpath for JPA @Entity types emf.setPackagesToScan("com.win.entity"); return emf; }
  170. XML IS OVER! and not just Spring XML

  171. web.xml

  172. <web-app> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/dispatcher-config.xml

    </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <web-app>
  173. Gone. (if you've got Servlet 3)

  174. public class WebInit implements WebApplicationInitializer { @Override public void onStartup(ServletContext

    container) { XmlWebApplicationContext ctx = new XmlWebApplicationContext() ctx.setConfigLocation( "/WEB-INF/spring/dispatcher-config.xml"); ServletRegistration.Dynamic dispatcher = container.addServlet( "dispatcher", new DispatcherServlet(ctx)); dispatcher.setLoadOnStartup(1); dispatcher.addMapping("/"); } }
  175. public class WebInit implements WebApplicationInitializer { @Override public void onStartup(ServletContext

    container) { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext() ctx.register(DispatcherConfig.class); ServletRegistration.Dynamic dispatcher = container.addServlet( "dispatcher", new DispatcherServlet(ctx)); dispatcher.setLoadOnStartup(1); dispatcher.addMapping("/"); } }
  176. WebApplicationInitializer Builds on Servlet 3's ServletContainerInitializer Auto-detected on Servlet container

    startup
  177. application-context.xml persistence.xml web.xml

  178. XML IS OVER.

  179. None
  180. None
  181. None
  182. XML IS OVER, if you want it*

  183. *which means that you really shouldn't worry, because XML support

    is definitely not going anywhere. This is a concern that commonly comes up when we talk about annotations and such, but we've said it before and we'll say it again: XML was, is, and ever will be a first class citizen in Spring. The changes in 3.1 just mean that if you don't want XML, you don't have to use it. That's all. kthx, bye.
  184. 3.1 RC1 is out Now is the time to test

    RC2 in a couple weeks GA by year end
  185. Questions?

  186. Thanks! @cbeams @rstoya05 @springframework this presentation on the web: http://cbeams.github.com/spring-3.1-config