简体   繁体   English

使用@Qualifier 注释抛出“NoUniqueBeanDefinitionException”(发现多个相同类型的bean)

[英]Using @Qualifier annotation throws "NoUniqueBeanDefinitionException" (found multiple beans of same type)

Problem问题

I'm receiving the error我收到错误

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.storage.StorageService' available: expected single matching bean but found 2: service2,service1

when attempting to start my Spring application.尝试启动我的 Spring 应用程序时。

This is caused by two @Service classes I've created that each implement and interface I've created, StorageService .这是由我创建的两个@Service类引起的,每个类都实现了我创建的接口StorageService I've named both of the @Service classes on the class level itself, @Service("service1") and @Service("service2") , and I'm trying to @Autowire each class in a @Controller constructor and specifying the @Service I want by using the @Qualifier annotation directly on each parameter in the constructor but I am still receiving the NoUniqueBeanDefinitionException error.我已经在类级别本身命名了两个@Service类, @Service("service1")@Service("service2") ,并且我试图在@Controller构造函数中@Autowire每个类并指定@Service我想要通过在构造函数中的每个参数上直接使用@Qualifier注释,但我仍然收到NoUniqueBeanDefinitionException错误。

What I expect我期待什么

I just expect Spring to be able to differentiate between the two Beans by using the @Qualifier annotation.我只是希望 Spring 能够通过使用@Qualifier注释来区分这两个 Bean。

Code代码

StorageService存储服务

package hello.storage;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

import java.nio.file.Path;
import java.util.stream.Stream;

public interface StorageService {

    void init();

    void store(MultipartFile file);

    Stream<Path> loadAll();

    Path load(String filename);

    Resource loadAsResource(String filename);

    void deleteAll();

}

FileSystemStorageService文件系统存储服务

package hello.storage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

@Service("service1")
public class FileSystemStorageService implements StorageService {

    private final Path rootLocation;

    @Autowired
    public FileSystemStorageService(StorageProperties properties) {
        this.rootLocation = Paths.get(properties.getLocation());
    }

    @Override
    public void store(MultipartFile file) {
        try {
            if (file.isEmpty()) {
                throw new StorageException("Failed to store empty file " + file.getOriginalFilename());
            }
            Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
        } catch (IOException e) {
            throw new StorageException("Failed to store file " + file.getOriginalFilename(), e);
        }
    }

    @Override
    public Stream<Path> loadAll() {
        try {
            return Files.walk(this.rootLocation, 1)
                    .filter(path -> !path.equals(this.rootLocation))
                    .map(path -> this.rootLocation.relativize(path));
        } catch (IOException e) {
            throw new StorageException("Failed to read stored files", e);
        }

    }

    @Override
    public Path load(String filename) {
        return rootLocation.resolve(filename);
    }

    @Override
    public Resource loadAsResource(String filename) {
        try {
            Path file = load(filename);
            Resource resource = new UrlResource(file.toUri());
            if(resource.exists() || resource.isReadable()) {
                return resource;
            }
            else {
                throw new StorageFileNotFoundException("Could not read file: " + filename);

            }
        } catch (MalformedURLException e) {
            throw new StorageFileNotFoundException("Could not read file: " + filename, e);
        }
    }

    @Override
    public void deleteAll() {
        FileSystemUtils.deleteRecursively(rootLocation.toFile());
    }

    @Override
    public void init() {
        try {
            Files.createDirectory(rootLocation);
        } catch (IOException e) {
            throw new StorageException("Could not initialize storage", e);
        }
    }
}

AltFileSystemStorageService替代文件系统存储服务

package hello.storage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

@Service("service2")
public class AltFileSystemStorageService implements StorageService {

    // Exactly the same as "FileSystemStorageService" this is just for testing/learning
}

FileUploadController文件上传控制器

package hello;

import java.io.IOException;
import java.util.stream.Collectors;

import hello.storage.AltFileSystemStorageService;
import hello.storage.FileSystemStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import hello.storage.StorageFileNotFoundException;
import hello.storage.StorageService;

@Controller
public class FileUploadController {

    private final StorageService storageService;
    private final StorageService altStorageService;

    @Autowired
    public FileUploadController(@Qualifier("service1") FileSystemStorageService storageService,
                                @Qualifier("service2") AltFileSystemStorageService altStorageService)
    {
        this.storageService = storageService;
        this.altStorageService = altStorageService;
    }

    // Controller specific methods

}

Application应用

package hello;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import hello.storage.StorageProperties;
import hello.storage.StorageService;

@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    CommandLineRunner init(StorageService storageService) {
        return (args) -> {
            storageService.deleteAll();
            storageService.init();
        };
    }
}

I believe the exception is being thrown because of the FileUploadController but I also thought I was using the @Qualifier annotation correctly.我相信由于FileUploadController引发了异常,但我也认为我正确使用了@Qualifier注释。

What I've checked我检查过的

I've gone over multiple examples from SO but nothing seems to be working.我已经查看了 SO 的多个示例,但似乎没有任何效果。

Complete Exception Stack完整的异常堆栈

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method init in hello.Application required a single bean, but 2 were found:
    - service2: defined in file [/UploadFiles/initial/target/classes/hello/storage/AltFileSystemStorageService.class]
    - service1: defined in file [/UploadFiles/initial/target/classes/hello/storage/FileSystemStorageService.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

[WARNING] 
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:542)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'init' defined in hello.Application: Unsatisfied dependency expressed through method 'init' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.storage.StorageService' available: expected single matching bean but found 2: service2,service1
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:509)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
    at hello.Application.main(Application.java:17)
    ... 6 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.storage.StorageService' available: expected single matching bean but found 2: service2,service1
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:221)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1229)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
    ... 25 more
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.838 s
[INFO] Finished at: 2019-09-07T13:21:10-04:00
[INFO] Final Memory: 34M/120M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) on project gs-uploading-files: An exception occurred while running. null: InvocationTargetException: Error creating bean with name 'init' defined in hello.Application: Unsatisfied dependency expressed through method 'init' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.storage.StorageService' available: expected single matching bean but found 2: service2,service1 -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

I think you should add @Qualifier to the CommandLineRunner :我认为您应该将@Qualifier添加到CommandLineRunner

@Bean
CommandLineRunner init(@Qualifier("service1") StorageService storageService)

This question was asked for a long time, and was resolved as well.这个问题问了很久,也解决了。 But I'd like to tell the big mistake of the author, it prevented him finding the root cause.但是我想告诉作者的大错误,它阻止了他找到根本原因。

According to stack trace, it told issue was in init method, not in Controller as the author considered.根据堆栈跟踪,它告诉问题出在init方法中,而不是作者认为的Controller中。 This wrong check took him far away to the root cause.这次错误的检查使他远离了根本原因。

Parameter 0 of method init in hello.Application required a single bean, but 2 were found:
    - service2: defined in file [/UploadFiles/initial/target/classes/hello/storage/AltFileSystemStorageService.class]
    - service1: defined in file [/UploadFiles/initial/target/classes/hello/storage/FileSystemStorageService.class]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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