Using Spring with Scala (Spring I/O 2016)

Summary: Demonstrating the steps that are necessary to use Spring with Scala by translating Spring.io’s guides from Java to Scala.


This is the content of my talk I gave at the Spring I/O conference in Barcelona (19.&20.05.2016). Slides can be found here.

Scala is becoming a popular choice when developing for the JVM. However, developers with a Spring/Java background might be reluctant to give up all of their familiar tools and frameworks.

In this post I’d like to demonstrate how to introduce Scala into an existing Spring/Java environment.

But why?

Using Spring with Scala feels a bit like this:

Sneaking Scala into a Java world

It seems I’m trying to sneak in Scala into the harmonic marriage of Spring with Java. A valid question therefore is: why should someone want to do this?

I think there are good reasons.

Why Scala?

I could also ask: why learn a new programming language? There are probably two reasons:

  • For fun: learning a new programming language makes me a better developer
  • For profit: your boss might want to use it, the job market has an increasing number of Scala projects etc.

Scala was built for functional programming from ground up, where for Java it’s been added on top of it. And that shows up in code, many times solutions can be expressed more concisely in Scala.

Why Spring + Scala?

Possible reasons why this might be a good idea:

  • You come from a Java/Spring background and think it sufficient in the beginning to learn a new language. Why drop also all the tools that you know and are efficient in (I for example absolutely love Spring Boot, I don’t want to give it up).
  • New languages come always with new frameworks and tools. Those are that: new and unproven. There is some risk involved using them. Spring has been around for a very long time and is proven and comes with support.
  • Probably the most common reason: you and your team want to gradually introduce Scala into your existing Java/Spring project. This is without problems possible.

Examples

In order to demonstrate how we can use Scala with Spring I went to the guide section of Spring.io1, selected a few of the guides and translated them into Scala.

The source code of the examples can be found as usual on Github2.

The Spring.io guides have the advantage of being short, yet showcasing distinct features of Spring.

First example: serving websites using Spring MVC

The Spring MVC example consists basically of four files:

.
├── build.gradle
└── src
    └── main
        ├── java
        │   └── hello
        │       ├── Application.java
        │       └── GreetingController.java
        └── resources
            └── templates
                └── greeting.html

We have our build file (Gradle), the template, the Spring main application and a controller (we don’t have a model in this simple example).

Making Gradle work with Scala

In order to make Gradle work with Scala, we just need to add the Scala plugin and add the dependency to Scala.

apply plugin: 'scala'
apply plugin: 'application'

mainClassName = "hello.Application"

dependencies {
    compile 'org.scala-lang:scala-library:2.11.1'
}

I’ve also added the application plugin so that I can execute the examples using gradle run but that is optional.

That’s all that is necessary to build & run the examples.

Application class

The Java application class is a typical Spring Boot application (Application.java):

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

This becomes following in Scala (Application.scala):

@SpringBootApplication
class Application {
}

object Application extends App {
  SpringApplication.run(classOf[Application], args:_*)
}

We don’t have static methods in Scala, so we add an object to our main class. We can use Java/Spring annotations like @SpringBootApplication in Scala the same way as in Java (one exception see below).

We only need to pass our arguments as args:_* (basically a compiler hint).

That’s all there is to do. This is true for the rest of the examples.

Template

Of course, no changes need here, just to demonstrate how this example works, the template (template.html) looks like this:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head> 
    <title>Getting Started: Serving Web Content</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>

We pass a name parameter to the template.

Controller

The Java side is a typical MVC controller (GreetingController.java):

@Controller
public class GreetingController {
    @RequestMapping("/greeting")
    public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
        model.addAttribute("name", name);
        return "greeting";
    }

}

In Scala, we only need to handle the @RequestMapping annotation differently. Instead of passing just a String, we need to wrap it into an Array("/greeting") (file GreetingController.scala):

@Controller
class GreetingController {
  @RequestMapping(Array("/greeting"))
  def greeting(@RequestParam(value="name", required=false, defaultValue="World") name: String, model: Model) : String = {
    model.addAttribute("name", name)
    "greeting"
  }
}

That’s it. We can now run the project and ping the endpoint:

