Search code examples
springspring-dataspring-orm

What are possible causes for Spring @ComponentScan being unable to auto create a class anotated by @Repository


I came across a tutorial which seemed to be fitting my usecase and tried implementing it. I failed but wasn't sure why. So I tried to find another example with similar code and looked at the book "Spring in Action, Fourth Edition by Craig Walls"

The books describes at page 300 the same basic approach. Define a JdbcTemplate Bean first.

@Bean
NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) {
    return new NamedParameterJdbcTemplate(dataSource);
}

Then a Repository implementing an Interface

@Repository
public class CustomRepositoryImpl implements CustomRepository {

private final NamedParameterJdbcOperations jdbcOperations;

private static final String TEST_STRING = "";

@Autowired
public CustomRepositoryImpl(NamedParameterJdbcOperations jdbcOperations) {
    this.jdbcOperations = jdbcOperations;
}

So I did like the example in the book suggests, wrote a test but got the error message

Error creating bean with name 'de.myproject.config.SpringJPAPerformanceConfigTest': Unsatisfied dependency expressed through field 'abc'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'de.myproject.CustomRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

To my understanding as book and tutorial describe, the Repository should be recognized as a Bean definition by the component scan.

To test this I created an context and asked for all registered Beans.

AnnotationConfigApplicationContext 
context = new AnnotationConfigApplicationContext();
context.getBeanDefinitionNames()

As assumed my Repository wasn't among them. So I increased, for test purposes only, scope of the search in my project, and set it to the base package. Every other Bean was shown, except the Repository.

As an alternative to component scanning and autowiring, the books describes the possibility to simply declare the Repository as a Bean, which I did.

@Bean
public CustomRepository(NamedParameterJdbcOperations jdbcOperations) {
    return new CustomRepositoryImpl(jdbcOperations);
}

After that Spring was able to wire the Repository. I looked at the github code of the book in hope for a better understanding, but unfortunately only the Bean solution, which runs, is implemented there.

So here are my questions:

1.) what possible reasons are there for a Bean definition, is a scenario like this one, not to be recognized by the component scan?

2.) this project already uses Spring JPA Data Repositories, are there any reasons not to use both approaches at the same time?


Solution

  • The problem is naming of your classes. There are many things to understand here.

    1. You define a repository Interface @Repository is optional provided it extends CRUDRepository or one of the repositories provided by spring-data. In this class you can declare methods(find By....). And spring-data will formulate the query based on the underlying database. You can also specify your query using @Query.
    2. Suppose you have a method which involves complex query or something which spring-data cannot do out of the box, in such case we can use the underlying template class for example JdbcTemplate or MongoTemplate..
    3. The procedure to do this is to create another interface and a Impl class. The naming of this interface should be exactly like Custom and your Impl class should be named Impl.. And all should be in same package.

    For example if your Repository name is AbcRepository then Your custom repository should be named AbcRepositoryCustom and the implementation should be named AbcRepositoryImpl.. AbcRepository extends AbcRepositoryCustom(and also other spring-data Repositories). And AbcRepositoryImpl implements AbcRepositoryCustom