Spring 4

A bit of history

The Spring Framework was first released in 2004; since then there have been significant major revisions:

  • Spring 2.0 provided XML namespaces and AspectJ support
  • Spring 2.5 embraced annotation-driven configuration
  • Spring 3.0 introduced a strong Java 5+ foundation across the framework codebase, and features such as the Java-based @Configuration model.

Version 4.0 is the latest major release of the Spring Framework and the first to fully support Java 8 features. You can still use Spring with older versions of Java, however, the minimum requirement has now been raised to Java SE 6. We have also taken the opportunity of a major release to remove many deprecated classes and methods.

Highlights

  • Java 8
  • Conditional bean definitions
  • Autowiring with Generics
  • New baseline (JDK6) & deprecated stuff drops
  • Spec upgrades: JMS 2.0, JTA 1.2, JPA 1.2, BeanValidation 1.1, JSR-236 Concurrency
  • Lambda support taking place of annonymous function blocks
  • Repeatable annotations (only possible with JDK8)
  • Spring formatting framework supporting new Java Date & Time API
  • Messaging and WebSocket support
  • Testing annotations as Meta Annotations

Java 8 support

  • Support for lambda expressions & method references with Spring callback interfaces.
  • First class support to java.time
  • JSR-310 Date and Time
  • Repeatable annotations
  • Parameter name discovery
  • Compatibility with Java 6 & 7 maintained.

Conditional

Scenario: If ‘servicedefault’ environment variable is set, use Impl1 otherwise use Impl2

Using @Configuration

@Configuration
public static class ContextConfig {
 @Bean
 public CustomerService customerService() {
  if (System.getProperty("servicedefault")!=null) return new CustomerServiceImpl1();
  return new CustomerServiceImpl2();
 }
}

Using BeanProfiles (Spring 3.1)

@Bean
@Profile("default")
public CustomerService service1() { 
	return new CustomerServiceImpl1(); 
}

@Bean
@Profile("prod")
public CustomerService service2() { 
	return new CustomerServiceImpl2(); 
}

Using @Conditional. Depends on a set of Condition classes to specify the predicates.

class HardCodedPropertyPresentCondition implements Condition {
 @Override
 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  return (System.getProperty("servicedefault") != null);
 }
}

class HardCodedPropertyAbsentCondition implements Condition {
 @Override
 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  return (System.getProperty("servicedefault") == null);
 }
}

@Bean
@Conditional(HardCodedPropertyPresentCondition.class)
public CustomerService service1() {
 return new CustomerServiceImpl1();
}

@Bean
@Conditional(HardCodedPropertyAbsentCondition.class)
public CustomerService service2() {
 return new CustomerServiceImpl2();
}

Testing meta annotations

If we discover that we are repeating the following configuration across our JUnit-based test suite:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class OrderRepositoryTests { }

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class UserRepositoryTests { }

We can reduce it by introducing a custom composed annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTest { }

Then we can use our custom @TransactionalDevTest annotation to simplify the configuration of individual test classes:

@RunWith(SpringJUnit4ClassRunner.class)
@TransactionalDevTest
public class OrderRepositoryTests { }

@RunWith(SpringJUnit4ClassRunner.class)
@TransactionalDevTest
public class UserRepositoryTests { }

Groovy Bean Definition DSL

  • Bean configuration using Groovy DSL

Sample:

	def reader = new GroovyBeanDefinitionReader(myApplicationContext)
	reader.beans {
	    dataSource(BasicDataSource) {
	        driverClassName = "org.hsqldb.jdbcDriver"
	        url = "jdbc:hsqldb:mem:grailsDB"
	        username = "sa"
	        password = ""
	        settings = [mynew:"setting"]
	    }
	    sessionFactory(SessionFactory) {
	        dataSource = dataSource
	    }
	    myService(MyService) {
	        nestedBean = { AnotherBean bean ->
	            dataSource = dataSource
	        }
	    }
	}

Removed deprecated stuff

Details

Generics-based injection matching

  • Spring 3.x ignores the generic type.
  • Readability + Expressiveness

@Service
public class MyBookAdminService implements BookAdminService{
	
	@Autowired 
	public MyBookAdminService(MyRepository<Account> repo){
		...
	}
}

@Bean
public MyRepository<Account> myAccountRepository(){
	return new MyAccountRepositoryImpl();
}

