The project utilizes Akka extensions to hook in Spring based dependency injection into Akka.
Here is what the extension looks like:
package sample import akka.actor.{ActorSystem, Props, Extension} import org.springframework.context.ApplicationContext /** * The Extension implementation. */ class SpringExtension extends Extension { var applicationContext: ApplicationContext = _ /** * Used to initialize the Spring application context for the extension. * @param applicationContext */ def initialize(applicationContext: ApplicationContext) = { this.applicationContext = applicationContext this } /** * Create a Props for the specified actorBeanName using the * SpringActorProducer class. * * @param actorBeanName The name of the actor bean to create Props for * @return a Props that will create the named actor bean using Spring */ def props(actorBeanName: String): Props = Props(classOf[SpringActorProducer], applicationContext, actorBeanName) } object SpringExtension { def apply(system : ActorSystem )(implicit ctx: ApplicationContext) : SpringExtension = SpringExt(system).initialize(ctx) }
So the extension wraps around a Spring application context. The extensions provides a props method which returns an Akka Props configuration object which uses the application context and the name which the actor is registered with Spring to return an instance of the Actor. The following is the SpringActorProducer:
package sample import akka.actor.{Actor, IndirectActorProducer} import org.springframework.context.ApplicationContext class SpringActorProducer(ctx: ApplicationContext, actorBeanName: String) extends IndirectActorProducer { override def produce: Actor = ctx.getBean(actorBeanName, classOf[Actor]) override def actorClass: Class[_ <: Actor] = ctx.getType(actorBeanName).asInstanceOf[Class[Actor]] }
Given this base code, how does Spring find the actors, I have used scanning annotations to annotate the actors this way:
package sample.actor import akka.actor.Actor import sample.service.CountingService import sample.SpringExtension._ import org.springframework.stereotype.Component import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Scope import akka.actor.ActorRef import sample.SpringExtension import org.springframework.context.ApplicationContext @Component("countingCoordinatingActor") @Scope("prototype") class CountingCoordinating @Autowired() (implicit ctx: ApplicationContext) extends Actor { import sample.messages._ var counter: Option[ActorRef] = None def receive = { case COUNT => countingActor() ! COUNT case g:GET => countingActor() ! g } private def countingActor(): ActorRef = { if (counter.isEmpty) { val countingActorProp = SpringExtension(context.system).props("countingActor") counter = Some(context.actorOf(countingActorProp, "counter")) } counter.get } } @Component("countingActor") @Scope("prototype") class CountingActor @Autowired()(countingService: CountingService) extends Actor { import sample.messages._ private var count = 0 def receive = { case COUNT => count = countingService.increment(count) case GET(requester: ActorRef) => requester ! RESULT(count) } }
The CountingService is a simple service that gets injected in by Spring. The following is the main Spring Application configuration where all the wiring takes place:
import akka.actor.ActorSystem import org.springframework.context.ApplicationContext import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Bean import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.ComponentScan @Configuration @ComponentScan(Array("sample.service", "sample.actor")) class AppConfiguration { @Autowired implicit var ctx: ApplicationContext = _; /** * Actor system singleton for this application. */ @Bean def actorSystem() = { val system = ActorSystem("AkkaScalaSpring") // initialize the application context in the Akka Spring Extension SpringExt(system) system } }
To make use of this entire set-up in a sample program:
import akka.actor.{ActorRef, ActorSystem} import sample.SpringExtension._ import scala.concurrent.duration._ import scala.concurrent._ import scala.util._ import sample.messages._ import org.springframework.context.annotation.AnnotationConfigApplicationContext import akka.actor.Inbox object Main extends App { // create a spring context implicit val ctx = new AnnotationConfigApplicationContext(classOf[AppConfiguration]) import Config._ // get hold of the actor system val system = ctx.getBean(classOf[ActorSystem]) val inbox = Inbox.create(system) val prop = SpringExtension(system).props("countingCoordinatingActor") // use the Spring Extension to create props for a named actor bean val countingCoordinator = system.actorOf(prop, "counter") // tell it to count three times inbox.send(countingCoordinator, COUNT) inbox.send(countingCoordinator, COUNT) inbox.send(countingCoordinator, COUNT) inbox.send(countingCoordinator, GET(inbox.getRef())) val RESULT(count) = inbox.receive(5.seconds) println(s"Got $count") system.shutdown system.awaitTermination }
References:
- Most of the code is thanks to the akka-java-spring project available here.
- The project is originally forked from here
No comments:
Post a Comment