Я недавно искал образцы Akka с Spring и нашел стартовый проект, который, казалось, соответствовал всем требованиям. Проект, однако, использует Spring-Scala, который является отличным проектом, но больше не поддерживается. Поэтому я хотел обновить образец, чтобы использовать вместо него основные Java-библиотеки Spring. Итак, вот попытка на развилке этого стартового проекта с ядром Spring вместо Spring-scala. Код доступен здесь .
В проекте используются расширения Akka для подключения инъекций зависимостей на основе Spring в Akka.
Вот как выглядит расширение:
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)
}
Таким образом, расширение охватывает контекст приложения Spring . Расширения предоставляют метод props, который возвращает объект конфигурации Akka Props, который использует контекст приложения и имя, которое актер зарегистрирован в Spring для возврата экземпляра Actor. Ниже приводится 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]]
}
Учитывая этот базовый код, как Spring находит актеров, я использовал сканирование аннотаций, чтобы комментировать актеров таким образом:
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)
}
}
CountingService — это простой сервис, который внедряется Spring. Ниже приведена основная конфигурация приложения Spring, в которой выполняется вся проводка:
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
}
}
Чтобы использовать всю эту настройку в примере программы:
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
}
Ссылки:
- Большая часть кода написана благодаря проекту akka-java-spring, доступному здесь.
- Проект изначально раздвоенный из здесь