Skip to content

Spring

Jan Machacek edited this page · 1 revision

Comparison with Spring Framework-based code

Here, I explain some of the Akka and Spray concepts in terms that would be familiar to Spring programmers. It will be a very brief explanation and I will make criminal simplifications. If you are seasoned Akka and Scala programmer, stop reading now; otherwise, dive in and see how Akka’s patterns map to the traditional request-response Spring code. We will build trivial application with RESTful API.

The Spring way

The crucial difference is structure of the application and its instantiation model. In contemporary Spring, we might use JavaConfig to describe the components and the dependencies between the components. We then let the Spring Framework instantiate the components and satisfy the dependencies. Typically, the components are singletons—the emergency handbrake of OOP… but we all know that. Let’s get back to the point of building application. In Spring, we’ll have:

public interface UserService {
  User get(Long id);

  List<User> findAll();

  void save(User user);
}

@Service
public class DefaultUserService implements UserService {

  public User get(Long id) {
  }

  List<User> findAll() {
  }

  public void save(User user) {
  }  

}

This represents the service tier–it contains the UserService component, which loads & saves the User entities to some underlying storage. Now, to provide a REST API around it, we need to write a controller:

@Controller("/users")
public class UserController {
  private final UserService userService;

  @Autowired
  public UserController(UserService userService) {
    this.userService = userService;
  }

  @RequestMapping(value = "/{id}", method = RequestMethod.GET)
  @ResponseBody
  public User view(Long id) {
    return this.userService.get(id);
  }

  @RequestMapping(value = "/", method = RequestMethod.GET)
  @ResponseBody
  public List<User> findAll() {
    return this.userService.findAll();
  }

  @RequestMapping(value = "/", method = RequestMethod.POST)
  @ResponseBody
  public User put(@Valid @RequestBody User user) {
    this.userService.save(user);
    return user;
  }
}

Booting Spring applications

That’s the Java code so far. However, this does not complete a Spring application. We need to give XML (or Java) configuration, and typically, we will turn this into a Java EE web application. We shall need two ApplicationContexts, one created by the ContextLoaderListener and one loaded by the DispatcherServlet. The ApplicationContext (the WebApplicationContext to be precise!) loaded by the DispatcherServlet sees every bean in the headless ApplicationContext loaded by the ContextLoaderListener.

Layers

To actually “boot” a Spring Web application, we prepare the WEB-INF/web.xml. Our favourite servlet container then reads this file and creates the components that ultimately prepare the two application contexts. The servlet container also directs incoming HTTP requests to our DispatcherServlet and the dispatcher servlet routes the requests though the innards of our application.

Akka & Spray

Let’s take a look at how we would structure the same application in Scala and Akka; using Spray to build the RESTful API tier. We are going to keep on separating the tiers as we did in Spring application, except now our UserService is going to be the UserActor and its interface is going to be the set of messages it handles and the set of replies it handles. So, turning the interface UserService to the messages, we arrive at:

case class Get(id: Long)
case class Save(user: User)
case class FindAll()

The UserActor will react to these messages by replying to the sender of the message with the appropriate response. This is the perfect opportunity to improve & refine the responses:

  • GetUser(id: Long) replies with Option[User]
  • Save(user: User) replies with Either[Exception, User]
  • FindAll() replies with List[User]

This allows us to complete the implementation of the actor (and, in the future, the higher-order kinds of the responses will lend themselves nicely to interesting shapeless or scalaz code).

class UserActor extends Actor {
  def receive = {
    case GetUser(id) =>
      // load the user, reply with None or Some(user)
      val user: Option[User] = ... 
      sender ! user
    case FindAll() =>
      // find all users
      val users: List[User] = ...
      sender ! users
    case Save(user) =>
      // persist the user
      sender ! Right(user)
  }
}

So, we have the middle tier of our application. The Spring interface’s methods map to the messages that the actors process; the parameters of the methods map to the values the messages carry; the return types map to the response types. Onwards to the controllers.

