Reputation: 55
I try to improve my Spring knowledge by reading Spring in action 4. When I've to to section, describing using of Qualifier annotation (3.3.2), i faced the problem. To test this annotation in action, I wrote Dessert interface, which is implemented by 3 classes, creating in context using @Component annotation. I also created class Taster, which "tastes" some dessert, autowired into by some qualifier. When I run my application, using AnnotationConfigApplicationContext - everything works good. With SpringJUnit4ClassRunner - it does not. I guess I miss something in my test code, but I do not have enough knowledge to realize what. Interface:
package bakery.intrface;
@FunctionalInterface
public interface Dessert {
void introduce();
}
Cake:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
@Component
public class Cake implements Dessert {
@Override
public void introduce() {
System.out.println("I am a cake!");
}
}
Cookie:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
@Component
public class Cookie implements Dessert {
@Override
public void introduce() {
System.out.println("I'm a cookie!");
}
}
Ice cream:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
@Component
public class IceCream implements Dessert {
@Override
public void introduce() {
System.out.println("I'm an ice cream!");
}
}
The class, consumes some bean, Taster:
package bakery;
import bakery.intrface.Dessert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class Taster {
private Dessert dessert;
public void taste(){
dessert.introduce();
}
@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
}
Configuration:
package bakery.config;
import bakery.Bakery;
import bakery.Taster;
import bakery.desserts.Cake;
import bakery.intrface.Dessert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackageClasses = Bakery.class)
public class BakeryConfig {
}
Run class:
package bakery;
import bakery.config.BakeryConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Bakery {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BakeryConfig.class);
String[] beans = context.getBeanDefinitionNames();
Taster taster = (Taster) context.getBean("taster");
taster.taste();
}
}
Test class:
package bakery;
import bakery.config.BakeryConfig;
import bakery.intrface.Dessert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BakeryConfig.class)
public class BakeryTest {
@Autowired
Dessert dessert;
@Autowired
Taster taster;
@Test
public void contextInit(){
assertNotNull(dessert);
dessert.introduce();
}
@Test
public void tasterInit(){
assertNotNull(taster);
}
}
When I run the test, I'm getting the exception: No qualifying bean of type [bakery.intrface.Dessert] is defined: expected single matching bean but found 3: cookie,iceCream,cake.
Upvotes: 0
Views: 4822
Reputation: 2070
This is to be expected.
The declaration
@Autowired
Dessert dessert;
is asking for a Dessert
object. Dessert
is the interface, and there are three implementing classes, Cookie
, IceCream
, and Cake
. Since you haven't made it more explicit which of those implementations you want, Spring throws an error because it can't decide what to do.
If you need this in your test, you can do one of the following:
@Autowired
@Qualifier("iceCream")
Dessert dessert;
to get only the ice cream dessert,
OR
@Autowired
List<Dessert> desserts;
to get a list containing all the implementations.
Upvotes: 1
Reputation: 717
There are 3 "Dessert" beans in your application context, you have to specify which one you want to wire.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BakeryConfig.class)
public class BakeryTest {
@Autowired
@Qualifier("iceCream") // <===================== you must specify which bean to be wired
Dessert dessert;
@Autowired
Taster taster;
Upvotes: 2