You are on page 1of 62

Effective Spring

Craig Walls craig@habuma.com Twitter: @habuma http://github.com/habuma

Who am I?
Java and Spring Fanatic Senior Engineer with SpringSource Spring Social Project Lead Author

Mayor of Post Ofce, Jal NM on Foursquare


Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Recommendations != Best Practices

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: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Consider merging web application contexts

A Tale of Two Contexts

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: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Solution: Put all beans in one context

Congure DispatcherServlet to load an empty context Congure ContextLoaderListener to load all beans

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Empty DispatcherServlet Context


<servlet> <servlet-name>appServlet</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> DispatcherServlet loads an empty application context

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Simple ContextLoaderListener Cong


<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/root-context.xml</param-value> </context-param>

<?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: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Simple ContextLoaderListener Cong


<context-param> <param-name>contextClass</param-name> <param-value>example.RootApplicationContext</param-value> </context-param>

@Configuration @Import({ MvcConfig.class, Neo4jConfig.class, SecurityConfig.class }) @ComponentScan(basePackages="example.config", excludeFilters={ @Filter(Configuration.class)} public class RootApplicationContext { }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Consider using Springs Java-based conguration over XML conguration

The Curse of the Angle-Bracket

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

Not type-safe
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

The Curse of the Angle-Bracket

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

Not refactor-friendly
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

The Curse of the Angle-Bracket

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

XML namespace URIs impossible to remember


Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

The Curse of the Angle-Bracket

<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: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

More Trouble with XML Cong

Not easy to do smart conguration Not easy to test conguration

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Springs Java Conguration


@Configuration public class MyConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.setBar(bar()); return foo; } @Bean public Bar bar() { return new Bar(); } }

Type-safe
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Springs Java Conguration


@Configuration public class MyConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.setBar(bar()); return foo; } @Bean public Bar bar() { return new Bar(); } }

As refactorable as any Java code


Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Springs Java Conguration


@Configuration public class MyConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.setBar(bar()); return foo; } @Bean public Bar bar() { return new Bar(); } }

@Conguration is easy to remember


(@Component also works)
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Springs Java Conguration


@Configuration public class MyConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.setBar(bar()); return foo; } @Bean public Bar bar() { return new Bar(); } }

Still verbose?
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Springs Java Conguration


@Configuration public class MyConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.setBar(bar()); return foo; } @Bean public Bar bar() { return new Bar("Cheers"); } }

Smart Conguration
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Springs Java Conguration


@Configuration public class MyConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.setBar(bar()); return foo; } @Bean public Bar bar() { return new Bar(lookupBarName()); } private String lookupBarName() {...} }

Smart Conguration
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Springs Java Conguration


@Configuration public class MyConfig { ... @Bean public List<Bar> bars() { List<Bar> bars = new ArrayList<Bar>(10); for (int i=0;i<10;i++) { bars.add(new Bar(lookupBarName(i)); } return bars; } private String lookupBarName(int i) {...} }

Smart Conguration
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

But what about XML namespaces?

<tx:annotation-driven /> <mvc:annotation-driven /> <sched:annotation-driven /> <aop:aspectj-autoproxy /> <context:load-time-weaver />

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

@Enable Annotations

@Configuration @EnableWebMvc @EnableTransactionManagement @EnableScheduling @EnableAsync @EnableAspectJAutoProxy @EnableLoadTimeWeaving public class MyConfig { ... }

(Spring 3.1.0 and up)

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Oh yeah...component-scanning

<context:component-scan base-package="com.habuma" />

...becomes...
@Configuration @ComponentScan("com.habuma") public class MyConfig { ... }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Favor component-scanning and autowiring over explicit conguration

Component Scanning
<context:component-scan base-package="com.habuma" />

or
@ComponentScan("com.habuma")

Scans package(s) for @Component classes


@Component public class FooService { ... }

Automatically creates beans in Spring


Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Auto-wiring
Component-scanning also includes auto-wiring (by type)
@Component public class FooService { @Autowired public FooService(Bar bar) { ... } ... }

JSR-330 @Inject also supported


@Named public class FooService { @Inject public FooService(Bar bar) { ... } ... }
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Verbosity in MVC Conguration


<beans ...> <bean id="fooController" class="example.FooController" /> <bean id="barController" class="example.BarController" /> <bean id="bazController" class="example.BazController" /> <bean id="izzController" class="example.IzzController" /> <bean id="ozzController" class="example.OzzController" /> <bean id="uzzController" class="example.UzzController" /> <bean id="zzzController" class="example.ZzzController" /> </beans>
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Annotating Spring MVC Controllers


@Controller @RequestMapping("/foo") public class FooController { private FooService fooService; @Inject public FooController(FooService fooService) { this.fooService = fooService; } ... }

@Controller is a specialized @Component


(and is picked up by component-scanning)
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Component-scanning Controllers
<beans ...> <context:component-scan base-package="example" /> </beans>

or
@Configuration @ComponentScan("example") public class MvcConfig { ... }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Consider using Spring 3.1 proles to handle environment-specic details

Different Beans for Different Envs


<jdbc:embedded-database id="dataSource" type="H2"> <jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:test-data.sql"/> </jdbc:embedded-database>

<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>

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDS" resource-ref="true" proxy-interface="javax.sql.DataSource" />


Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Different Beans for Different Envs


@Bean(destroyMethod="shutdown") public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); } @Bean(destroyMethod="dispose") public DataSource dataSource() { return JdbcConnectionPool.create( environment.getProperty("database.url"), environment.getProperty("database.username"), environment.getProperty("database.password")); } public @Bean DataSource jndiDataSource() { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/myDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); }
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Solution?

