2017-10-18 136 views
1

我正在使用Axon + Spring Boot创建一个简单的应用程序,只是为了确保我在Axon框架中理解基本组件,然后才将其用于实际项目。 有一个应该当我通过CommandGateway命令发送到被称为类TaskAggregate内的@CommandHandler注释的方法,但在运行应用程序后,我得到异常:NoHandlerForCommandException with axon-spring-boot-starter

Exception in thread "main" org.axonframework.commandhandling.NoHandlerForCommandException: No handler was subscribed to command [com.xxx.axontest.task.CreateTaskCommand] 

按文档,@CommandHandler注释应该足以将命令hander订阅到命令总线。我想我一定会错过一些东西。你可以看看下面的代码,并指出我正确的方向?

的pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>com.xxx</groupId> 
    <artifactId>axon-test</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 

    <properties> 
     <axon.version>3.0.6</axon.version> 
    </properties> 

    <parent> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-parent</artifactId> 
     <version>1.5.7.RELEASE</version> 
    </parent> 

    <dependencies> 

     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-web</artifactId> 
     </dependency> 

     <dependency> 
       <groupId>org.axonframework</groupId> 
       <artifactId>axon-spring-boot-starter</artifactId> 
       <version>${axon.version}</version> 
     </dependency> 

    </dependencies> 

    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.springframework.boot</groupId> 
       <artifactId>spring-boot-maven-plugin</artifactId> 
      </plugin> 
     </plugins> 
    </build> 

</project> 

App.java

package com.xxx.axontest; 

import org.axonframework.commandhandling.gateway.CommandGateway; 
import org.axonframework.eventsourcing.eventstore.EventStorageEngine; 
import org.axonframework.eventsourcing.eventstore.inmemory.InMemoryEventStorageEngine; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.context.ConfigurableApplicationContext; 
import org.springframework.context.annotation.Bean; 

import com.xxx.axontest.task.CreateTaskCommand; 

@SpringBootApplication 
public class App { 

    public static void main(String[] args) { 
     ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args); 
     CommandGateway commandGateway = configurableApplicationContext.getBean(CommandGateway.class); 
     commandGateway.send(new CreateTaskCommand(123, "asd")); 
    } 

    @Bean 
    public EventStorageEngine eventStorageEngine() { 
     return new InMemoryEventStorageEngine(); 
    } 

    @Bean 
    public AnnotationCommandHandlerBeanPostProcessor 
annotationCommandHandlerBeanPostProcessor() { 
    return new AnnotationCommandHandlerBeanPostProcessor(); 
    } 
} 

CreateTaskCommand.java

package com.xxx.axontest.task; 

import org.axonframework.commandhandling.TargetAggregateIdentifier; 

public class CreateTaskCommand { 

    @TargetAggregateIdentifier 
    private int taskId; 
    private String name; 

    public CreateTaskCommand(int taskId, String name) { 
     this.taskId = taskId; 
     this.name = name; 
    } 

    public int getTaskId() { 
     return taskId; 
    } 

    public String getName() { 
     return name; 
    } 
} 

TaskCreatedEvent.java

package com.xxx.axontest.task; 

import org.axonframework.commandhandling.TargetAggregateIdentifier; 

public class TaskCreatedEvent { 

    @TargetAggregateIdentifier 
    private int taskId; 
    private String name; 

    public int getTaskId() { 
     return taskId; 
    } 

    public String getName() { 
     return name; 
    } 

} 

TaskAggregate.java

package com.xxx.axontest.task; 

import org.apache.log4j.LogManager; 
import org.apache.log4j.Logger; 
import org.axonframework.commandhandling.CommandHandler; 
import org.axonframework.commandhandling.model.AggregateIdentifier; 
import org.axonframework.commandhandling.model.AggregateLifecycle; 
import org.axonframework.eventsourcing.EventSourcingHandler; 
import org.axonframework.spring.stereotype.Aggregate; 

@AggregateRoot 
public class TaskAggregate { 

    private Logger logger = LogManager.getLogger(TaskCreatedEvent.class); 

    @AggregateIdentifier 
    private int taskId; 
    private String name; 

    @CommandHandler 
    public void handleCommand(CreateTaskCommand createTaskCommand) { 
     logger.info("Command received"); 
     AggregateLifecycle.apply(new TaskCreatedEvent()); 
    } 

