Modern Enterprise Application Configuration with Spring

Modern Enterprise Application Configuration with Spring

As delivered at Øredev 2012 in Malmo, Sweden.
- Original session listing: http://oredev.org/2012/sessions/modern-enterprise-application-configuration-with-spring
- Video: https://vimeo.com/54942353

21874091836ab7d3a769c1a89a0702f3?s=128

Chris Beams

November 08, 2012
Tweet

Transcript

  1. None
  2. modern enterprise app config

  3. chris beams

  4. you

  5. github.com/cbeams/modern-config

  6. modern enterprise app config

  7. enterprise think big (big enough to hire own devs) typically

    not tech companies finance, media, insurance, gov't, ...
  8. enterprise application typically long-lived teams change over time tech and

    requirements do too
  9. enterprise application often large ... in size of codebase ...

    in no. of devs
  10. enterprise application legacy and diversity are the rule

  11. enterprise app configuration what components are involved? how are they

    parameterized? what dependencies exist? how are they satisfied?
  12. modern enterprise app config concise comprehensive clear and communicative flexible

    and extensible code-based and typesafe
  13. None
  14. the Spring mission to provide comprehensive infrastructural support for building

    enterprise applications
  15. ok... but what exactly is Spring?

  16. Spring Framework 1.x

  17. (2004)

  18. 2004 spring ~= spring-framework

  19. Spring Framework 3.x

  20. (2012)

  21. 2012 spring != spring-framework

  22. 2012 spring >> spring-framework

  23. spring == spring-framework spring-security spring-integration spring-batch spring-data-* spring-social

  24. spring == spring-roo spring-amqp spring-webflow spring-ws spring-net ...

  25. spring == spring-*

  26. None
  27. None
  28. spring == comprehensive infrastructural support ...and there's a lot of

    infrastructure out there! configuration is just one (important) concern
  29. which brings us back to...

  30. modern enterprise app config

  31. the approach · ·

  32. the approach POJOs + 3rd party components ·

  33. the approach POJOs + 3rd party components configuration instructions ·

  34. the approach POJOs + 3rd party components ↓ configuration ┌─────────────────────┐

    instructions → │ the Spring container│ └─────────────────────┘ ·
  35. the approach POJOs + 3rd party components ↓ configuration ┌─────────────────────┐

    instructions → │ (ApplicationContext)│ └─────────────────────┘ ·
  36. the approach POJOs + 3rd party components ↓ configuration ┌─────────────────────┐

    instructions → │ the Spring container│ └─────────────────────┘ ·
  37. the approach POJOs + 3rd party components ↓ configuration ┌─────────────────────┐

    instructions → │ the Spring container│ └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  38. you get one consistent mechanism for config fail-fast behavior at

    startup dependency injection vs dependency lookup ... leading to simple, testable POJO components ability to enhance behavior through AOP
  39. config style POJOs + 3rd party components ↓ configuration ┌─────────────────────┐

    instructions → │ the Spring container│ └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  40. config style POJOs + 3rd party components ↓ <beans> ┌─────────────────────┐

    XML → │ the Spring container│ └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  41. <beans/>

  42. <beans> </beans>

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

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

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

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

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

    </bean> <bean .../> <bean .../>
  51. <bean .../> </beans>

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

    </bean> <bean .../> <bean .../>
  53. <bean .../> <import resource="more-beans.xml"/> </beans>

  54. <beans> xml simple general purpose flexible

  55. but...

  56. <beans> xml verbose not typesafe wants special tooling

  57. <namespace:*> xml helps

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

  59. <namespace:*> xml concise powerful easier to use

  60. but...

  61. <namespace:*> xml opaque non-extensible difficult to write your own

  62. config style POJOs + 3rd party components ↓ <beans> ┌─────────────────────┐

    XML → │ the Spring container│ └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  63. config style POJOs + 3rd party components ↓ <beans> +

    ┌─────────────────────┐ <namespace:*> → │ the Spring container│ XML └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  64. config style POJOs + 3rd party components ↓ @Component- ┌─────────────────────┐

    scanning → │ the Spring container│ + @Autowired └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  65. @Component public class Foo { // ... } @Component public

    class Bar { private Foo foo;
  66. @Autowired public void setFoo(Foo foo) { this.foo = foo; }

    }
  67. @Component-scanning + @Autowired eliminates a lot of xml really concise,

    convenient now widely used especially for MVC @Controllers
  68. but...

  69. @Component-scanning + @Autowired can't wire up third-party code ambiguities can

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

    class Bar { @Autowired public void setFoo(Foo foo) { ... } }
  71. I mean, if you've got this package com.foo; @Component public

    class Bar { @Autowired public void setFoo(Foo foo) { ... } } ... then this is kind of ironic, right? <context:component-scan base-package="com.foo"/>
  72. config style POJOs + 3rd party components ↓ @Component- ┌─────────────────────┐

    scanning → │ the Spring container│ + @Autowired └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  73. config style POJOs + 3rd party components ↓ @Configuration ┌─────────────────────┐

    + @Bean → │ the Spring container│ └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  74. bean definition @Configuration public class AppConfig { @Bean public QuoteService

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

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

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

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

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

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

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

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

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

    main(String... args) { } }
  84. 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")); } }
  85. 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")); } }
  86. 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 typesafe fashion QuoteService quoteService = ctx.getBean(QuoteService.class); System.out.println(quoteService.currentValue("AAPL")); } }
  87. 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")); } }
  88. @Configuration typesafe, object-oriented can configure any component complete programmatic control

    no special tooling required centralized, blueprint-style
  89. but...

  90. @Configuration no equivalent to <namespace:*/> xml still required for tx

    mgmt, aop, etc
  91. @Enable*

  92. @Enable* (and friends)

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

  94. simple.

  95. if you've got this package com.foo; @Component public class Bar

    { @Autowired public void setFoo(Foo foo) { ... } }
  96. if you've got this package com.foo; @Component public class Bar

    { @Autowired public void setFoo(Foo foo) { ... } } ... then this is kind of ironic ... <context:component-scan base-package="com.foo"/>
  97. I mean, if you've got this package com.foo; @Component public

    class Bar { @Autowired public void setFoo(Foo foo) { ... } } ... but this makes a lot more sense. @ComponentScan("com.foo")
  98. and if you've got this package com.foo; @Repository public class

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

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

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

  102. example @EnableScheduling

  103. some component package com.foo; public class ChatterBox { public void

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

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

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

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

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

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

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

    public ChatterBox chatterBox() { // Spring ensures return new ChatterBox(); // saySomething() method is } // called once per second }
  111. @Enable* benefits easy to see what's going on easy to

    write your own adapts smoothly from simple to sophisticated supported beyond core spring-framework e.g. spring-data's @EnableMongoRepositories
  112. code-based configuration is complete as of Spring Framework 3.1 can

    configure all major container features mix and match styles as desired
  113. but...

  114. but... ?

  115. no buts.

  116. no buts. code-based configuration: can be adopted incrementally but can

    go all the way which means...
  117. None
  118. None
  119. XML IS OVER!

  120. XML IS OVER! for dependency injection

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

  122. XML IS OVER! for Hibernate integration, too

  123. Hibernate 4 support Spring 3.1 builds against Hibernate 4 new

    orm.hibernate4 packaging
  124. familiar? <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>
  125. we can do better than that.

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

    .buildSessionFactory(); }
  127. XML IS OVER!

  128. XML IS OVER! and not just Spring XML

  129. JPA

  130. 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>
  131. say goodbye.

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

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

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

    emf.setDataSource(dataSource()); emf.setPackagesToScan("com.foo.domain"); return emf; }
  135. @Bean public LocalContainerEntityManagerFactoryBean emf() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();

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

  137. web.xml

  138. <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>
  139. gone. (if you've got Servlet 3)

  140. 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("/"); } }
  141. None
  142. WebApplicationInitializer allows code-based config of servlet container builds on Servlet

    3's ServletContainerInitializer auto-detected on Servlet container startup
  143. application-context.xml persistence.xml web.xml

  144. XML IS OVER.

  145. None
  146. None
  147. None
  148. XML IS OVER, if you want it*

  149. *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. These new features just mean that if you don't want XML, you don't have to use it. That's all. kthx, bye.
  150. config style POJOs + 3rd party components ↓ @Configuration ┌─────────────────────┐

    + @Bean → │ the Spring container│ └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  151. config style POJOs + 3rd party components ↓ @Configuration ┌─────────────────────┐

    + @Bean → │ the Spring container│ + @Enable* └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  152. config style POJOs + 3rd party components (hybrid) ↓ <bean>

    + ┌─────────────────────┐ @Component + → │ the Spring container│ @Configuration └─────────────────────┘ ┌─────────────────────────┐ │ fully configured system │ │ ready for use │ └─────────────────────────┘
  153. recommendations

  154. recommendations go for 100% code-based config @Component + @Autowired for

    "your" components @Bean for third-party components @Enable to control framework features
  155. modern enterprise app config concise comprehensive clear and communicative flexible

    and extensible code-based and typesafe
  156. much more Environment and PropertySource APIs bean definition profiles testing

    support for @Configuration classes brand-new spring-scala project spring-integration Scala & Groovy DSLs
  157. check it out everything in this talk available in 3.1

    spring-framework 3.2 RC1 is now out a great time for feedback RC2 in a few weeks GA by year end
  158. get involved everything's on GitHub see CONTRIBUTING.md new Gradle build

    in 3.2 makes it easy
  159. questions?

  160. thanks! @cbeams | cbeams.github.com/modern-config @springframework | github.com/springsource example code: github.com/cbeams/distyles