Pick a different set of Spring congs at build-time, of course!

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

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: craig@habuma.com 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: craig@habuma.com 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>

...or as a system environment variable, JVM system property, or an entry in JNDI


-Dspring.profile.action=production
Email: craig@habuma.com Twitter: @habuma Blog: http://www.springinaction.com Sample Code: http://github.com/habuma

Consider using Spring Data JPA to create repositories instead of writing them yourself

Ever Written This?


public class ProductDaoImpl implements ProductDao { @PersistenceContext private EntityManager em; public Collection loadProductsByCategory(String category) { Query query = em.createQuery( "from Product as p where p.category = :category"); query.setParameter("category", category); return query.getResultList(); } }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Ever Written This?


public class ProductDaoImpl implements ProductDao { @PersistenceContext private EntityManager em; public Product fetchProductById(Long id) { return em.find(Product.class, id); } public Long save(Product product) { em.persist(product); return product.getId(); } }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Stop Writing Boilerplate Repositories!

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Let Spring Data JPA Handle It


Step 1: Add Spring Data JPA to your project
Maven
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.1.0.RELEASE</version> </dependency>

Gradle
dependencies { compile "org.springframework.data:spring-data-jpa:1.1.0.RELEASE" ... }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Let Spring Data JPA Handle It

Step 2: Add Spring Data JPA to your Spring cong


<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jpa="http://www.springframework.org/schema/data/jpa" ...> <jpa:repositories base-package="com.habuma.samples" /> </beans>

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Let Spring Data JPA Handle It


Step 3: Write your repository interface
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); }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Let Spring Data JPA Handle It


Step 4: There is no step 4 No really...thats all there is to it Just inject and use...
@Controller @RequestMapping("/orders") public class OrdersController { @Inject private OrderRepository orderRepository; @RequestMapping(RequestMethod.GET) public @ResponseBody Iterable<Order> allOrders() { return orderRepository.findAll(); } }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

By the way...

Spring Data also does NoSQL


(Neo4j, MongoDB, CouchDB, etc, etc) More on that in a different session

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

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: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Consider using SpringJUnit4TestRunner to integration-test your beans

Testing With and Without Spring

Two kinds of testing... Unit-testing (Spring-supported, not Spring-involved) Integration-testing (Spring-supported, Spring-involved)

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

How Not to Write Spring Tests


public class SomeTest { private ApplicationContext context; @Before public void setup() { context = new ClassPathXmlApplicationContext( "example/tests/SomeTest-context.xml"); } @Test public testFoo() { Foo foo = context.getBean("foo", Foo.class); // ... do assertions ... } }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

How You Should Write Spring Tests


@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("examples/tests/SomeTest-context.xml") public class SomeTest { @Autowired private Foo foo; @Test public testFoo() { // ... do assertions ... } }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

How You Should Write Spring Tests


@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes="examples.tests.SomeConfig") public class SomeTest { @Autowired private Foo foo; @Test public testFoo() { // ... do assertions ... } }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Consider using Spring Test MVC to test controllers

Testing @Controllers as POJOs


@Controller @RequestMapping("/orders") public class OrdersController { ... @RequestMapping("/{orderId}/items") public String orderItems( @PathVariable("orderId") Long orderId, Model model) { model.addAttribute("lineItems", orderRepository.lineItemsForOrder(orderId); return "lineItems"; } ... }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Testing @Controllers as POJOs

@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 }

Only tests whats inside the handler method

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Testing @Controllers as Controllers

@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 }

Tests Spring MVC stuff and method internals

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Spring Test MVC


https://github.com/SpringSource/spring-test-mvc
Maven
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test-mvc</artifactId> <version>1.0.0.M1</version> </dependency>

Gradle
dependencies { compile "org.springframework:spring-test-mvc:1.0.0.M1" ... }

In Springs milestone repository


http://repo.springsource.org/milestone
Blog: http://www.springinaction.com Email: craig@habuma.com Twitter: @habuma Sample Code: http://github.com/habuma

Consider using Spring Test MVC to test REST client code

Heres the problem...


How can I test this...
public RestTemplate getRestTemplate() { return restTemplate; } public Page getCocaColaPage() { return getRestTemplate().getForObject( "https://graph.facebook.com/coca-cola", Page.class); }

...without hitting the real Facebook Graph API?

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Solution: Mock the Server


@Test public void getCocaColaPage() throws Exception { MockRestServiceServer mockServer = MockRestServiceServer.createServer(facebook.getRestTemplate()); mockServer.expect(requestTo("http://graph.facebook.com/coca-cola") .andExpect(method(GET)) .andRespond(withResponse(jsonResource("cocacola.json"))); Page cocacola = facebook.getCocaColaPage(); Assert.assertEquals("40796308305", cocacola.getId()); ... // more assertions }

Email: craig@habuma.com

Twitter: @habuma

Blog: http://www.springinaction.com

Sample Code: http://github.com/habuma

Q &A

You might also like