Я недавно искал образцы 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, доступному здесь.
- Проект изначально раздвоенный из здесь