2. Akka中的依赖注入
Akka是基于Actor并发模型的强大框架,底层用Scala编写但完全兼容Java。实际开发中常需将Akka集成到现有Spring项目,或直接用Spring管理Actor的依赖。
Spring/Akka集成的核心难点在于生命周期差异:Actor的生命周期与普通Spring Bean完全不同。Actor本身是内部实现细节(不能被Spring管理),而对外暴露的是可序列化、可跨节点传输的Actor引用。
幸运的是,Akka提供了扩展机制,让外部依赖注入框架的集成变得简单粗暴。
3. Maven依赖
在Spring项目中使用Akka,只需两个核心依赖:spring-context
和akka-actor
。版本号建议统一管理:
<properties>
<spring.version>5.3.25</spring.version>
<akka.version>2.4.14</akka.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.12</artifactId>
<version>${akka.version}</version>
</dependency>
</dependencies>
注意:akka-actor
依赖名中的_2.12
后缀表示该版本基于Scala 2.12构建,会自动引入对应Scala库。建议去Maven中央仓库确认最新版本。
4. 向Akka Actor注入Spring Bean
我们构建一个简单示例:创建一个能根据人名生成问候语的Actor,将问候逻辑抽离为独立服务并通过Spring注入。
4.1 定义Actor和服务
先看GreetingActor
实现(继承UntypedActor
):
- 核心逻辑在
onReceive
方法:处理Greet
消息时,提取人名调用GreetingService
生成问候语 - 非法消息交给
unhandled
处理 Greet
消息类型定义为内部静态类(最佳实践)
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class GreetingActor extends UntypedActor {
private GreetingService greetingService;
// 构造器
@Override
public void onReceive(Object message) throws Throwable {
if (message instanceof Greet) {
String name = ((Greet) message).getName();
getSender().tell(greetingService.greet(name), getSelf());
} else {
unhandled(message);
}
}
public static class Greet {
private String name;
// 标准构造器/getter
}
}
关键点:
- **必须用
@Scope("prototype")
**:每次获取Bean都创建新实例,匹配Actor生命周期 - **省略
@Autowired
**:Spring 4.3+的隐式构造器注入特性会自动注入GreetingService
GreetingService
实现很简单,注意用@Component
声明为Spring Bean:
@Component
public class GreetingService {
public String greet(String name) {
return "Hello, " + name;
}
}
4.2 通过Akka扩展添加Spring支持
最优雅的集成方案是使用Akka扩展。扩展是ActorSystem级别的单例,包含两部分:
- 扩展类(实现
Extension
标记接口) - 扩展ID类(继承
AbstractExtensionId
)
两者紧密耦合,通常将前者作为后者的内部类:
public class SpringExtension
extends AbstractExtensionId<SpringExtension.SpringExt> {
public static final SpringExtension SPRING_EXTENSION_PROVIDER
= new SpringExtension();
@Override
public SpringExt createExtension(ExtendedActorSystem system) {
return new SpringExt();
}
public static class SpringExt implements Extension {
private volatile ApplicationContext applicationContext;
public void initialize(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public Props props(String actorBeanName) {
return Props.create(
SpringActorProducer.class, applicationContext, actorBeanName);
}
}
}
实现要点:
SPRING_EXTENSION_PROVIDER
单例持有扩展实例SpringExt
扩展类保存Spring上下文,并提供props
方法创建Props
对象Props
是Actor的蓝图,这里委托给SpringActorProducer
处理
最后是SpringActorProducer
,它实现IndirectActorProducer
接口控制Actor实例化:
- 不直接创建Actor,而是从Spring容器获取Bean
- 配合
prototype
作用域,确保每次返回新实例
public class SpringActorProducer implements IndirectActorProducer {
private ApplicationContext applicationContext;
private String beanActorName;
public SpringActorProducer(ApplicationContext applicationContext,
String beanActorName) {
this.applicationContext = applicationContext;
this.beanActorName = beanActorName;
}
@Override
public Actor produce() {
return (Actor) applicationContext.getBean(beanActorName);
}
@Override
public Class<? extends Actor> actorClass() {
return (Class<? extends Actor>) applicationContext
.getType(beanActorName);
}
}
4.3 整合配置
创建Spring配置类完成最后拼图:
@ComponentScan
自动扫描Bean- 声明
ActorSystem
Bean并初始化Spring扩展
@Configuration
@ComponentScan
public class AppConfiguration {
@Autowired
private ApplicationContext applicationContext;
@Bean
public ActorSystem actorSystem() {
ActorSystem system = ActorSystem.create("akka-spring-demo");
SPRING_EXTENSION_PROVIDER.get(system)
.initialize(applicationContext);
return system;
}
}
4.4 获取Spring管理的Actor
测试注入效果:通过扩展创建ActorRef并发送消息
ActorRef greeter = system.actorOf(SPRING_EXTENSION_PROVIDER.get(system)
.props("greetingActor"), "greeter");
FiniteDuration duration = FiniteDuration.create(1, TimeUnit.SECONDS);
Timeout timeout = Timeout.durationToTimeout(duration);
Future<Object> result = ask(greeter, new Greet("John"), timeout);
Assert.assertEquals("Hello, John", Await.result(result, duration));
关键步骤:
- 用扩展的
props
方法创建Props
- 通过
actorOf
获取ActorRef - 使用
ask
模式发送消息(返回Scala的Future
) - 用
Await.result
等待结果(实际项目建议异步处理)
5. 总结
本文展示了Spring与Akka集成的完整方案,核心是:
- 将Actor声明为Spring的
prototype
Bean - 通过Akka扩展桥接Spring容器
- 用
IndirectActorProducer
控制Actor实例化
这种方案既保持了Akka的Actor模型特性,又享受了Spring的依赖注入能力。完整代码可在GitHub获取。