Just like the services, even the controllers are ultimately actors. These actors react to the incoming HTTP requests, perform whatever processing is required and then write to the response. Just like Spring’s controllers, Akka & Spray allows you to write the API tier without worrying about the underlying HTTP infrastructure. We construct Routes and then give these Routes to the Spray actors that handle the HTTP requests.

class UserService(implicit actorSystem: ActorSystem) 
  extends Directives 
  with LiftJsonSupport 
  with DefaultUnmarshallers 
  with DefaultMarshallers {

  // the name of the userActor (see trait Core)
  def userActor = actorSystem.actorFor("/user/user") 

  val route = 
    path("users") {
      get {
        path(LongNumber) { id =>
          completeWith( (userActor ? GetUser(id)).mapTo[Option[User]] )
        } ~
        path(Slash) {
          completeWith( (userActor ? FindAll()).mapTo[List[User]] )
        }
      } ~
      put {
        content(as[User]) { user =>
          completeWith( (userActor ? Save(user)).mapTo[Either[Exception, User]] ) 
        }
      }
    }

}

Notice the naming: we called this the UserService—by service here we mean functionality available to others. So, in Spray, the term service usually refers to the equivalent of Spring controllers. The controllers interpret the values of the HTTP requests, invoke the middle tier (our UserActor) and write to the responses.

Booting Akka & Spray applications

However, who or where creates these actors? In the Spring world, we’d sprinkle them with the @Component annotation and ask the Spring Framework to manage them for us. Akka does not have such annotation; moreover, the actors are not usually singletons! We need to create them ourselves. We shall define components that wrap our middle tier and our API tier.

trait Core {
  implicit def actorSystem: ActorSystem

  actorSystem.actorOf(Props[UserActor], "user")
}

This trait defines the actors that make up the core of our system—in this example, it is the single UserActor. This is therefore rough equivalent of Spring’s JavaConfiguration or, even more loosely, the XML configuration file.

Next, we create a component that wraps the APIs

trait API {
  this: Core =>

  val routes = new UserService().route :: Nil

  val svc: Route => ActorRef = 
    route => actorSystem.actorOf(Props(new HttpService(route)))

  val rootService = actorSystem.actorOf(
    props = Props(new RootService(
      svc(routes.head),
      routes.tail.map(svc):_*
    )),
    name = "root-service"
  )
}

The rough mapping to the Spring world is that the Core trait defines the headless ApplicationContext and that the API trait defines the WebApplicationContext. To put it yet another way, the components created in the Core trait are completely independent of any APIs; the components in the Api trait depends on the components in the Core trait and provide the RESTful endpoints.

Finally, we can use Spray-can to boot the components and wrap them in real HTTP server:

trait Web {
  this: Api with Core =>

  val ioWorker = new IoWorker(actorSystem).start()

  val sprayCanServer = actorSystem.actorOf(
    Props(new HttpServer(ioWorker, MessageHandlerDispatch.SingletonHandler(
      actorSystem.actorOf(Props(new SprayCanRootService(rootService)))))),
    name = "http-server"
  )

  sprayCanServer ! HttpServer.Bind("localhost", 8080)

  actorSystem.registerOnTermination {
    ioWorker.stop()
  }

}

object Main extends App {
  implicit val system = ActorSystem("App")

  class Application(val actorSystem: ActorSystem) 
    extends Core with Api with Web

  new Application(system)

  sys.addShutdownHook {
    system.shutdown()
  }

}

Notice that our Akka/Spray application starts with equivalent of public static void main(String[] args)! There is no need for a servlet container of application server (but there is the option of having one, of course!).

The mapping

Mapping

All

The crucial difference

I cannot go on any longer ignoring the elephant in the room!

Elephant in the room

So far, you might think that the only difference is in the naming and in the code we write to create the components. In Spring, we have the various ApplicationContexts with our components “living” in them; in Akka, we have to assemble the application ourselves. No, the crucial difference is that Akka applications are asynchronous by nature. Every operation is performed asynchronously, with the implied hope that by the time someone asks for the result, the underlying operation will have finished. This also explains the reason why Akka applications must run in Servlet 3.0 enabled container or in their own container, such as Spray-can.

Something went wrong with that request. Please try again.