Container improvements

  • Composable annotation with overridable attributes (e.g. custom scope annotation with proxyModule attribute)
  • Conditional bean definitions @Conditional (e.g. Enable for specific profiles). Foundation for Spring Boot
  • @Autowired and @Lazy on injection points - Requesting a lazy-initialization proxy individually per injection point
  • Target-class proxies for classes with arbitrary constructors - CGLib proxies using Objenesis, not invoking any constructor

WebSocket, SockJS, and STOMP Messaging

org.springframework.messaging module

  • Message and Channels abstraction
  • Endpoints using generic messaging patterns

Websockets support on Spring MVC:

  • Support for raw WebSocket handling
  • Flexible endpoints through native server support (Tomcat 7/8, Jetty 9, Glassfish 4, Wildfly 8)
  • Transparent SockJS fallback option
  • STOMP for higher-level messaging on top of WebSocket channel

Spring API examples

JDBCTemplate

  • PreparedStatementSetter
    void setValues(PreparedStatement ps) throws SQLException
  • RowMapper
    Object mapRow(ResultSet rs, int rowNum) throws SQLException

Lambda

// V1 - Inline lambda style
JdbcTemplate jt = new JdbcTemplate(datasource);

jt.query(
  "SELECT name, age FROM person WHERE department = ?", 
  ps -> ps.setString(1, "Sales"), 
  (rs, rowNum) -> new Person(rs.getString(1), rs.getInt(2))
)

// V2 style
JdbcTemplate jt = new JdbcTemplate(datasource);

jt.query(
  "SELECT name, age FROM person WHERE department = ?", 
  ps -> {
  	ps.setString(1, "Sales")
  },
  (rs, rowNum) -> {
  	new Person(rs.getString(1), rs.getInt(2))
  }
)

Method reference

public List<Person> getPersonList(String department){
	JdbcTemplate jt = new JdbcTemplate(datasource);
	jt.query(
	  "SELECT name, age FROM person WHERE department = ?", 
	  ps -> ps.setString(1, "Sales"), 
	  this::mapPerson)
	)
}

Person mapPerson(ResultSet rs, int rowNum) throws SQLException{
	return new Person(rs.getString(1), rs.getInt(2));
}

Date and Time

  • Spring formatting framework supports java 8 date and time (JSR-310)
import java.time.*;
import org.springframework.format.annotation.*;

public class Customer{
	// @DateTimeFormat(iso=ISO.DATE)
	private LocalDate birthDate;

	@DateTimeFormat(pattern="M/d/yy h:mm")
	private LocalDate lastContact;

	...
}

Repeatable annotations

// java 7
@Schedules({
	@Scheduled(cron = "0 0 12 * * ?"),
	@Scheduled(cron = "0 0 18 * * ?")
})
public void performTempFileCleanup(){ ... }

// java 8
@Scheduled(cron = "0 0 12 * * ?")
@Scheduled(cron = "0 0 18 * * ?")
public void performTempFileCleanup(){ ... }

Parameter name discovery

  • Advanced reflection support
  • Parameter names on interfaces
  • Spring’s DefaultParameterNameDiscoverer
  • ASM (bytecode manipulation based)
  • Java 8 parameter reflection
@Controller
public class MyMvcController{
	@RequestMapping(value="/books/{id}", method="GET")
	public Book findBook(){
		return this.bookAdminService.findBook(id);
	}
}

Web improvements

  • spring-websocket module
  • spring-messaging module adds support for STOMP

Testing Improvements

  • Almost all annotations in the spring-test module can be used as meta-annotations to create composed annotations (reduce configuration duplication) (e.g., @ContextConfiguration, @WebAppConfiguration, @ContextHierarchy, @ActiveProfiles, etc.)
  • Active Bean definition profiles can be resolved programatically implementing a custom ActiveProfilesResolver and registering it via the resolver attribute of @ActiveProfiles
  • New SocketUtils class was introduced to spring-core. Enables scan free TCP / UDP ports on localhost.
  • org.springframework.mock.web based on the Servlet 3.0 API (e.g., MockHttpServletRequest, MockServletContext, etc.)

Reference

  • http://docs.spring.io/spring/docs/current/spring-framework-reference/html/new-in-4.0.html
  • http://www.infoq.com/presentations/spring-4-java8
  • http://dsyer.com/presos/decks/spring4.html
  • http://www.javacodegeeks.com/2013/10/spring-4-conditional.html
  • http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#integration-testing-annotations-meta