Статьи

Образцы Акки со Скалой и Весной

Я недавно искал образцы 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
}

Ссылки: