Introducing Spring JavaConfig

Introducing Spring JavaConfig

Slides from presentation originally delivered at SpringOne 2008 in Hollywood FL

21874091836ab7d3a769c1a89a0702f3?s=128

Chris Beams

December 04, 2008
Tweet

Transcript

  1. Introducing Spring JavaConfig Chris Beams Senior Consultant Lead, Spring JavaConfig

    SpringSource December 1-4, 2008 at the Westin Diplomat, Hollywood, FL, USA
  2. Goal

  3. From <bean/>

  4. To @Bean

  5. SpringSource Confidential. Do not distribute without express permission A bit

    about me •Senior Consultant with SpringSource •Project Lead, Spring JavaConfig •Trained 100’s of developers on Spring
  6. SpringSource Confidential. Do not distribute without express permission A bit

    about you (how presumptuous!) •You’re someone who... •Knows something about <beans/> •Dabbled in Spring 2.5’s @DI (@Autowired) •Really likes type-safety •Wants to work in pure Java!
  7. Elevator Pitch

  8. Spring JavaConfig provides the full power of the Spring DI

    container... ...with the familiarity, ease, and refactorable type-safety of pure Java.
  9. Which means

  10. None
  11. But wait... is Spring’s really so bad?

  12. SpringSource Confidential. Do not distribute without express permission Spring IDE

    http://springide.org
  13. SpringSource Confidential. Do not distribute without express permission SpringSource Tool

    Suite •Adds XML intelligence beyond the features in Spring IDE •Especially useful for large teams http://springsource.com/products/sts
  14. SpringSource Confidential. Do not distribute without express permission And all

    the other major IDEs...
  15. SpringSource Confidential. Do not distribute without express permission Namespaces hide

    XML plumbing • Spring 2.0 brought namespaces to the party, taking us from: <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:application.properties"/> </bean> • To: <context:property-placeholder location="classpath:application.properties"/>
  16. SpringSource Confidential. Do not distribute without express permission Namespaces hide

    XML plumbing • And from: <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer"> <property name="destination" ref="orderQueue"/> <property name="messageListener" ref="messageListenerAdapter"/> <property name="connectionFactory" ref="connectionFactory"/> </bean> <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter"> <property name="delegate" ref="orderProcessor"/> <property name="defaultListenerMethod" value="processOrder"/> </bean> • To: <jms:listener-container> <jms:listener destination="queue.order" ref="orderProcessor" method="processOrder"/> </jms:listener-container>
  17. SpringSource Confidential. Do not distribute without express permission Maybe XML

    ain’t so bad after all... • IDE Tooling for Spring XML rocks • Namespaces dramatically improve the Spring XML landscape • More expressive, less verbose • Just ask Spring Security where it’s 200+ lines of config went! • But, XML does still leave a few things to be desired...
  18. "strings"

  19. SpringSource Confidential. Do not distribute without express permission Strings considered

    harmful getBean("foo") == + "strings" !=
  20. (Casting)

  21. SpringSource Confidential. Do not distribute without express permission An anti-refactoring

    twofer Foo foo = (Foo)context.getBean("foo");
  22. SpringSource Confidential. Do not distribute without express permission Ahh... that’s

    better Foo foo = context.getBean(Foo.class);
  23. SpringSource Confidential. Do not distribute without express permission As type-safe

    as it gets • JavaConfig takes full advantage of Java 5.0’s generics to avoid the pitfalls of "strings" and (casting).
  24. SpringSource Confidential. Do not distribute without express permission All Java,

    all the time • Spring XML may be much easier to deal with today • but... • if we could do it all in Java, why wouldn’t we?
  25. It’s all about metadata

  26. SpringSource Confidential. Do not distribute without express permission @nnotations make

    JavaConfig possible <bean id="orderProcessor" class="com.foo.OrderProcessor"/> @Bean OrderProcessor orderProcessor() { ... }
  27. SpringSource Confidential. Do not distribute without express permission @Bean ==

    <bean/> <bean id="orderProcessor" class="com.foo.OrderProcessor" scope="prototype" primary="true"/> @Bean(scope=PROTOTYPE, primary=TRUE) OrderProcessor orderProcessor() { ... }
  28. SpringSource Confidential. Do not distribute without express permission @Configuration ==

    <beans/> <beans default-lazy-init="true" > <bean id="orderProcessor" class="com.foo.OrderProcessor"/> </beans> @Configuration(defaultLazy=TRUE) public class AppConfig { public @Bean OrderProcessor orderProcessor() { ... } }
  29. Time to bootstrap

  30. SpringSource Confidential. Do not distribute without express permission Enter JavaConfigApplicationContext

    ApplicationContext ctx = new ClassPathXmlApplicationContext("app-config.xml"); TransferService service = (TransferService) ctx.getBean("transferService"); service.transfer(50.00, "1", "2"); JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(AppConfig.class); TransferService service = ctx.getBean(TransferService.class); service.transfer(50.00, "1", "2");
  31. DEMO Configuring the Spring container with JavaConfig

  32. Refs and Values

  33. SpringSource Confidential. Do not distribute without express permission Inter-bean references

    <bean id="orderProcessor" class="com.acme.OrderProcessor"> <constructor-arg ref="orderRepository"/> </bean> <bean id="orderRepository" class="com.acme.JdbcOrderRepository"> <constructor-arg ref="dataSource"/> </bean> @Bean public OrderProcessor orderProcessor() { return new OrderProcessor(orderRepository()); } @Bean public OrderRepository orderRepository() { return new JdbcOrderRepository(dataSource()); }
  34. SpringSource Confidential. Do not distribute without express permission Q: What

    to do with literal values? @Bean public OrderRepository orderRepository() { return new JdbcOrderRepository(dataSource()); } @Bean public DataSource dataSource() { jdbcDataSource ds = new jdbcDataSource(); ds.setDatabase("jdbc:hsqldb:hsql://localhost:9001"); ds.setUser("sa"); ds.setPassword(""); return ds; } Hmm... No! No! No!
  35. SpringSource Confidential. Do not distribute without express permission A: Externalize

    ‘em! db.url="jdbc:hsqldb:hsql://localhost:9001" db.user="sa" db.pass="" com/acme/db.properties: @Configuration @PropertiesValueSource("classpath:com/acme/db.properties") public class DataConfig { private @ExternalValue("db.url") String url; private @ExternalValue("db.user") String username; private @ExternalValue("db.pass") String password; @Bean public DataSource dataSource() { jdbcDataSource ds = new jdbcDataSource(); ds.setDatabase(url); ds.setUser(username); ds.setPassword(password); return ds; } }
  36. SpringSource Confidential. Do not distribute without express permission Multiple value

    sources @ExternalValue @PropertiesValueSource @SystemPropertiesValueSource @EnvironmentValueSource
  37. Let’s get @Transactional

  38. SpringSource Confidential. Do not distribute without express permission Enabling @Transactional

    via XML <bean id="transferService" class="com.bank.TransferServiceImpl"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven/> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> ApplicationContext ctx = new ClassPathXmlApplicationContext("app-config.xml"); TransferService service = (TransferService) ctx.getBean("transferService"); service.transfer(50.00, "A123", "C456"); public class TransferServiceImpl implements TransferService { @Transactional public void transfer(double amt, String srcAcctId, String destAcctId) { // transfer amt from src account to dest } } bean is a Spring-generated transactional proxy
  39. SpringSource Confidential. Do not distribute without express permission Enabling @Transactional

    via JavaConfig @Configuration @AnnotationDrivenTx public class AppConfig { @Bean TransferService transferService() { ... } @Bean DataSource dataSource() { ... } @Bean DataSourceTransactionManager transactionManager() { ... } } ctx = new JavaConfigApplicationContext(AppConfig.class); TransferService service = ctx.getBean(TransferService.class); service.transfer(50.00, "A123", "C456"); bean is a Spring-generated transactional proxy public class TransferServiceImpl implements TransferService { @Transactional public void transfer(double amt, String srcAcctId, String destAcctId) { // transfer amt from src account to dest } } Performs the same function as <tx:annotation-driven/>
  40. @Aspect-oriented

  41. SpringSource Confidential. Do not distribute without express permission An @Aspect,

    applied with XML <aop:aspectj-autoproxy/> <bean class="com.acme.PropertyChangeTracker"/> <bean id="foo" class="com.acme.Foo"/> ctx = new ClassPathXmlApplicationContext("app-config.xml"); Foo foo = ctx.getBean("foo"); foo.setCacheSize(100); @Aspect public class PropertyChangeTracker { @Before("execution(void set*(*)) && target(obj) && args(value)") public void trackChange(JoinPoint jp, Object obj, Object value) { logger.info(format("%s property about to change to '%s' on object '%s'", jp.getSignature().getName(), value, obj)); } } bean is a Spring-generated AOP proxy INFO setCacheSize about to change to '100' on object 'foo'
  42. SpringSource Confidential. Do not distribute without express permission An @Aspect,

    applied with JavaConfig @Configuration @AspectJAutoProxy public class AppConfig { @Bean PropertyChangeTracker propertyChangeTracker() { ... } @Bean Foo foo() { ... } } ctx = new JavaConfigApplicationContext(AppConfig.class); Foo foo = ctx.getBean(Foo.class); foo.setCacheSize(100); @Aspect public class PropertyChangeTracker { @Before("execution(void set*(*)) && target(obj) && args(value)") public void trackChange(JoinPoint jp, Object obj, Object value) { logger.info(format("%s property about to change to '%s' on object '%s'", jp.getSignature().getName(), value, obj)); } } bean is a Spring-generated AOP proxy INFO setCacheSize about to change to '100' on object 'foo' Detects any beans that are @Aspect-annotated. Performs same function as <aop:aspectj-autoproxy/>
  43. Mix it up

  44. SpringSource Confidential. Do not distribute without express permission Plays nicely

    with others @Bean + @DI @Bean + <bean/>
  45. SpringSource Confidential. Do not distribute without express permission Bootstrapping @DI

    with XML package com.bank.services; @Service public class TransferService { private final AccountRepository accountRepository; @Autowired public TransferService(AccountRepository accountRepository) { this.accountRepository = accountRepository; } public void transfer(double amt, String srcAcctId, String destAcctId) { ... } } ApplicationContext ctx = new ClassPathXmlApplicationContext("app-config.xml"); TransferService service = (TransferService) ctx.getBean("transferService"); service.transfer(50.00d, "A123", "C456"); <context:component-scan base-package="com.bank.services"/> app-config.xml
  46. SpringSource Confidential. Do not distribute without express permission Bootstrapping @DI

    with JavaConfig @Configuration @ComponentScan("com.bank.services") public class AppConfig { @Bean public AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource()); } } ctx = new JavaConfigApplicationContext(AppConfig.class); TransferService service = ctx.getBean(TransferService.class); service.transfer(50.00d, "A123", "C456"); Scans @Service-annotated TransferService bean Gets injected into TransferService’s @Autowired constructor package com.bank.services; @Service public class TransferService { public @Autowired TransferService(AccountRepository accountRepository) { ... } }
  47. SpringSource Confidential. Do not distribute without express permission An @Autowired

    @Configuration @Configuration @ComponentScan("com.bank.dataaccess") public class AppConfig { private @Autowired AccountRepository accountRepository; public @Bean TransferService transferService() { return new TransferService(accountRepository); } } JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(AppConfig.class); TransferService service = ctx.getBean(TransferService.class); service.transfer(50.00d, "A123", "C456"); Scans and injects @Repository-annotated JdbcAccountRepository bean package com.bank.dataaccess; @Repository public class JdbcAccountRepository { ... }
  48. SpringSource Confidential. Do not distribute without express permission JavaConfig <->

    @DI integration is seamless • @AnnotationDrivenConfig • Enables processing of • @Autowired • JSR-250 annos @PostConstruct, @PreDestroy, @Resource.. • Is the exact analog of <context:annotation-config/> • @ComponentScan • Enables @AnnotationDrivenConfig functionality implicitly • Scans packages for @Component-(meta-)annotated classes • Is the exact analog of <context:component-scan/> • @Configuration classes are subject to being @Autowire’d too!
  49. SpringSource Confidential. Do not distribute without express permission Plays nicely

    with others @Bean + @DI @Bean + <bean/>
  50. SpringSource Confidential. Do not distribute without express permission Bootstrapping JavaConfig

    from XML package com.bank; @Configuration public class DataAccessConfig { public @Bean AccountRepository accountRepository() { return new JdbcAccountRepository(dataSource()); } } ApplicationContext ctx = new ClassPathXmlApplicationContext("app-config.xml"); TransferService service = (TransferService) ctx.getBean("transferService"); service.transfer(50.00d, "A123", "C456"); <bean class="org.springframework.config.java.process.ConfigurationPostProcessor"/> <bean class="com.bank.DataAccessConfig"/> <bean id="transferService" class="com.bank.service.TransferService"> <constructor-arg ref="accountRepository"/> </bean> app-config.xml
  51. SpringSource Confidential. Do not distribute without express permission Bootstrapping XML

    from JavaConfig @Configuration @ImportXml(locations="classpath:com/bank/data-access-config.xml") @AnnotationDrivenInjection public class AppConfig { private @Autowired AccountRepository accountRepository; @Bean public TransferService transferService() { return new TransferService(accountRepository); } } ctx = new JavaConfigApplicationContext(AppConfig.class); TransferService service = ctx.getBean(TransferService.class); service.transfer(50.00d, "A123", "C456"); <bean id="accountRepository" class="com.bank.dataaccess.JdbcAccountRepository"> <constructor-arg ref="dataSource"/> </bean> com/bank/data-access-config.xml
  52. SpringSource Confidential. Do not distribute without express permission JavaConfig <->

    XML integration is seamless • ConfigurationPostProcessor • Allows for bootstrapping JavaConfig from XML • A Spring BeanPostProcessor • Detects any beans that are @Configuration classes • Registers as beans the objects returned from @Bean methods • May have its own namespace support in the future • @ImportXml • Allows for bootstrapping XML from JavaConfig • Perfect for incremental migration to JavaConfig
  53. Modularize

  54. SpringSource Confidential. Do not distribute without express permission Splitting up

    @Bean methods across classes @Configuration public class DataSourceConfig { @Bean public DataSource dataSource() { // create and return a JDBC DataSource } } @Configuration public class RepositoryConfig { @Bean public AccountRepository accountRepository() { return new AccountRepository(dataSource()); } } Wants to reference the DataSourceConfig.dataSource() @Bean method, but can’t!
  55. SpringSource Confidential. Do not distribute without express permission Remember: @Configurations

    can be @Autowired! @Configuration public class DataSourceConfig { @Bean public DataSource dataSource() { // create and return a JDBC DataSource } } @Configuration @AnnotationDrivenConfig public class RepositoryConfig { @Autowired private DataSource dataSource; @Bean public AccountRepository accountRepository() { return new AccountRepository(dataSource); } } new JavaConfigApplicationContext(RepositoryConfig.class, DataSourceConfig.class);
  56. SpringSource Confidential. Do not distribute without express permission Preferred approach

    @Configuration public class DataSourceConfig { @Bean public DataSource dataSource() { // create and return a JDBC DataSource } } @Configuration @AnnotationDrivenConfig public class RepositoryConfig { @Autowired private DataSourceConfig dataSourceConfig; @Bean public AccountRepository accountRepository() { return new AccountRepository(dataSourceConfig.dataSource()); } } new JavaConfigApplicationContext(RepositoryConfig.class, DataSourceConfig.class); •100% refactorable •Easy navigation within the IDE
  57. SpringSource Confidential. Do not distribute without express permission @Import @Configuration

    public class DataSourceConfig { @Bean public DataSource dataSource() { // create and return a JDBC DataSource } } @Configuration @Import(DataSourceConfig.class) @AnnotationDrivenConfig public class RepositoryConfig { @Autowired DataSourceConfig dataSourceConfig; @Bean public AccountRepository accountRepository() { return new AccountRepository(dataSourceConfig.dataSource()); } } @Import informs JavaConfig about DataSourceConfig... ...so we only need to specify one @Configuration class when creating the context new JavaConfigApplicationContext(RepositoryConfig.class);
  58. SpringSource Confidential. Do not distribute without express permission Rich support

    for modular @Configurations •JavaConfig builds on @DI for it’s approach to modularity •Use @Autowired, @AnnotationDrivenConfig •JavaConfigApplicationContext •Allows for passing multiple @Configuration classes to constructor •@Import •Allows for composition of @Configuration classes •Analogous to <import/> •Autowire @Configuration beans for best effect •More refactorable •Easier navigation / discoverability
  59. What about webapps?

  60. SpringSource Confidential. Do not distribute without express permission The usual

    approach <web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/application-config.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> </web-app> JVM Servlet Container <bean/> <bean/> <bean/>
  61. SpringSource Confidential. Do not distribute without express permission First-class support

    for web applications <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.config.java.context.JavaConfigWebApplicationContext </param-value> </context-param> JVM Servlet Container <web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value> </param-value> </context-param> <listener> <listener-class> org.spring...ContextLoaderListener </listener-class> </listener> </web-app> com.foo.AppConfig @Bean @Bean @Bean
  62. DEMO Converting Spring’s PetClinic application from XML configuration to JavaConfig

  63. Roadmap

  64. SpringSource Confidential. Do not distribute without express permission Short-term •JavaConfig

    is released at Milestone 4 (1.0.0.M4) •Work is underway on M5 •M5 will track Spring 3.0 •Spring 3.0 will include core elements of JavaConfig •JavaConfig 1.0 GA will release with (or shortly after) Spring 3.0 GA
  65. Summary

  66. SpringSource Confidential. Do not distribute without express permission Summary •

    JavaConfig allows for pure-Java, 100% XML-free configuration • No more (casting), no more "strings" • Works seamlessly with other configuration styles • Declarative support for transaction management, AOP, JMX & more • First-class support for web application development • Object-oriented configuration allows for new possibilities • Open for extension • The future looks bright for JavaConfig! • Integration into Spring 3.0
  67. Q&A

  68. Thank you! chris.beams@springsource.com http://springframework.org/javaconfig http://twitter.com/javaconfig