    @EventSourcingHandler 
    public void onTaskCreatedEvent(TaskCreatedEvent taskCreatedEvent) { 
     logger.info("Event received"); 
    } 

    public int getTaskId() { 
     return taskId; 
    } 

    public void setTaskId(int taskId) { 
     this.taskId = taskId; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

} 

在此先感谢。

+0

一件重要的事情:事件和命令应该是不可变的(无设置器) –

+0

从doc:“CommandHandlers需要订阅CommandBusin命令才能接收命令“ –

+0

@ConstantinGalbenu,我根据你的建议删除了setter。 – Rodney

回答

2

我认为你需要用@Aggregate而不是@AggregateRoot来标注你的聚合。作为注释的 @Aggregate既是@AggregateRoot注释,也被Spring Boot Axon Starter模块用来表示该类别的聚合工厂和Repository必须被创建。 此外,这将意味着您的聚合中的@CommandHandler注释函数也会被找到并注册到CommandBus,可能会解决您捕获的异常。

否则,来自Allard的YouTube上的webinars在版本3中启动Axon应用程序可能会给您一些洞察。

但是,总之,尝试另外然而切换@AggregateRoot标注为@Aggregate :-)

,你应该调整为CreateTaskCommand@CommandHandler注释功能是为TaskAggregate构造。 最后,Axon要求你有一个没有参数的构造函数给你,所以也在那里添加一个public TaskAggregate() { }构造函数。

+0

好的。我试过了,现在我得到** Command'com.xxx.axontest.task.CreateTaskCommand'导致org.axonframework.commandhandling.model.AggregateNotFoundException(在事件存储中未找到聚合)** – Rodney

+0

调整了评论指出你应该可以通过构造函数而不是常规函数来处理'CreateTaskCommand'。另外,也是一个无参数构造函数,正如Axon所要求的那样。 – Steven

0

您还需要将您的处理程序注册到命令总线。我发现this tutorial应该可以帮到你。从那里快速突出:

@Configuration 
public class AppConfiguration { 

    @Bean 
    public SimpleCommandBus commandBus() { 
     SimpleCommandBus simpleCommandBus = new SimpleCommandBus(); 
     // This manually subscribes the command handler: 
     // DebitAccountHandler to the commandbus. 
     simpleCommandBus.subscribe(DebitAccount.class.getName(), new DebitAccountHandler()); 
     return simpleCommandBus; 
    } 
} 

P.S.一个重要的事情:事件和命令应该是不可变的(不是setter)

+0

你提到的教程是使用轴2.4.1。我正在使用3.0.6。在[axon的文档](https://docs.axonframework.org/v/3.0/part3/spring-boot-autoconfig.html),Aggregate Configuration部分中,他们说:** Axon会自动注册所有的@CommandHandler注释方法如果不存在** – Rodney

+0

在本教程中,您提到他们也会说: **如果在Spring中使用Axon,请使用AnnotationCommandHandlerBeanPostProcessor,它允许我们拥有Spring bean,其中有注释了方法的Spring bean @CommandHandler会变成命令处理程序。** – Rodney

+0

在我的项目中,命令处理程序在聚合中。在axon 3.0.6中没有可以继承的CommandHandler接口。 – Rodney

2

基于上面的代码,几句话:

  • 你不需要提供AnnotationCommandHandlerBeanPostProcessor。实际上,指定一个可能会干扰Axon/Spring Boot自动配置的正常运行。
  • 创建新的Aggregate实例的命令应放置在构造函数中。尚未调用该方法的实例。请注意,您将(也)必须指定一个无参数构造函数。
  • taskId应该由@EventSourcingHandler设置。吸气剂和吸气剂不属于(事件源)聚合物
  • 在活动中,您不需要指定@TargetAggregateIdentifier。它们只是命令的手段。

根据您提供的代码,我无法解释这个异常,但是显式定义的AnnotationCommandHandlerBeanPostProcessor有可能会出现。

Steven正确指出了@AggregateRoot注解。它应该是@Aggregate。以上评论仍然有效,但并不直接涉及例外[/编辑]

+0

有点困惑,因为[documentation](https://docs.axonframework.org/v/3.0/part3/spring-boot-autoconfig.html)说:** Axon会自动注册所有CommandHandler注释的方法命令总线,并建立一个存储库,如果没有。**没有提到构造函数。但是,如您所建议的那样,在构造函数中使用@CommandHandler注释而不是方法,可以解决问题。非常感谢你。 – Rodney