Monday, August 26, 2013

Reasons to consider spring-boot for your next Spring based application!

Spring-boot  provides a quick way to create a Spring based application. There are some very compelling reasons to consider spring-boot for your next project:

Reason 1 : Simpler Dependency management using spring-boot starter projects.

Consider the effort required to start up a CRUD web application using Spring-boot, assuming that the CRUD is implemented using a h2 database with Spring-Data providing the data access abstraction. The maven pom required for such a project is the following:

<?xml version="1.0" encoding="UTF-8"?>
<project ...

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>0.5.0.BUILD-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
  <dependency>
   <groupId>org.thymeleaf</groupId>
   <artifactId>thymeleaf-spring3</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jetty</artifactId>
  </dependency>  
  <dependency>
   <groupId>org.hsqldb</groupId>
   <artifactId>hsqldb</artifactId>
   <scope>runtime</scope>
  </dependency>  
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.hamcrest</groupId>
   <artifactId>hamcrest-library</artifactId>
   <scope>test</scope>
  </dependency>      
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>           
        </plugins>
    </build>

</project>


I have removed a few things for brevity.

Now, compare this to a traditional pom.xml where all the dependencies are typically spelled out:

.....

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.11</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.hamcrest</groupId>
   <artifactId>hamcrest-core</artifactId>
   <version>1.2.1</version>
  </dependency>
  <dependency>
   <groupId>org.hamcrest</groupId>
   <artifactId>hamcrest-library</artifactId>
   <version>1.2.1</version>
  </dependency>

  <dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.16</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>${slf4j.version}</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jcl-over-slf4j</artifactId>
   <version>${slf4j.version}</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-log4j12</artifactId>
   <version>${slf4j.version}</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${aspectj.version}</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>${aspectj.version}</version>
  </dependency>
        <!-- dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>        
  <dependency>
   <groupId>net.sf.flexjson</groupId>
   <artifactId>flexjson</artifactId>
   <version>2.0</version>
  </dependency>
  <!-- Spring dependencies -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>${spring.version}</version>
   <scope>test</scope>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aspects</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>${h2.version}</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>${hibernate.version}</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>${hibernate.version}</version>
   <exclusions>
    <exclusion>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
    </exclusion>
    <exclusion>
     <groupId>dom4j</groupId>
     <artifactId>dom4j</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.hibernate.javax.persistence</groupId>
   <artifactId>hibernate-jpa-2.0-api</artifactId>
   <version>1.0.0.Final</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>4.3.0.Final</version>
   <exclusions>
    <exclusion>
     <groupId>javax.xml.bind</groupId>
     <artifactId>jaxb-api</artifactId>
    </exclusion>
    <exclusion>
     <groupId>com.sun.xml.bind</groupId>
     <artifactId>jaxb-impl</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>javax.validation</groupId>
   <artifactId>validation-api</artifactId>
   <version>1.0.0.GA</version>
  </dependency>

  <dependency>
   <groupId>javax.transaction</groupId>
   <artifactId>jta</artifactId>
   <version>1.1</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>commons-pool</groupId>
   <artifactId>commons-pool</artifactId>
   <version>1.5.4</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.4</version>
  </dependency>  
  <dependency>
   <groupId>commons-dbcp</groupId>
   <artifactId>commons-dbcp</artifactId>
   <version>1.3</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
    <exclusion>
     <groupId>commons-pool</groupId>
     <artifactId>commons-pool</artifactId>
    </exclusion>
    <exclusion>
     <groupId>xerces</groupId>
     <artifactId>xerces</artifactId>
    </exclusion>
    <exclusion>
     <groupId>xerces</groupId>
     <artifactId>xercesImpl</artifactId>
    </exclusion>
    <exclusion>
     <groupId>xml-apis</groupId>
     <artifactId>xml-apis</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <!-- Jackson JSON Processor -->
  <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.2.1</version>
  </dependency>
  <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.2.1</version>
  </dependency>
  <dependency>
      <groupId>com.fasterxml.jackson.module</groupId>
      <artifactId>jackson-module-jaxb-annotations</artifactId>
      <version>2.2.1</version>
  </dependency>
  <dependency>
   <groupId>org.apache.tiles</groupId>
   <artifactId>tiles-core</artifactId>
   <version>2.2.1</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.apache.tiles</groupId>
   <artifactId>tiles-jsp</artifactId>
   <version>2.2.1</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${spring.version}</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring.version}</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>commons-digester</groupId>
   <artifactId>commons-digester</artifactId>
   <version>2.0</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.2.1</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>3.1</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>
  <dependency>
   <groupId>javax.el</groupId>
   <artifactId>el-api</artifactId>
   <version>1.0</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>joda-time</groupId>
   <artifactId>joda-time</artifactId>
   <version>1.6</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>jsp-api</artifactId>
   <version>2.1</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-core</artifactId>
   <version>${spring-security.version}</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>${spring-security.version}</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>${spring-security.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-taglibs</artifactId>
   <version>${spring-security.version}</version>
  </dependency>
  <dependency>
      <groupId>com.googlecode.flyway</groupId>
      <artifactId>flyway-core</artifactId>
      <version>1.7</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-jpa</artifactId>
   <version>${spring-data-jpa.version}</version>
  </dependency>   
 
    
 </dependencies>
...

This is indeed a massive improvement in the way Spring specific dependencies are managed.

Reason 2: Quick standalone mode with auto configuration
All that needs to be done to start up a working web application is to run - "mvn spring:boot" and Spring boot does the rest. It provides a way to quickly run the application with an embedded tomcat or jetty container and auto-configures the application based on the libraries available in the classpath without needing any explicit configuration to be provided by the user - for eg. if it finds hsqldb libraries in the classpath it automatically configures a datasource with hsqldb as the embedded database, if it finds thymeleaf jars in the classpath it automatically configures thymeleaf as the templating engine, if it finds hibernate libraries in the classpath then it automatically uses hibernate as the JPA provider. This allows for a progressive enhancement in the development of a working application - Spring boot provides a way for a developer to quickly bootstrap and prototype the application and then slowly replace some of the auto-configured components with more robust alternatives - say a Postgres DB instead of hsqldb etc.

Reason 3: Opinionated Dependencies
Spring boot provides an opinionated and well tested set of dependencies that work well with the Spring ecosystem of projects. for eg. logback with slf4j as the logging dependency, using Servlet 3.0+ apis, Jackson 2.2 for json handling, thymeleaf for web page templating.


Conclusion:
The Spring-boot project is still under heavy development but even in its current state it holds a lot of potential in greatly simplifying application development using Spring umbrella of projects.


Here is a small reference application using Spring-boot - https://github.com/bijukunjummen/spring-boot-mvc-test.git



Monday, August 12, 2013

Spring based applications and logging dependencies

There is not much to say on the topic of Spring based applications and adding logging dependencies apart from pointing to the excellent blog entry at the Spring site itself - http://blog.springsource.org/2009/12/04/logging-dependencies-in-spring/

This is an old entry, but is still valid. The essence of the article is that Spring has one core logging dependency - on Apache Commons Logging. Given this dependency, there are two good options for Spring based applications.

1. Use slf4j with any of the slf4j supporting logging frameworks. This can be done by using the jcl to slf4j bridge library that slf4j provides, along with any of the compile time binding to a logging framework. say for eg, with log4j the following would be the maven dependencies:

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-core</artifactId>
 <version>${spring.version}</version>
 <exclusions>
  <exclusion>
   <artifactId>commons-logging</artifactId>
   <groupId>commons-logging</groupId>
  </exclusion>
 </exclusions>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>jcl-over-slf4j</artifactId>
 <version>1.7.5</version>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-api</artifactId>
 <version>1.7.5</version>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>1.7.5</version>
</dependency>
<dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>1.2.17</version>
</dependency>

There are 5 dependencies here - the commons-logging is being explicitly excluded first, then a jcl-over-slf4j library provides a JCL adapter to the slf4j library, then the slf4j api is required, the binding to log4j and finally the log4j library.


2. A better option is to use logback as the logging library. Logback natively supports slf4j api, so the dependencies is much more simplified:

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-core</artifactId>
 <version>${spring.version}</version>
 <exclusions>
  <exclusion>
   <artifactId>commons-logging</artifactId>
   <groupId>commons-logging</groupId>
  </exclusion>
 </exclusions>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>jcl-over-slf4j</artifactId>
 <version>1.7.5</version>
</dependency>
<dependency>
 <groupId>ch.qos.logback</groupId>
 <artifactId>logback-classic</artifactId>
 <version>1.0.13</version>
</dependency>

References:
Springsource Blog entry by Dave Syer: http://blog.springsource.org/2009/12/04/logging-dependencies-in-spring/

Slf4J legacy bridge: http://www.slf4j.org/legacy.html

Logback logging library: http://logback.qos.ch/

Thursday, August 1, 2013

Spring - Autowiring multiple beans of the same type and @Primary annotation

Consider a simple Spring annotation based context, with two beans with @Service annotation, but of the same type:

@Service 
public class CustomerServiceImpl1 implements CustomerService{
 @Override
 public Customer getCustomer(long id) {
  return new Customer(1, "Test1");
 }
}

and

@Service 
public class CustomerServiceImpl2 implements CustomerService{
 @Override
 public Customer getCustomer(long id) {
  return new Customer(1, "Test1");
 }
}

Now, a client which injects in the CustomerService bean as a dependency will fail, as by default the injection of autowired fields is by type and there are two beans in the context of the same type. An error message along these lines is typically what is seen at startup:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [pkg.CustomerService] is defined: expected single matching bean but found 2: customerServiceImpl2,customerServiceImpl1

A standard fix for this is to inject by bean name instead of by type, this way:

public class ConfigTest
{
    @Autowired
    @Qualifier("customerServiceImpl1")
    private CustomerService customerService;
    ...
}    

Note that I have made use of the bean naming convention for stereotype annotations which is described in more detail here

However there is another way to disambiguate which specific bean to inject in, and this is with the @Primary annotation. This annotation indicates that the target bean should be given precedence when autowiring by type. This begs the question, what happens if more than one bean of the same type is annotated with @Primary, well Spring will raise an exception like before. @Primary is expected to be applied on only bean of a specific type.

@Primary is also supported in Java Configuration, so @Bean methods can also be tagged with @Primary to indicate the higher precedence.

@Configuration
public class TestConfiguration
{
 @Bean
 @Primary
 public CustomerService customerService1() {
  return new CustomerServiceImpl1();
 }
 
 @Bean
 public CustomerService customerService2() {
  return new CustomerServiceImpl2();
 } 
}