0

I have a problem with Spring bean DefaultConfigurationService initialization that is extended from abstract class. I am totally stuck.

Class hiearchy is as follows:

public interface ConfigurationService {
    TaxPayer getTaxPayer();
} 

This class is mentioned to be useful for services that need to be initialized:

public abstract class BaseInitializationAwareService {

    private boolean initialized = false;

    public abstract void initialize();

    protected void checkInitialization() {
        if (!initialized) {
            initialize();
        }
    }

    protected void setInitialized() {
        this.initialized = true;
    }
}

This class acts as base class for Configuration service.

public abstract class BaseConfigurationService extends BaseInitializationAwareService implements ConfigurationService {


}

And with this bean, that acts as a configuration service, is a problem:

    public class DefaultConfigurationService extends BaseConfigurationService {

        private TaxPayerService taxPayerService;

        @Autowired
        public void setTaxPayerService(TaxPayerService taxPayerService) {
            Assert.notNull(taxPayerService);
            this.taxPayerService = taxPayerService;
        }

public void initialize() {
        Optional<TaxPayer> dbtaxPayer = taxPayerService.getActiveTaxPayer();
        if (!dbtaxPayer.isPresent()) {
            throw new IllegalStateException("Tax payer setting not found!");
        }
        this.taxPayer = dbtaxPayer.get();
        setInitialized();
    }

    // the rest omitted...
    }

when I'm creating DefaultConfigurationService bean:

@Bean
    public BaseConfigurationService configurationService() {
        DefaultConfigurationService configurationService = new DefaultConfigurationService();
        configurationService.initialize();
        return configurationService;
    }

then taxPayerService in DefaultConfigurationService is null - it seems that is not autowired.

Can it be connected to the fact that DefaultConfigurationService is extended from abstract class?

TaxPayer service bean:

@Bean
    public TaxPayerService taxPayerService() {
        DatabaseTaxPayerService taxPayer = new DatabaseTaxPayerService();
        return taxPayer;
    }

This bean is probably never initialized...

Thats is a exception:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webSecurityConfig.ApiSecurity': Unsatisfied dependency expressed through method 'setContentNegotationStrategy' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'passwordRecoverController': Unsatisfied dependency expressed through method 'setUserService' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'defaultUserService': Unsatisfied dependency expressed through method 'setNotificationService' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'notificationService': Unsatisfied dependency expressed through method 'setConfigurationService' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'configurationService' defined in class path resource [com.example.config/AppConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.services.BaseConfigurationService]: Factory method 'configurationService' threw exception; nested exception is java.lang.NullPointerException


For example, bean that needs BaseConfigurationService:

    public class EmailNotificationService extends BaseService implements NotificationService {

        private BaseConfigurationService configurationService;

@Autowired
    public void setConfigurationService(BaseConfigurationService configurationService) {
        Assert.notNull(configurationService);
        this.configurationService = configurationService;
    }

    // the rest omitted... 
    }

#update 1

Bean initialization example with internal dependencies to another beans:

@Bean
    public TransactionDataService transactionDataService() {
        return new DefaultTransactionDataService();
    }

and DefaultTransactionDataService:

public class DefaultTransactionDataService implements TransactionDataService {
    private PrivateKeyService privateKeyService;

    @Autowired
    public void setPrivateKeyService(PrivateKeyService privateKeyService) {
        Assert.notNull(privateKeyService);
        this.privateKeyService = privateKeyService;
    }
}

and bean dependency

@Bean
    public PrivateKeyService privateKeyService() {
        return new DefaultPrivateKeyAwareService();
    }

and it works.

jnemecz
  • 3,171
  • 8
  • 41
  • 77
  • 1
    The problem is you are calling `initialize` from inside your `@Bean` method. That will be invoked before Spring even had a change on injecting the dependencies. Instead of calling the method yourself specify it as the `initMethod` in the `@Bean` method. That will make spring first create the bean, inject dependencies and then call the init method. – M. Deinum Feb 02 '17 at 10:33

1 Answers1

0

It is related to your instruction to Spring how to initialize your bean:

@Bean is used to explicitly declare a single bean, rather than letting Spring do it automatically as above. It decouples the declaration of the bean from the class definition, and lets you create and configure beans exactly how you choose.

So when you create instance of your class manually your have to set all class field manually as well.

It can be done like this:

@Bean
public BaseConfigurationService configurationService() {
    DefaultConfigurationService configurationService = new DefaultConfigurationService();
    configurationService.setTaxPayerService(taxPayerService());
    configurationService.initialize();
    return configurationService;
}

