12

I am new to Spring Boot and I'm getting the following error when writing a file upload API:

Error:Description:
Field fileStorageService in com.primesolutions.fileupload.controller.FileController required a bean of type 'com.primesolutions.fileupload.service.FileStorageService' that could not be found.
The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.primesolutions.fileupload.service.FileStorageService' in your configuration.*

Controller class:

public class FileController 
{
    private static final Logger logger = LoggerFactory.getLogger(FileController.class);

    @Autowired
    private FileStorageService fileStorageService;

    @PostMapping("/uploadFile")
    public UploadFileResponse uploadFile(@RequestParam("file") MultipartFile file) {
        String fileName = fileStorageService.storeFile(file);

        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                .path("/downloadFile/")
                .path(fileName)
                .toUriString();

        return new UploadFileResponse(fileName, fileDownloadUri,
                file.getContentType(), file.getSize());
    }

    @PostMapping("/uploadMultipleFiles")
    public List<UploadFileResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
        return Arrays.asList(files)
                .stream()
                .map(file -> uploadFile(file))
                .collect(Collectors.toList());
    }
}

Service class:

private final Path fileStorageLocation;


    @Autowired
    public FileStorageService(FileStorageProperties fileStorageProperties) {
        this.fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir())
                .toAbsolutePath().normalize();

        try {
            Files.createDirectories(this.fileStorageLocation);
        } catch (Exception ex) {
            throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
        }
    }

    public String storeFile(MultipartFile file) {
        // Normalize file name
        String fileName = StringUtils.cleanPath(file.getOriginalFilename());

        try {
            // Check if the file's name contains invalid characters
            if(fileName.contains("..")) {
                throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
            }

            // Copy file to the target location (Replacing existing file with the same name)
            Path targetLocation = this.fileStorageLocation.resolve(fileName);
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);

            return fileName;
        } catch (IOException ex) {
            throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
        }
    }

Configuration class:

@ConfigurationProperties(prefix = "file")
public class FileStorageProperties {

    private String uploadDir;

    public String getUploadDir()
    {
        return uploadDir;
    }

    public void setUploadDir(String uploadDir) {
        this.uploadDir = uploadDir;
    }
}

Main:

@SpringBootApplication
@EnableConfigurationProperties({
        FileStorageProperties.class
})
public class FileApplication {
    public static void main(String[] args) {
        SpringApplication.run(FileApplication.class, args);
    }
}

properties file

## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB

## File Storage Properties
# All files uploaded through the REST API will be stored in this directory
file.upload-dir=C:/Projects/SpringBootProject/Primesolutions/PrimeSolutions/FileUpload

I'm trying to read the file upload property and pass it to the controller class.

Christopher Schneider
  • 3,482
  • 2
  • 24
  • 36
Akash
  • 123
  • 1
  • 3
  • 6
  • 2
    Is your `FileStorageService` class annotated with `@Service` or `@Component`? It's not clear from the code that you've included. – Jordan Jan 30 '20 at 19:30
  • No jordan it is annotated with @Autowired – Akash Jan 30 '20 at 19:45
  • Your constructor is annotated with `@Autowired`, but for Spring to be able to take a class and autowire it into other classes, the entire class needs to be annotated with `@Component`, or one of the other annotations that implement the same interface (such as `@Service` or `@Controller`). This lets Spring know that this class should be managed by Spring. If you annotate your service at the top level (just above `public class FileStorageService`) with `@Service`, that should solve your issue. – Jordan Jan 30 '20 at 19:50
  • It is possible that sometimes the class is already imported thru' another Jar which interferes with the existing class or interface. – Smart Coder Apr 05 '22 at 17:58

8 Answers8

11

The error seems to indicate that Spring does not know any bean of type com.primesolutions.fileupload.service.FileStorageService.

As said in the comment, make sure you class FileStorageServiceis annotated by @Service or @Component:

@Service
public class FileStorageService {
...
}

