2. Akka中的依赖注入

Akka是基于Actor并发模型的强大框架,底层用Scala编写但完全兼容Java。实际开发中常需将Akka集成到现有Spring项目,或直接用Spring管理Actor的依赖。

Spring/Akka集成的核心难点在于生命周期差异:Actor的生命周期与普通Spring Bean完全不同。Actor本身是内部实现细节(不能被Spring管理),而对外暴露的是可序列化、可跨节点传输的Actor引用。

幸运的是,Akka提供了扩展机制,让外部依赖注入框架的集成变得简单粗暴。

3. Maven依赖

在Spring项目中使用Akka,只需两个核心依赖:spring-contextakka-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级别的单例,包含两部分:

  1. 扩展类(实现Extension标记接口)
  2. 扩展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));

关键步骤:

  1. 用扩展的props方法创建Props
  2. 通过actorOf获取ActorRef
  3. 使用ask模式发送消息(返回Scala的Future
  4. Await.result等待结果(实际项目建议异步处理)

5. 总结

本文展示了Spring与Akka集成的完整方案,核心是:

  1. 将Actor声明为Spring的prototype Bean
  2. 通过Akka扩展桥接Spring容器
  3. IndirectActorProducer控制Actor实例化

这种方案既保持了Akka的Actor模型特性,又享受了Spring的依赖注入能力。完整代码可在GitHub获取。


原始标题:Introduction to Spring with Akka