0

I started learning Spring Boot last week, so I'm still getting the hang of everything. Autowired seems nice, but I'm occasionally having trouble with it when using it on fields, such as repositories. I've tried searching for what the requirements are for using it, but I can't really find a definitive source.

For example, and please correct any of these if they are wrong, I know:

  • Don't instantiate the field yourself. Otherwise, the Autowiring won't work.
  • The object can't be static, because static fields are set up before Spring is even active (there is a workaround, however).
  • The class the field is in must at least have @Component, or something derived from it.
  • The package with the Autowired field must be at or below the package with @SpringBootApplication (or @ComponentScan).

I had to find points 2, 3, and 4 myself. They weren't listed anywhere and I only came across them in other StackOverflow answers.

What else is there? Essentially, I have:

@Component
public class BookSearchClient {

    @Autowired
    private BookRepository bookRepository;

    public List<Book> processBookResult(String json) {
        GoogleBook googleBooks = JsonUtil.googleBookFromJson(json);
        List<Book> bookList = ConversionUtil.googleBooksToSimpleBooks(googleBooks);
        for (Book book : bookList) {
            bookList.add(book);
            if (findBook(book) == null) {
                bookRepository.save(book);
            }
        }
        return bookList;
    }

    private Book findBook(Book book) {
        Book foundBook = bookRepository.findByIsbn13(book.getIsbn13());
        if (foundBook == null) {
            foundBook = bookRepository.findByIsbn10(book.getIsbn10());
        }

        return foundBook;
    }
}

And then:

@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
    Book findByIsbn10(String isbn10);
    Book findByIsbn13(String isbn13);
}

I am meeting all of these so far, but am still getting a null pointer exception for bookRepository. The controllers worked without me having @Repository on the repositories, but I figured I'd add it to see if it would help (it did not). I have similar classes that also fit the pattern outlined in the list above (specifically, @RestControllers), but in this case it's not working. I know I'm missing something, I just don't know what it is.

User51610
  • 453
  • 5
  • 18
  • Make sure that you've put `@EnableJpaRepositories` annotation on SpringBootApplication class or on some `@Configuration`-marked class – Nikolai Shevchenko Jul 06 '20 at 17:55
  • 1
    Please provide stacktrace too, I suspect NPE might be somewhere else.. – Koray Tugay Jul 06 '20 at 17:58
  • @NikolaiShevchenko I just tried that annotation and it didn't help. – User51610 Jul 06 '20 at 18:02
  • @KorayTugay the stack trace refers to `Book foundBook = bookRepository.findByIsbn13(book.getIsbn13());`. I debugged it, and `book` is fine; `bookRepository` is null. – User51610 Jul 06 '20 at 18:03
  • Can you provide more details ? I find it interesting that Spring was able to autowire this bean. Also read through this question your third point is invalid https://stackoverflow.com/questions/10604298/spring-component-versus-bean – sarcode Jul 06 '20 at 18:11
  • I found that `BookSearchClient` needs to also be `Autowired`. So, it seems like every object along a whole chain needs to be `Autowired` and not manually instantiated? – User51610 Jul 06 '20 at 18:41

2 Answers2

0

I solved this by also making BookSearchClient Autowired. I guess to add another point to the list above (and unrelatedly, possibly removing point 3 according to @sarcode), it seems like every object along the whole chain needs to be @Autowired as well.

User51610
  • 453
  • 5
  • 18
  • 1
    Right, injection only works when both the object that is injected and the object that gets injected into have been created by the CDI container, not by a direct call to the constructor. –  Jul 06 '20 at 19:01
0

try this one. It should be worked.

@RestController
public class BookController
{
        @Autowired
        BookSearchClient bookSearchClient;
}

@Service
public class BookSearchClient {

    @Autowired
    private BookRepository bookRepository;

    public List<Book> processBookResult(String json) {
        GoogleBook googleBooks = JsonUtil.googleBookFromJson(json);
        List<Book> bookList = ConversionUtil.googleBooksToSimpleBooks(googleBooks);
        for (Book book : bookList) {
            bookList.add(book);
            if (findBook(book) == null) {
                bookRepository.save(book);
            }
        }
        return bookList;
    }

    private Book findBook(Book book) {
        Book foundBook = bookRepository.findByIsbn13(book.getIsbn13());
        if (foundBook == null) {
            foundBook = bookRepository.findByIsbn10(book.getIsbn10());
        }

        return foundBook;
    }
}

@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
    Book findByIsbn10(String isbn10);
    Book findByIsbn13(String isbn13);
}

your third point is invalid. you can create a bean instance explicitly in the configuration class and call it via @Autowired annotation.

@Configuration
public class BookConfig
{
 @Bean
 public <className>  getBean()
  {
       return new <className>();
  }

}
Nafaz M N M
  • 1,558
  • 2
  • 27
  • 41
  • Per my comment on my question, this is indeed what I ended up doing. I was not aware of needing to `Autowire` the whole chain of objects. If I think about it, it does make sense. But the API reference online seems to literally be a dump of the javadoc, and the various tutorials and how-tos online don't really mention much about prerequisites / assumptions either. – User51610 Jul 06 '20 at 21:19