SpringBoot - BeanDefinitionOverrideException: 无效的bean定义。

103

我正在尝试使用Spring Boot在本地设置DynamoDB。最初我成功进行了设置,并能够通过仓库将数据写入/保存到DynamoDB。从那时起,我添加了更多的类来构建我的应用程序。现在当我尝试启动应用程序时,我会收到以下异常:

org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'agentRepository' defined in null: Cannot register bean definition [Root bean: class [org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'agentRepository': There is already [Root bean: class [org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.

我已经广泛搜索了stackoverflow和互联网,但没有任何有用的解决方案。错误信息也很具误导性。

我的项目层次结构如下:

ai.test.as
  - as
      - agent
          - business
          - intent
          - exception
          - Agent.java
          - AgentDTO.java
          - AgentRespository.java
          - AgentController.java
          - AgentService.java
          - AgentServiceImpl.java
  - config
     - DynamoDBConfig.java

DynamoDBConfig.java

package ai.test.as.config;

import ai.test.as.agent.AgentRepository;
import ai.test.as.agent.intent.template.TemplateRepository;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import org.socialsignin.spring.data.dynamodb.repository.config.EnableDynamoDBRepositories;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableDynamoDBRepositories(basePackageClasses = {AgentRepository.class})
public class DynamoDBConfig
{
    @Value("${aws.dynamodb.endpoint}")
    private String dynamoDBEndpoint;

    @Value("${aws.auth.accesskey}")
    private String awsAccessKey;

    @Value("${aws.auth.secretkey}")
    private String awsSecretKey;

    @Bean
    public AmazonDynamoDB amazonDynamoDB()
    {
        AmazonDynamoDB dynamoDB = new AmazonDynamoDBClient(getAwsCredentials());
        dynamoDB.setEndpoint(dynamoDBEndpoint);

        return dynamoDB;
    }

    @Bean
    public AWSCredentials getAwsCredentials()
    {
        return new BasicAWSCredentials(awsAccessKey, awsSecretKey);
    }
}

代理商仓库.java

package ai.test.as.agent;

import ai.test.as.agent.Agent;
import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.springframework.data.repository.CrudRepository;

@EnableScan
public interface AgentRepository extends CrudRepository<Agent, String>
{
}

AgentController.java(使用AgentRepository的位置)

@RestController
@RequestMapping(value = "/v1/agents")
public class AgentController
{
    @Autowired
    private AgentRepository agentRepository;

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public void test()
    {
        Agent agent = new Agent();
        agent.setAgentNumber("123456");
        agent.setId(1);

        agentRepository.save(agent);
    }
}

Spring建议如下:> 在null中定义的bean 'agentRepository'无法注册。已在null中定义了具有该名称的bean,并且禁用了覆盖。

这里的null是什么意思?是因为我的应用程序配置出了问题吗?另外,它怎么可能已经被注册了呢?

请给我一些指示,因为我对接下来该怎么做感到非常困惑。


你尝试过在AgentRepository上使用@Component注解吗? - OEH
之前我尝试使用@Component注释来读写DynamoDB,但是不管怎样,按照您的建议,我现在也尝试了一下,但是还是不起作用。 - Vino
关于您的问题,如果bean已经注册,您可以通过使用ApplicationContext对象的getBeanDefinitionNames()方法在上下文中打印所有bean。通过在控制台中打印它们,您可以看到使用了哪些bean键。网络上有几个示例,因此我不会在这里分享。 - OEH
请问您能否在AgentRepository类中添加[@Repository]或[@Service]注释并尝试一下?另外,您的AgentRepository是一个接口,使用接口而不是类有什么用例吗? - venkat
不幸的是,其他答案都没有解决这个问题开始出现的原因。理论上,最好的解决方案应该从识别为什么/如何有两个bean开始。 - Dherik
显示剩余2条评论
8个回答

171

自Spring Boot 2.1版本起,需要启用Bean覆盖功能。

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1-Release-Notes

Bean重写

为了防止意外覆盖bean,Bean重写默认已禁用。如果您依赖于重写功能,则需要将spring.main.allow-bean-definition-overriding设置为true。

设置

spring.main.allow-bean-definition-overriding=true

或 yml,

spring:
   main:
     allow-bean-definition-overriding: true

重新启用覆盖。

编辑,

Bean覆盖是基于bean的名称而不是其类型。例如:

@Bean
public ClassA class(){
   return new ClassA();
}

@Bean
public ClassB class(){
   return new ClassB();
}

在2.1及以上版本中会导致此错误,默认情况下,bean名称取自方法名称。重命名方法或向Bean注释添加name属性将是有效的修复方法。


6
谢谢你的提问。但是我可以知道为什么我应该覆盖这个Bean吗?我确定我只创建了一个AgentRepository的Bean,那么会被覆盖吗? - Vino
3
请阅读那个例外并了解它实际上在抱怨什么,它正在抱怨 DynamoDBRepositoryFactoryBean bean。我认为它已经通过引导应用程序进行注册,并且不需要使用 @EnableDynamoDBRepositories(basePackageClasses = {AgentRepository.class})。同时,对于任何给它投反对票的人,解释一下你的理由是很常见的。 - Darren Forsythe
1
鉴于它是Spring Framework中的一个功能,并且被广泛使用,认为它很"脏"。去掉EnableDynamoDBRepositories(basePackageClasses = {AgentRepository.class})可能会解决问题,但我没有访问Dynamo设置的权限,如果我猜测可能会再次触发Bean的创建,但如果无法解决,我会提出一个问题并提供示例。 - Darren Forsythe
@DarrenForsythe FYI,我尝试删除@EnableDynamoDBRepositories并删除覆盖属性,但问题仍然存在。因此,我认为无论如何都需要该注释。 - Vino
我认为这是一种非常冒险的解决方法,可能会导致不确定的行为。最好解决潜在的问题,即Spring尝试两次创建这些存储库。最有可能是由于错误配置或spring-data-dynamodb / spring-boot / spring-data版本不兼容引起的。 - Ahmad Abdelghany
显示剩余3条评论

26

通过这种方法启用Bean覆盖的示例

@SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
或者
@SpringBootApplication (properties = "spring.main.allow-bean-definition-overriding=true")

2
请注意,如果这是一个测试 bean,您的解决方案基本相同,请不要依赖 bean 的命名。重命名它,必要时使用 primary 进行注入。 - Darren Forsythe
你能解释一下这为什么是必要的吗? - devinho
是的,这基本上对于测试来说是不必要的。如果你想避免这个问题,你需要确保你的SpringBootTest没有默认地选择主应用程序上下文,如果你有一个特定的测试上下文的话。 - Pwnstar

2

我遇到了同样的问题,问题在于多个仓库工厂试图向它们注册所有仓库接口。在我的情况下,这是 JpaRepositoryFactoryBeanDynamoDBRepositoryFactoryBean。正如其他答案中提到的那样,你可以通过日志看到:

[INFO] Bootstrapping Spring Data DynamoDB repositories in DEFAULT mode.
[INFO] Finished Spring Data repository scanning in 64ms. Found 2 DynamoDB repository interfaces.
[INFO] Bootstrapping Spring Data JPA repositories in DEFAULT mode.

解决方案如下:
  1. 请确保您使用的 spring-data-dynamodb / spring-boot / spring-data 版本兼容,可以通过查看兼容矩阵来确认。
  2. 请确保每个仓库只被创建一次。在我的情况下,我需要添加以下代码:
@SpringBootApplication
@EnableAutoConfiguration(exclude = {
       DataSourceAutoConfiguration.class,
       DataSourceTransactionManagerAutoConfiguration.class,
       HibernateJpaAutoConfiguration.class})

配置可能因应用程序中有多少个存储库以及不同版本而有所不同。阅读关于“多存储库配置”的文章可能会有所帮助。 Multi-Repository-configuration

2

我认为我在使用MongoDB时遇到了同样的问题。至少错误消息看起来完全相同,而且我也只有一个用于MongoDB的存储库,类似于这样:

public interface MyClassMongoRepository extends MongoRepository<MyClass, Long> {
}

问题是由于 MyClass 类之前在另一个数据库中使用而引起的。Spring 在创建 MongoRepository 之前悄悄地创建了一些 JpaRepository。两个仓库具有相同的名称,这导致了冲突。
解决方法是复制 MyClass,将其移动到 MongoRepository 的包中,并删除任何与 JPA 相关的注释。

1
是的,这解决了问题,但它将您的代码与持久性提供程序(MongoDB)绑定在一起,这违背了JPA的目的。 - Ahmad Abdelghany
@AhmadAbdelghany,我知道这一点,但我只是想为那些不想更改全局选项来解决本地问题的开发人员提供另一个选择。 - Sascha Doerdelmann

2

我在尝试将一个PostgreSQL数据库通过spring-data-jdbc添加到已经使用MongoDB的现有项目中时,遇到了同样的问题。

看起来问题是由于MongoDB和PostgreSQL的存储库都被两个模块(spring-mongo和spring-jdbc)扫描。它们都试图创建一些bean并发生冲突。

在我的情况下,MongoDB存储库和PostgreSQL存储库位于同一个包中。

接受的答案解决了我的问题,但我从启动日志中得到了一些提示:

Finished Spring Data repository scanning in 319ms. Found 4 repository interfaces
Finished Spring Data repository scanning in 319ms. Found 5 repository interfaces

这很奇怪,因为我只有一个PostgreSQL仓库和4个MongoDB仓库。

我将PostgreSQL仓库移动到与MongoDB仓库不同的包中,并将PostgreSQL仓库的基础包配置为新包。

在我的情况下:

@EnableJdbcRepositories(basePackageClasses = MyOnlyPostgreSQLRepository.class) // TODO: Use the real package or a dedicated base class

这对我解决了问题(没有为覆盖的bean设置属性-这是我喜欢的)。启动日志现在也显示了正确数量的仓库(1和4)。


是的,我可以确认,在我的情况下添加一个额外的@Configuration bean,并使用@EnableJpaRepositories(basePackages = {"A.repository", "A.common.repository"})解决了问题(我在主项目(包A.repository)和被该项目引用的中间库(包A.common.repository)中都定义了Repository对象)。 - hello_earth

1
在我的情况下,有两个Maven依赖项中都定义了相同的Bean。当我对所有项目执行mvn dependency:tree时,我发现了这一点。

1

您可以在application.properties文件中使用spring.main.allow-bean-definition-overriding=true

或者

使用@SpringBootApplication(properties = "spring.main.allow-bean-definition-overriding=true")


0

这个错误信息表明您的应用程序中定义了多个具有相同名称的bean定义。在Spring框架中,每个bean必须在应用程序上下文中具有唯一的名称。

要解决此问题,您应该重命名其中一个类或其bean定义,以便每个bean定义都具有唯一的名称。您可以通过在@Component、@Repository或@Service注释中使用“name”属性来实现此目的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接