Home > Back-end >  SpringBoot Bean dependency injection with annotation @Primary and @Secondary
SpringBoot Bean dependency injection with annotation @Primary and @Secondary

Time:10-08

I have a repository interface and two classes implementing it, one is cache repository and the other is MongoDB repository.

public interface Repository {}

@Primary
@Component
public class CacheRepo implement Repository {}

@Component
public class MongoDBRepo implement Repository {}

The ideal process for fetching an item would be to check if it exists in the cache using cache repo and if not, go with MongoDB repo, I have a @Primary on my CacheRepo class, and dependency inject Repository interface in my service, but how could I still use the same injected instance as MongoDBRepo if item not found in Cache? Is there something like @Secondary annotation?

CodePudding user response:

I would suggest you take a look at @Qualifier. With it, you can specify which Bean you want to inject. For that, the best option is to define a component name for each bean as follows:

public interface Repository {}

@Component("cache")
public class CacheRepo implement Repository {}

@Component("mongodb")
public class MongoDBRepo implement Repository {}

Then, in another Spring-managed bean you can specify the implementation as follows:

@Autowired
@Qualifier("mongodb")
private Repository mongoRepo;

You can read more about it at https://www.baeldung.com/spring-qualifier-annotation.

CodePudding user response:

I think you misunderstood the purpose of @Primary.
As the documentation says:

Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.

You may use @Cacheble annotation on the MongoDBRepo's methods to do what you want and define a custom cache manager to use your CacheRepo

Here is a useful introduction to @Cacheble with some examples https://www.baeldung.com/spring-cache-tutorial

CodePudding user response:

What you are trying to implement is Repository Pattern

Here is a simple way to implement it

public interface MyRepository {
  Optional<MyClass> findById(Long id);
}

Then you will have 3 implementations. This is where the logic lies.

@Repository
@Qualifier("db")
public interface MyDbRepository extends MyRepository, CrudRepository<MyClass, Long>{
}

@Component
@Qualifier("cache")
public class MyCacheRepository implements MyRepository {
  public Optional<MyClass> findById(Long id){
    return Optional.empty();
  }
}

// This is the key
@Component
@Primary
public class MyDataSource implements MyRepository {

  @Autowire
  @Qualifier("db")
  private MyRepository dbRepo;

  @Autowire
  @Qualifier("cache")
  private MyRepository cacheRepo;

  public Optional<MyClass> findById(Long id){
    Optional<MyClass> myResponse = myCache.findById(id);
    if(myResponse.isPresent()){
      return myResponse;
    } else {
      myResponse = dbRepo.findById(id);
      if(myResponse.isPresent){
        // Update your cache here
      }
      return myResponse;
    }
  }

}
  • Related