$ gradle run 
...
2016-05-06 16:05:52.644  INFO 1297 --- [  restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2016-05-06 16:05:52.650  INFO 1297 --- [  restartedMain] scala.App$class                          : Started App.class in 5.117 seconds (JVM running for 5.753)

$ curl http://localhost:8080/greeting?name=SpringIO
<!DOCTYPE HTML>
<html>
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>Hello, SpringIO!</p>
</body>
</html>

Example consuming a REST service

In this example, we access a sample rest service to retrieve a random quote.

complete-java$ tree .
.
├── build.gradle
├── src
│   └── main
│       └── java
│           └── hello
│               ├── Application.java
│               ├── Quote.java
│               └── Value.java

Beans in Scala

Simple POJOs are shorter with less boilerplate in Scala, however, they don’t follow the bean standard with getter/setter. In order to make this work, we need to annotate fields of a bean with Scala’s @BeanProperty annotation (Value.scala):

@JsonIgnoreProperties(ignoreUnknown = true)
class Value {
  @BeanProperty
  var id: Long  = _
  @BeanProperty
  var quote: String = _

  override def toString : String = "Value{id=" + id +", quote=" + quote +"}"

Asynchronous consumption

The original guide uses a synchronous RestTemplate to access the webservice. That translates almost 1:1 in Scala. To make things a bit more interesting, let’s use an asynchronous example.

The Java application class (Application.java):

RestTemplate restTemplate = new RestTemplate();
Quote quote = restTemplate.getForObject("http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
log.info(quote.toString());

// Async version
AsyncRestTemplate asyncTemplate = new AsyncRestTemplate();
final ListenableFuture<ResponseEntity<Quote>> quoteFuture = asyncTemplate.getForEntity("http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
quoteFuture.addCallback(
        success -> log.info("async: " + quote),
        failure -> log.error("Async error", failure));

The example uses the AsynchRestTemplate. Calling the endpoint returns a future that we can attach two callbacks to, one for the success case and one for a failure. addCallback is Java 8 ready, meaning we can use Java 8 Lambdas.

Java 8 Lambdas look strikingly similar to Scala function literals

Java: (Integer a, Integer b) -> a + b

Scala: (a:Int, b:Int) => a + b

However, putting Scala functions into the Spring callback method does not work. We have to fall back to ugly anonymous classes:

 
quoteFuture.addCallback(new ListenableFutureCallback[ResponseEntity[Quote]]() {
  override def onSuccess(entity : ResponseEntity[Quote]) : Unit = 
     log.info("async: " + entity.getBody.toString)
  override def onFailure(t : Throwable) : Unit = 
     log.error("Async error", t)
})

This is going to change with Scala 2.12. See below.

Example: providing a REST API

├── build.gradle
├── complete-java.iml
├── complete-java.ipr
├── complete-java.iws
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
    └── main
        └── java
            └── hello
                ├── Application.java
                ├── Person.java
                └── PersonRepository.java

In Spring Data Rest most work resolves around implementing repositories.

The Java side is straight forward (PersonRepository.java):

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
    List<Person> findByLastName(@Param("name") String name);
}

That translates almost 1:1 in Scala (PersonRepository.scala):

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
trait PersonRepository extends PagingAndSortingRepository[Person, Long] {
    def findByLastName(@Param("name") name: String): List[Person]
}

However, running this creates an error:

PersonRepository.scala:8: type arguments [hello.Person,Long] do not conform to trait PagingAndSortingRepository's type parameter bounds [T,ID <: java.io.Serializable] trait PersonRepository extends PagingAndSortingRepository[Person, Long] {

which translates is due to: Scala Long != Java Long. We need to make sure to pass the Java base type to the Java interface import java.lang.Long (or use the full package name).

Final example: JDBC

In this example, we access a relational database using JdbcTemplates. We create a table, insert some data and execute a query to check the results.

Java side

jdbcTemplate.execute("DROP TABLE customers IF EXISTS");
jdbcTemplate.execute("CREATE TABLE customers(" +
        "id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");

// Split up the array of whole names into an array of first/last names
List<Object[]> splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream()
        .map(name -> name.split(" "))
        .collect(Collectors.toList());

// Use a Java 8 stream to print out each tuple of the list
splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1])));

// Uses JdbcTemplate's batchUpdate operation to bulk load data
jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);

