dimanche 1 mars 2015

Beans defined with @Bean behave differently in Autowiring compared to beans defined with xml or @Component

I'm using Spring 4.1.5. Beans defined with @Bean act strange. Basically I am having problems Autowiring those beans when the dependency type is different from what is defined in the @Bean method signature.


For example if I define a @Bean with its interface type (MessageService) then I cannot autowire it in another dependent bean with its implementation type (MessageServiceImpl) (I am not putting proxies in the picture). Not even with the type of another interface that it happens to implement. These scenarios work as expected when the bean is defined in xml or with @Component. Here is the code:


Main interface



package hello.annotations;

public interface MessageService {
String getMessage();
}


Secondary interface



package hello.annotations;

public interface AnotherInterface {
boolean anotherMethod();
}


Implementation



package hello.annotations;

public class MessageServiceImpl implements MessageService, AnotherInterface {
public String getMessage() {
return "my msg";
}

public boolean anotherMethod() {
return true;
}
}


Dependent bean



package hello.annotations;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

public class MessagePrinter {

@Autowired
private MessageServiceImpl service1;

@Autowired
private AnotherInterface service2;

@Autowired
private MessageService service;

public void printMessage() {
System.out.println(System.identityHashCode(service));
System.out.println(System.identityHashCode(service1));
System.out.println(System.identityHashCode(service2));
System.out.println(this.service.getMessage());
}
}


Application



package hello.annotations;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Application {

@Bean
MessageService mockMessageService() {
return new MessageServiceImpl();
}

@Bean
MessagePrinter messagePrinter() {
return new MessagePrinter();
}

public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
MessagePrinter printer = context.getBean(MessagePrinter.class);
printer.printMessage();
}
}


So you can see that in MessagePrinter, I am trying to inject the MessageServiceImpl with various ways: as a MessageService interface, as a MessageServiceImpl, and as an AnotherInterface.


I would say that this code doesn't work and it throws this error:



Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private hello.annotations.MessageServiceImpl hello.annotations.MessagePrinter.service1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [hello.annotations.MessageServiceImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 12 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [hello.annotations.MessageServiceImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
... 14 more{@org.springframework.beans.factory.annotation.Autowired(required=true)}


But here is where it gets even stranger: I just noticed that this is not deterministic. Sometimes it works. Run it 5-6 times you should notice it work sometimes. And another observation: If I change the order of the dependency fields in MessagePrinter putting the



@Autowired private MessageService service;


first, then I think it always works. At least as many times as I have run it.


Is this a bug or am I missing something?


Thanks and sorry for the long post.


Aucun commentaire:

Enregistrer un commentaire