Or move dependency directly to constructor, so dependency of your DefaultConfigurationService will be more obvious:

@Bean
public BaseConfigurationService configurationService() {
    DefaultConfigurationService configurationService = new DefaultConfigurationService(taxPayerService());
    configurationService.initialize();
    return configurationService;
}

Good description

Community
  • 1
  • 1
Sergii Getman
  • 3,845
  • 5
  • 34
  • 50
  • Thank you. Do you have some more information about your answer? I'm not really sure about it because the problem I described happens after I created mentioned abstract class. And in my app there are many beans that are initialized with `@Bean` annotation and have internal dependencies. And it worked well for me until now. – jnemecz Feb 02 '17 at 09:42
  • you can follow my recommendation or not. it's up you. i provided you with link with more extended description. Do you have `DefaultConfigurationService ` annotated with `@Component`? What time you get an exception? Where do you invoke your `configurationService()`? – Sergii Getman Feb 02 '17 at 09:52
  • @Sergei - maybe we don't understand earch other, please read my answer, I wrote that I have many beans that are initialized with `@Bean` annotation, have internal dependencies and these beans were initialized properly. Please see my #1 updated please. – jnemecz Feb 02 '17 at 10:24
  • i understood you, if create class with `new` you create it with usual java approach. do you understand difference between between spring object creation and plane java object creation? – Sergii Getman Feb 02 '17 at 10:29
  • Please see code I mentioned in my #1 update, it will be clear. – jnemecz Feb 02 '17 at 10:30
  • if you create bean with `new` : `new DefaultTransactionDataService()` nothing was injected by spring in its particular object. it's it – Sergii Getman Feb 02 '17 at 10:32
  • 1
    `@Bean` is basically the same as the XML `bean` declaration. If you have `@Autowired` fields Spring will inject those, you don't have to do anything manually. However if you are calling methods before spring has time to do dependency injection (which is the case here) nothing is injected. – M. Deinum Feb 02 '17 at 10:36
  • @M.Deinum what do you mean `time to do dependency injection`? in both annotation and xml bean you are not autowiring (you wire explicit) – Sergii Getman Feb 02 '17 at 10:48
  • Not with `@Autowired` What you are proposing is to do all the wiring yourself, that basically beats the whole purpose of `@Autowired` Even with XML when you had `@Autowired` you only need to declare the bean and the annotation processors (when enabled) would do the rest. – M. Deinum Feb 02 '17 at 10:49
  • @M.Deinum I just explain how to avoid null. To support all auto wired chain we have to use '@Component' – Sergii Getman Feb 02 '17 at 10:51
  • @SergiiGetman, please, do not edit your comments, later it doesn't make sense what the others reply to your previous comment meaning... – jnemecz Feb 02 '17 at 11:08
  • @user1315357 what exactly comment you mean? – Sergii Getman Feb 02 '17 at 11:14
  • `@Component` isn't mandatory for `@Autowired`. It applies to all beans registered regardless if they are detected by component scanning or manually created by `@Bean` methods or `bean` elements in XML configuration. – M. Deinum Feb 03 '17 at 09:43
  • @M.Deinum don't agree with you https://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-java-bean-annotation – Sergii Getman Feb 03 '17 at 10:14
  • I suggest you do a little test... Create an xml file. Create 2 beans without `@Component` and add `@Autowired` on the fields. Just register them in xml, add `` and load the xml file... The auto wiring will be done. You don't need component-scanning or `@Component` for that. – M. Deinum Feb 03 '17 at 10:19
  • BTW the documentation you point at actually supports my claim. The same applies to beans created with `@Bean` they don't have to be `@Component`s or scanned and still be able to use `@Autowired`. As stated I suggest you do a little test. (Although I have been using Spring for ~ 14 years and written a couple of books about it I kind of know what I'm talking about :) ). – M. Deinum Feb 03 '17 at 10:21
  • @M.Deinum are we on the same page ? : ) let's look at `@Bean public CoachService coachService() { return new CoachService(); }` and `public class CoachService { @Autowired private CoachRepository coachRepository;` in this case autowired property `coachRepository` will be null, but if we inject this @Bean `coachService` somewhere else - yes, it can be autowired. And yes, I know that you are Spring expert that's why I was so surprised. but possibly i understood you in incorrect way : ) – Sergii Getman Feb 03 '17 at 10:32
  • No it won't be null... In fact it cannot be null as in that case your application would blow up at the moment it started because it couldn't do auto wiring. When using java based configuration annotation processing is enabled by default (as opposed to xml based configuration which has to be told it needs to switch on annotation processing). – M. Deinum Feb 05 '17 at 07:31