简体   繁体   中英

Why is @Bean(initMethod="") not detecting given method in spring?

Edit Fixed by changing package.

I have this configuration file for spring framework

@Configuration
public class AppConfig {
   @Bean(initMethod = "populateCache")
    public AccountRepository accountRepository(){
       return new JdbcAccountRepository();
   }
}

JdbcAccountRepository looks like this.

@Repository
public class JdbcAccountRepository implements AccountRepository {
    @Override
    public Account findByAccountId(long 
        return new SavingAccount();
    }

    public void populateCache() {
        System.out.println("Populating Cache");
    }

    public void clearCache(){
        System.out.println("Clearing Cache");
    }
}

I'm new to spring framework and trying to use initMethod or destroyMethod. Both of these method are showing following errors.

Caused by: org.springframework.beans.factory.support.BeanDefinitionValidationException: Could not find an init method named 'populateCache' on bean with name 'accountRepository'

Here is my main method.

public class BeanLifeCycleDemo {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new
                AnnotationConfigApplicationContext(AppConfig.class);
        AccountRepository bean = applicationContext.getBean(AccountRepository.class);
        applicationContext.close();
    }
}

Edit I was practicing from a book and had created many packages for different chapters. Error was it was importing different JdbcAccountRepository from different package that did not have that method. I fixed it and it works now. I got hinted at this from answers.

Like you said, if you are mixing configurations types, it can be confusing. Besides, even if you created a Bean of type AccountRepository , because Spring does a lot of things at runtime, it can call your initMethod, even if the compiler couldn't.

So yes, if you have many beans with the same type, Spring can be confused an know which one to call, hence your exception.

Oh and by the way, having a Configuration creating the accountRepoisitory Bean , you can remove the @Repository from your JdbcAccountRepository ... It is either @Configuration + @Bean or @Component/Repository/Service + @ComponentScan .

TL;DR

Here is more information and how Spring creates your bean : What object are injected by Spring ?

@Bean(initMethod = "populateCache")
public AccountRepository accountRepository(){
    return new JdbcAccountRepository();
}

With this code, Spring will :

  • Detect that you want to add a Bean in the application Context
  • The bean information are retrieved from the method signature. In your case, it will create a bean of type AccountRepository named accountRepository ... That's all Spring knows, it won't look inside your method body.
  • Once Spring is done analysing your classpath, or scanning the bean definitions, it will start instanciating your object.
  • It will therefor creates your bean accountRepository of type AccountRepository .
  • But Spring is "clever" and nice with us. Even if you couldn't write this code without your compiler yelling at you, Spring can still call your method.

To make sure, try writing this code :

AccountRepository accountRepository = new JdbcAccountRepository();
accountRepository.populateCache(); // Compiler error => the method is not found.

But it works for Spring... Magic.

My recommandation, but you might thinking the same now: If you have classes across many packages to answer different business case, then rely on @Configuration classes. @ComponentScan is great to kickstart your development, but reach its limit when your application grows...

Annotate populateCache method with @PostConstruct and remove initMethod from @Bean . It will work.

You mix two different ways of spring bean declaration:

  1. Using @Configuration classes. Spring finds all beans annotated with @Configuration and uses them as a reference to what beans should be created. So if you follow this path of configuration - don't use @Repository on beans. Spring will detect it anyway

  2. Using @Repository - other way around - you don't need to use @Configuration in this case. If you decide to use @Repository put @PostConstruct annotation on the method and spring will call it, in this case remove @Configuration altogether (or at least remove @Bean method that creates JdbcAccountRepository )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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