log.info("Querying for customer records where first_name = 'Josh':");
jdbcTemplate.query(
        "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[] { "Josh" },
        (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name"))
).forEach(customer -> log.info(customer.toString()));

The steps in Scala:

Create a collection of names in Scala

Dealing with Java, we can’t use Scala’s default immutable collections. Instead, we need to fall back to mutable package in Scala.

import scala.collection.mutable.ListBuffer

val splitUpNames = ListBuffer("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long“).map(_.split(" "))

splitUpNames.foreach(name => log.info("Inserting customer record for %s %s".format(name(0), name(1))))

passing a collection to Java

The signature of batchUpdate method is this: public int[] batchUpdate(String sql, List<Object[]> batchArgs)

We need to pass a List<Object[]> collection. This requires type casting:

  • Java Object <=> Scala AnyRef
  • Java Array [] <=> Scala Array
  • Java (mutable) List <=> Scala mutable.Buffer

We do the casting here:

jdbcTemplate.batchUpdate(
  "INSERT INTO customers(first_name, last_name) VALUES (?,?),   
  splitUpNames
    .asInstanceOf[mutable.Buffer[Array[AnyRef]])

Now, splitUpNames.instanceOf[...] is still a Scala collection that we can’t pass to Java. For this, Scala provides JavaTransformations.asJava. The complete code becomes:

import collection.JavaConverters._

jdbcTemplate.batchUpdate(
  "INSERT INTO customers(first_name, last_name) VALUES (?,?),   
  splitUpNames
    .asInstanceOf[mutable.Buffer[Array[AnyRef]].asJava)

Querying the db: receiving a collection from Java

In the final step, we want to accomplish this in Scala:

jdbcTemplate.query().foreach(...)

Here, we have to other direction and need to call asScala on the collection that is returned from Java.

The complete implementation:

jdbcTemplate.query(
  "SELECT id, first_name, last_name FROM customers WHERE first_name = ?",
  Array("Josh").asInstanceOf[Array[AnyRef]],
  // no Java 8 Lambda support in Scala < 2.12
  new RowMapper[Customer]{
    override def mapRow(rs: ResultSet, rowNum: Int): Customer = new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name"))
  }).asScala.foreach((customer:Customer) => log.info(customer.toString))

Summary

Translating is not always easy

Those are the steps I had to do in order to make my selection of guides work in Scala:

What Step Effort required
Make Gradle work with Scala add scala plugin & dependency easy
Application class split into class & object, pass args with args:_* easy
Use Spring annotations need to add Array(STRING) easy
Create beans in Scala Use @BeanProperty annotation easy
Implement Java/Spring interface Import java base types slightly annoying
Using Java collections in Scala and vice versa Transform collections using JavaTransformations asJava and asScala slightly annoying
Pass collections from Java to Scala and vice versa Casting required, eg Object to AnyRef annoying
Pass collections from Java to Scala and vice versa We have to use mutable collections in Scala annoying
Using Scala functions as Java Lambdas Not possible for Scala < 2.12 annoying / not a problem anymore

Scala SDK is getting better at dealing with Java

One important point to remember: the Scala SDK is getting better at working with Java 8. Scala 2.12 has Lambda support and that makes working with Spring a lot better in Scala.

That brings me to another point: what about using additional frameworks that ease working with Spring? For example, there has been a now defunct Spring-Scala project3. I think there is no need for a framework like that. I prefer to deal in a consistent way with my Java dependencies and rather bet on the Scala SDK to further improve.

Conclusion

If you are wondering why I have only talked about how to use Java from Scala and not more about using Spring from Scala, you are right. And that’s the good news because it means:

It works. Spring is just another Java framework that I can use from Scala.

Drawbacks of using Spring instead of a pure Scala framework:

  • I may not always be able to write most idiomatic Scala code
  • Transforming & casting can get tiresome

Advantages:

  • I can keep my productivity (Spring Boot, yay!)
  • I can use a well-proven and supported framework

Thanks for reading

I hope this post was helpful for you. If you have comments, questions or found a bug please let me know, either in the comments below or contact me directly.

Resources

Subscribe to Human Intelligence Engineering
Explorations of human ingenuity in a world of technology.