Make also sure that this class is located in a sub-package of your class FileApplication. For example, if your FileApplication class is located in a package com.my.package, make sure your FileStorageService is located in the package com.my.package.** (same package or any sub package).

Few notes to improve your code by the way :

  • When your class has only one not default constructor, the use of @Autowired on the constructor is optional.

  • Do not put too much code in your constructor. Use instead the @PostConstruct annotation.


    @Service
    public class FileStorageService {
        private FileStorageProperties props;
        // @Autowired is optional in this case
        public FileStorageService (FileStorageProperties fileStorageProperties) {
            this.props = fileStorageProperties;
            this.fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir())
                    .toAbsolutePath().normalize();
        }

        @PostConstruct
        public void init() {
            try {
                Files.createDirectories(this.fileStorageLocation);
            } catch (Exception ex) {
                throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
            }
        }
    }

  • It is better to avoid the @Autowired on a field. Use the constructor instead. It is better for your tests, and more maintainable:
public class FileController {
    private FileStorageService service;

    public FileController(FileStorageService service) {
        this.service = service;
    }
}
RUARO Thibault
  • 2,342
  • 1
  • 6
  • 12
  • Small question here. Suppose the FileStorage class mentioned inherits from an interface. Which of them is to be annotated as Service? In my case, I annotated both, and it did not seem to have an issue, but it would be great to know which one is to marked. – Lycanthropeus Apr 14 '21 at 09:19
  • Check this link: https://stackoverflow.com/questions/16351780/where-should-service-annotation-be-kept-interface-or-implementation. It is best to annotate the implementation class instead of the interface – RUARO Thibault Jun 20 '21 at 18:35
  • The key for me was where was the class located in a package, Thanks! – Luis Manrique Aug 29 '21 at 15:46
  • Need to ensure that no similar class exists from another imported jar. – Smart Coder Apr 05 '22 at 20:52
4

I solved this problem using where i use @Autowired annotation just replace with this`

@Autowired(required = false)

`

wazirX
  • 59
  • 3
2

Tried with removing the (exclude = {DataSourceAutoConfiguration.class }) parameter with @SpringBootApplication:

Before:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })

public class SpringBootMain { ...

After:

@SpringBootApplication

public class SpringBootMain { ...

Worked for me.

1

When @Autowired doesn’t work

There are several reasons @Autowired might not work.

When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection. Also when you use @Autowired in the class of which you created a new instance, the Spring context will not be known to it and thus most likely this will also fail. Another reason can be that the class you want to use @Autowired in, is not picked up by the ComponentScan. This can basically be because of two reasons.

  1. The package is outside the ComponentScan search path. Move the package to a scanned location or configure the ComponentScan to fix this.

  2. The class in which you want to use @Autowired does not have a Spring annotation. Add one of the following annotatons to the class: @Component, @Repository, @Service, @Controller, @Configuration. They have different behaviors so choose carefully! Read more here.

I solved this problem using :

@ComponentScan({ "com.yourpkg.*" })

Make sure you @ComponentScan cover all classes contains annotatons : @Component, @Repository, @Service, @Controller, @Configuration.

Reference : https://technology.amis.nl/2018/02/22/java-how-to-fix-spring-autowired-annotation-not-working-issues/

Abd Abughazaleh
  • 2,962
  • 1
  • 27
  • 42
1

The class which is going to be Autowired should be marked with @Service or @Component. Also if the class is in different package then need to add the @ComponentScan annotation in the main class as follows.

@ComponentScan({"com.beta.replyservice", "com.beta.ruleService"})
@SpringBootApplication
Senthuran
  • 1,211
  • 1
  • 12
  • 18
0

I use @Service on the service class which has to be Autowired. It solves my error. or you can use @Autowired(required = false) to disable the auto wiring for a particular instance.

0

I had the same issue. It was solved for me when I added a dependency on "spring-webmvc".

Ryan M
  • 15,686
  • 29
  • 53
  • 64
-1

make sure to have respective annotations for classes. Same issue solved for me when i add @Service annotation for interfaces and implemented service classes