Akka Actor provides the foundational Actor Model implementation for building concurrent, distributed, resilient and elastic applications with supervision hierarchies and location transparency.
The Actor trait defines the core behavior of all actors, while Props provides type-safe configuration for actor creation with deployment, dispatcher, and mailbox settings.
Core actor behavior with message handling and lifecycle hooks.
import scala.annotation.unused
/**
* Base trait for all actors defining core behavior and lifecycle
*/
trait Actor {
// Type alias to make Receive known in subclasses without import
type Receive = Actor.Receive
/**
* Message handling partial function - must be implemented
* @return PartialFunction mapping messages to actions
*/
def receive: Actor.Receive
/**
* Actor execution context providing access to system and self.
* Initialized when actor is created - complex internal implementation.
*/
implicit val context: ActorContext
/**
* Reference to this actor - MUST BE A VAL for proper initialization.
* Marked final to prevent override.
*/
implicit final val self: ActorRef
/**
* Sender of the current message being processed
* @return ActorRef of sender, or deadLetters if no sender
*/
final def sender(): ActorRef
/**
* Supervision strategy for child actors.
* Default implementation returns SupervisorStrategy.defaultStrategy.
* @return SupervisorStrategy defining fault handling
*/
def supervisorStrategy: SupervisorStrategy = SupervisorStrategy.defaultStrategy
/**
* Called when actor is started - override for initialization.
* Default implementation does nothing.
*/
@throws(classOf[Exception])
def preStart(): Unit = ()
/**
* Called when actor is stopped - override for cleanup.
* Default implementation does nothing.
*/
@throws(classOf[Exception])
def postStop(): Unit = ()
/**
* Called before restart on crashed actor.
* Default implementation stops all children and calls postStop().
* @param reason - Exception that caused restart (unused in default implementation)
* @param message - Message being processed when failure occurred (unused in default implementation)
*/
@throws(classOf[Exception])
def preRestart(@unused reason: Throwable, @unused message: Option[Any]): Unit = {
context.children.foreach { child =>
context.unwatch(child)
context.stop(child)
}
postStop()
}
/**
* Called after restart on new actor instance.
* Default implementation calls preStart().
* @param reason - Exception that caused restart (unused in default implementation)
*/
@throws(classOf[Exception])
def postRestart(@unused reason: Throwable): Unit = preStart()
/**
* Called for messages not handled by receive
* @param message - Unhandled message
*/
def unhandled(message: Any): Unit
}
object Actor {
/**
* Type alias for message handling function
*/
type Receive = PartialFunction[Any, Unit]
/**
* Receive function that handles no messages
*/
object emptyBehavior extends Receive {
def isDefinedAt(x: Any) = false
def apply(x: Any) = throw new UnsupportedOperationException("Empty behavior apply()")
}
/**
* Receive function that ignores all messages
*/
object ignoringBehavior extends Receive {
def isDefinedAt(x: Any): Boolean = true
def apply(x: Any): Unit = ()
}
/**
* Sentinel value indicating no sender
*/
final val noSender: ActorRef = null
}Usage Examples:
import akka.actor._
// Basic actor implementation
class MyActor extends Actor {
def receive = {
case "hello" => sender() ! "Hello back!"
case count: Int =>
println(s"Received count: $count")
if (count > 0) self ! (count - 1)
case _ => unhandled(message)
}
override def preStart(): Unit = {
println("MyActor started")
}
override def postStop(): Unit = {
println("MyActor stopped")
}
}
// Actor with state
class CounterActor extends Actor {
private var count = 0
def receive = {
case "increment" => count += 1
case "get" => sender() ! count
case "reset" => count = 0
}
}Immutable configuration for actor creation and deployment.
/**
* Immutable configuration object for actor creation
*/
class Props private (
val deploy: Deploy,
val clazz: Class[_],
val args: immutable.Seq[Any]
) {
/**
* Create new Props with custom dispatcher
* @param dispatcher - Dispatcher ID from configuration
* @return New Props with dispatcher setting
*/
def withDispatcher(dispatcher: String): Props
/**
* Create new Props with custom mailbox
* @param mailbox - Mailbox type from configuration
* @return New Props with mailbox setting
*/
def withMailbox(mailbox: String): Props
/**
* Create new Props with deployment configuration
* @param deploy - Deployment settings
* @return New Props with deploy settings
*/
def withDeploy(deploy: Deploy): Props
/**
* Create new Props with router configuration
* @param routerConfig - Router configuration
* @return New Props with routing
*/
def withRouter(routerConfig: RouterConfig): Props
}
object Props {
/**
* Create Props from actor class (Scala API)
* @tparam T - Actor type with ClassTag
* @return Props for creating actors of type T
*/
def apply[T <: Actor: ClassTag]: Props
/**
* Create Props from actor class with constructor arguments (Scala API)
* @param creator - By-name parameter creating actor instance
* @return Props for the creator function
*/
def apply(creator: => Actor): Props
/**
* Create Props from Java class
* @param actorClass - Java class extending Actor
* @return Props for the actor class
*/
def create(actorClass: Class[_ <: Actor]): Props
/**
* Create Props from Java class with constructor arguments
* @param actorClass - Java class extending Actor
* @param args - Constructor arguments
* @return Props for actor class with arguments
*/
def create(actorClass: Class[_ <: Actor], args: Any*): Props
/**
* Create Props from creator function (Java API)
* @param creator - Creator function returning Actor
* @return Props for the creator
*/
def create(creator: Creator[Actor]): Props
/**
* Empty Props for no-arg actor construction
*/
val empty: Props
}Usage Examples:
import akka.actor._
// Simple Props creation
val props1 = Props[MyActor]
val props2 = Props(new MyActor)
// Props with constructor arguments
class ParameterizedActor(name: String, value: Int) extends Actor {
def receive = {
case "getName" => sender() ! name
case "getValue" => sender() ! value
}
}
val props3 = Props(new ParameterizedActor("test", 42))
// Props with custom dispatcher and mailbox
val props4 = Props[MyActor]
.withDispatcher("my-dispatcher")
.withMailbox("priority-mailbox")
// Java API
val javaProps = Props.create(classOf[MyActor])
val javaPropsWithArgs = Props.create(classOf[ParameterizedActor], "test", 42)Common patterns for creating actors with different lifecycles and behaviors.
/**
* Java-friendly actor base class
*/
abstract class AbstractActor extends Actor {
/**
* Create receive behavior using builder pattern
* @return Receive behavior for this actor
*/
def createReceive(): AbstractActor.Receive
final def receive = createReceive()
}
object AbstractActor {
/**
* Builder for creating receive behavior
*/
abstract class Receive {
def build(): Actor.Receive
}
}
/**
* Legacy untyped actor base (deprecated)
*/
abstract class UntypedActor extends AbstractActor {
/**
* Handle received message
* @param message - Received message
*/
def onReceive(message: Any): Unit
final def createReceive(): AbstractActor.Receive = {
case msg => onReceive(msg)
}
}Usage Examples:
// AbstractActor with builder pattern
class JavaStyleActor extends AbstractActor {
import akka.japi.pf.ReceiveBuilder
def createReceive(): AbstractActor.Receive = {
ReceiveBuilder.create()
.match(classOf[String], (msg: String) => {
println(s"Received string: $msg")
})
.match(classOf[Integer], (msg: Integer) => {
println(s"Received integer: $msg")
})
.matchAny((msg: Any) => {
println(s"Received unknown: $msg")
})
.build()
}
}Traits for adding logging capabilities to actors.
/**
* Mixin trait providing logging to actors
*/
trait ActorLogging { this: Actor =>
/**
* Logging adapter for this actor
*/
def log: LoggingAdapter
}
/**
* Mixin providing diagnostic logging with MDC support
*/
trait DiagnosticActorLogging extends Actor {
/**
* Diagnostic logging adapter
*/
val log: DiagnosticLoggingAdapter
/**
* Mapped Diagnostic Context for current message
* @param currentMessage - Message being processed
* @return MDC map for logging context
*/
def mdc(currentMessage: Any): MDC
}Usage Examples:
// Actor with logging
class LoggingActor extends Actor with ActorLogging {
def receive = {
case msg =>
log.info("Received message: {}", msg)
// Process message
}
}
// Actor with diagnostic logging
class DiagnosticActor extends Actor with DiagnosticActorLogging {
def receive = {
case msg =>
log.info("Processing message")
// Message processing
}
override def mdc(currentMessage: Any): MDC = {
Map(
"messageType" -> currentMessage.getClass.getSimpleName,
"actorPath" -> self.path.toString
)
}
}/**
* Deployment configuration for actors
*/
case class Deploy(
path: String,
config: Config,
routerConfig: RouterConfig,
scope: Scope,
dispatcher: String,
mailbox: String
) {
def withDispatcher(dispatcher: String): Deploy
def withMailbox(mailbox: String): Deploy
def withRouterConfig(routerConfig: RouterConfig): Deploy
}
/**
* Deployment scope (local, remote, cluster)
*/
sealed trait Scope
case object LocalScope extends Scope
case class RemoteScope(node: Address) extends Scope
/**
* Factory for creating actors with specific constructor arguments
*/
trait IndirectActorProducer {
def actorClass: Class[_ <: Actor]
def produce(): Actor
}Install with Tessl CLI
npx tessl i tessl/maven-com-typesafe-akka--akka-actor-2-11