Craig Walls - Effective Spring
Craig Walls - Effective Spring
Who am I?
Java and Spring Fanatic Senior Engineer with SpringSource Spring Social Project Lead Author
The code is more what youd call guidelines than actual rules.
- Captain Hector Barbossa, Pirates of the Caribbean:The Curse of the Black Pearl
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
DispatcherServlet creates an application context So does ContextLoaderListener DispatcherServlet can see ContextLoaderListeners beans ContextLoaderListener cannot see DispatcherServlets beans Confusing, unnecessary, and complicates some things (security)
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Congure DispatcherServlet to load an empty context Congure ContextLoaderListener to load all beans
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
<?xml version="1.0" encoding="UTF-8"?> <beans ...> <import resource="appServlet/servlet-context.xml" /> <import resource="neo4j-context.xml" /> <import resource="security.xml" /> <context:component-scan base-package="com.mouseguests" /> </beans>
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
@Configuration @Import({ MvcConfig.class, Neo4jConfig.class, SecurityConfig.class }) @ComponentScan(basePackages="example.config", excludeFilters={ @Filter(Configuration.class)} public class RootApplicationContext { }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
<beans ...> <bean id="foo" class="example.Foo"> <property name="bar" ref="bar" /> </bean> <bean id="bar" class="example.Bar"/> </beans>
Not type-safe
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
<beans ...> <bean id="foo" class="example.Foo"> <property name="bar" ref="bar" /> </bean> <bean id="bar" class="example.Bar"/> </beans>
Not refactor-friendly
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
<beans ...> <bean id="foo" class="example.Foo"> <property name="bar" ref="bar" /> </bean> <bean id="bar" class="example.Bar"/> </beans>
<beans ...> <bean id="foo" class="example.Foo"> <property name="bar" ref="bar" /> </bean> <bean id="bar" class="example.Bar"/> <bean .../> <bean .../> <bean .../> <bean .../> </beans>
Verbose
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Type-safe
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
Still verbose?
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
Smart Conguration
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
Smart Conguration
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
Smart Conguration
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
<tx:annotation-driven /> <mvc:annotation-driven /> <sched:annotation-driven /> <aop:aspectj-autoproxy /> <context:load-time-weaver />
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
@Enable Annotations
@Configuration @EnableWebMvc @EnableTransactionManagement @EnableScheduling @EnableAsync @EnableAspectJAutoProxy @EnableLoadTimeWeaving public class MyConfig { ... }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Oh yeah...component-scanning
...becomes...
@Configuration @ComponentScan("com.habuma") public class MyConfig { ... }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Component Scanning
<context:component-scan base-package="com.habuma" />
or
@ComponentScan("com.habuma")
Auto-wiring
Component-scanning also includes auto-wiring (by type)
@Component public class FooService { @Autowired public FooService(Bar bar) { ... } ... }
Component-scanning Controllers
<beans ...> <context:component-scan base-package="example" /> </beans>
or
@Configuration @ComponentScan("example") public class MvcConfig { ... }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
<bean id="dataSource" class="JdbcConnectionPool" factory-method="create" destroy-method="dispose"> <constructor-arg value="${database.url}" /> <constructor-arg value="${database.username}" /> <constructor-arg value="${database.password}" /> </bean>
Solution?
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Better Solution
@Configuration @Profile("dev") public class ProductionConfig { @Bean(destroyMethod="shutdown") public DataSource dataSource() { EmbeddedDatabaseFactory factory = new EmbeddedDatabaseFactory(); ... } } @Configuration @Profile("qa") public class QAConfig { @Bean(destroyMethod="dispose") public DataSource dataSource() { return JdbcConnectionPool.create(...); } } @Configuration @Profile("production") public class ProductionConfig { public @Bean DataSource jndiDataSource() { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); ... } }
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
Better Solution
<beans ...> <beans profile="dev"> <jdbc:embedded-database id="dataSource" type="H2"> <jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:test-data.sql"/> </jdbc:embedded-database> </beans> <beans profile="qa"> <bean id="dataSource" class="JdbcConnectionPool" factory-method="create" destroy-method="dispose"> <constructor-arg value="${database.url}" /> <constructor-arg value="${database.username}" /> <constructor-arg value="${database.password}" /> </bean> </beans> <beans profile="production"> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDS" resource-ref="true" proxy-interface="javax.sql.DataSource" /> </beans> </beans>
Email: [email protected] Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma
Activating Proles
spring.profiles.active
and spring.profiles.default
In web.xml
<context-param> <param-name>spring.profiles.default</param-name> <param-value>development</param-value> </context-param> <servlet> <servlet-name>myApp</servlet-name> <servlet-class>org...DispatcherServlet</servlet-class> <init-param> <param-name>spring.profiles.default</param-name> <param-value>development</param-value> </init-param> </servlet>
Consider using Spring Data JPA to create repositories instead of writing them yourself
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Gradle
dependencies { compile "org.springframework.data:spring-data-jpa:1.1.0.RELEASE" ... }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
By the way...
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Back to Step 3
public interface OrderRepository extends JpaRepository<Order, Long> { List<Order> findByCustomer(String customer); List<Order> findByCustomerLike(String customer); List<Order> findByCustomerAndType(String customer, String type); List<Order> findByCustomerLikeAndType(String customer, String type); @Query("select o from Order o " + "where o.customer = 'Chuck Wagon' and o.type = ?1") List<Order> findChucksOrders(String type); }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Two kinds of testing... Unit-testing (Spring-supported, not Spring-involved) Integration-testing (Spring-supported, Spring-involved)
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
@Test public void orderItems() { OrderRepository orderRepo = ...; // mock OrderRepository OrdersController controller = new OrdersController(orderRepo); ModelMap model = new ModelMap(); Assert.assertEquals("lineItems", controller.orderItems(12345L, model); List<LineItem> items = (List<LineItem>) model.asMap().get("lineItems"); Assert.assertEquals(5, items.size()); ... // more assertions }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
@Test public void orderItems() { OrderRepository orderRepo = ...; // mock OrderRepository OrdersController controller = new OrdersController(orderRepo); MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller); mockMvc.perform(get("/orders/12345/items")) .andExpect(view().name("lineItems")) .andExpect(model().attributeExists("lineItems")) .andExpect(...) // more assertions }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Gradle
dependencies { compile "org.springframework:spring-test-mvc:1.0.0.M1" ... }
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Email: [email protected]
Twitter: @habuma
Blog: http://www.springinaction.com
Q &A