简体   繁体   中英

Spring Boot unable to scan MongoRepository

I have a persistance interface that implements MongoRepository interface that looks as follows:

package org.prithvidiamond1.DB.Repositories;

import org.prithvidiamond1.DB.Models.SomeModel;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ServerRepository extends MongoRepository<SomeModel, String> {
}

Despite this, I keep getting the following error:

Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'botApplication': 
Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 'org.prithvidiamond1.DB.Repositories.ServerRepository' available: expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations: {}


PPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in org.prithvidiamond1.BotApplication required a bean of type 'org.prithvidiamond1.DB.Repositories.ServerRepository' that could not be found.


Action:

Consider defining a bean of type 'org.prithvidiamond1.DB.Repositories.ServerRepository' in your configuration.

I have tried many solutions (custom @ComponentScan , using @Service instead of @Component , etc.) I found on the internet but none could help me solve the problem, can someone explain to me what is wrong and how I should fix this?

Note: The directory structure is as follows (this is not the full directory structure, but I think this should be enough to get an idea):

org.prithvidiamond1
   |
   +--BotApplication.java
   |
   +--DB
      |
      +--Repository
      |
      +--ServerRepository.java

BotApplication.java looks as follows:

package org.prithvidiamond1;

import org.jc.api.Api;
import org.jc.api.ApiBuilder;
import org.jc.api.entity.server.Server;
import org.prithvidiamond1.DB.Models.Server;
import org.prithvidiamond1.DB.Repositories.ServerRepository;
import org.slf4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Collection;

@Component
public class BotApplication {
    private final Api api;
    private final ServerRepository serverRepository;

    public BotApplication(ServerRepository serverRepository, Logger logger){
        String botToken = System.getenv().get("BOT_TOKEN");

        this.api = new ApiBuilder();

        this.serverRepository = serverRepository;

        appRuntime(logger);
    }

    public void appRuntime(Logger logger){
        logger.info("Bot has started!");

        // Handling server entries in the database
        if (this.serverRepository.findAll().isEmpty()) {
            logger.trace("server data repository empty, initializing data repository...");
            Collection<Server> servers = api.getServers();
            for (Server server : servers) {
                this.serverRepository.save(new Server(String.valueOf(server.getId())));
            }
            logger.trace("server data repository initialized");
        }
    }

    @Bean
    public Api getApi() {
        return this.api;
    }
}

Edit: Here is a link to a repository with all the code: https://github.com/prithvidiamond1/DiamondBot/tree/springboot-restructure

use annotation @Autowired with the constructor of BotApplication class as shown below -

@Autowired
public BotApplication(ServerRepository serverRepository, Logger logger){
        String botToken = System.getenv().get("BOT_TOKEN");

        this.api = new ApiBuilder();

        this.serverRepository = serverRepository;

        appRuntime(logger);
    }

您需要在主应用程序类之上使用 @EnableMongoRepositories 来启用对 mongo 存储库 bean 的扫描。

You have Created Repository bean (ServerRepository)within your application.

But in your BotApplication component (which itself is a bean), you are not telling spring to inject dependency of Repository bean (ie Dependancy Injection).

Such a dependancy injection can be achieved by constructor , field-based or setter based methodologies.

You can either remove ServerRepository serverRepository from public BotApplication(ServerRepository serverRepository, Logger logger) constructor & just use :

@Autowired
private final ServerRepository serverRepository;

Or as other answer suggested, use @Autowired in Constructor itself and remove field ServerRepository serverRepository :

`
@Autowired
public BotApplication(ServerRepository serverRepository, Logger logger)
`

Please note, here, this.serverRepository = serverRepository; is also not required in constructor as dependency injection will take care of this.

The problem is that you are using the primary source class( BotApplication.class ) for executing custom code during start-up.

    public static void main(String[] args) {
        ApplicationContext AppContext = SpringApplication.run(BotApplication.class, args);
    }

    public BotApplication(DiscordServerRepository serverRepository, Logger logger){
        ...
        appRuntime(logger);
    }

In the SpringApplication.run method you need to pass only classes which provide beans definitions. These are @Configuration classes. It is not a good place for using repositories or any startup code.
See the best practices Running code after Spring Boot starts

I describe one of the best solutions: ApplicationRunner

  1. BotApplication class must implement ApplicationRunner interface. The interface is used to indicate that a bean should run when it is contained within a SpringApplication .
    Execute all startup code at the run method.
@Component
public class BotApplication implements ApplicationRunner {
    private final DiscordApi api;
    private final DiscordServerRepository serverRepository;

    private final Logger logger;

    public BotApplication(DiscordServerRepository serverRepository, Logger logger){
        String botToken = System.getenv().get("BOT_TOKEN");
        this.logger = logger;
        this.serverRepository = serverRepository;
        this.api = new DiscordApiBuilder()
                .setToken(botToken)
                .setAllIntents()
                .setWaitForServersOnStartup(true)
                .setWaitForUsersOnStartup(true)
                .login().exceptionally(exception -> {    // Error message for any failed actions from the above
                    logger.error("Error setting up DiscordApi instance!");
                    logger.error(exception.getMessage());
                    return null;
                })
                .join();
    }

    public void appRuntime(Logger logger){
        logger.info("Bot has started!");

        // Handling server entries in the database
        if (this.serverRepository.findAll().isEmpty()) {
            logger.trace("Bot server data repository empty, initializing data repository...");
            Collection<Server> servers = api.getServers();
            for (Server server : servers) {
                this.serverRepository.save(new DiscordServer(String.valueOf(server.getId()), Main.defaultGuildPrefix));
            }
            logger.trace("Bot server data repository initialized");
        }
    }

    @Bean
    public DiscordApi getApi() {
        return this.api;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        appRuntime(logger);
    }
}
  1. Pass Main.class to the SpringApplication.run
@SpringBootApplication
@EnableMongoRepositories
public class Main {
    public static void main(String[] args) {
        ApplicationContext AppContext = SpringApplication.run(Main.class, args);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM