11

In my Java spring application, I have

public class BinarySearchImpl {

    @Autowired
    @Qualifier("Quick")
    SortAlgorithem sorter;

    Log log=LogFactory.getLog(BinarySearchImpl.class);

    public BinarySearchImpl(SortAlgorithem sorter) {
        log.info("Binary Search Bean is created");
        this.sorter=sorter;
    }

SortAlgorithem is an interface which makes my application loosely coupled:

public interface SortAlgorithem {

    public int[] sort(int[] arrayNumbers);

}

And then there are 2 implementations for this interface. One is BubbleSort:

@Component
@Qualifier("Bubble")
public class BubbleSort implements SortAlgorithem {

    Log log=LogFactory.getLog(BubbleSort.class);

    public int[] sort(int[] numbers) {
        log.info("Bubble sort is called");
        return numbers;
    }
}

and the other is QuickSort:

@Component
@Qualifier("Quick")
//@Primary
public class QuickSort implements SortAlgorithem{

    Log log= LogFactory.getLog(QuickSort.class);

    public int[] sort(int[] numbers) {
        log.info("Quick Sort is called");
        return numbers;
    }

}

At the end, when I call my app it complains with this message:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

I am wondering... Why @Qualifier annotation does not work?

Paulo Merson
  • 13,270
  • 8
  • 79
  • 72
Jeff
  • 7,767
  • 28
  • 85
  • 138

6 Answers6

11

You have annotated a field with @Autowired and @Qualifier, but you have also created a constructor which sets the field.

I think that Spring is using the constructor, but doesn't automatically know that the constructor parameter corresponds to the annotated field.

So move the annotations into the constructor declaration:

private SortAlgorithm sorter;

@Autowired 
public BinarySearchImpl(@Qualifier("quick") SortAlgorithm sorter) {
     this.sorter = sorter;
}

Alternatively, you could use a zero-arg constructor, keep your field annotation and let Spring inject using reflection. However in my opinion constructor-injection is better -- it allows you to unit test cleanly, without involving Spring or reflection.

As other answers point out, there are other ways to disambiguate autowired beans -- and the Spring docs explain them all -- but using qualifiers like this does work.

slim
  • 40,215
  • 13
  • 94
  • 127
8

The correct approach:

public interface SortAlgorithem {
    public int[] sort(int[] arrayNumbers);
}

@Component("Bubble")
public class BubbleSort implements SortAlgorithem {
    Log log = LogFactory.getLog(BubbleSort.class);

    public int[] sort(int[] numbers) {
        log.info("Bubble sort is called");
        return numbers;
   }
}

@Primary
@Component("Quick")
public class QuickSort implements SortAlgorithem {
    Log log = LogFactory.getLog(QuickSort.class);

    public int[] sort(int[] numbers) {
        log.info("Quick Sort is called");
        return numbers;
    }
}

and then you need to use your implementations like this:

@Autowired
@Qualifier(value = "Bubble")
private SortAlgorithem bubbleSort;

@Autowired
@Qualifier(value = "Quick")
private SortAlgorithem quickSort;
Shrikant Wandhare
  • 2,301
  • 1
  • 9
  • 7
1

try

@Component("qualifier_name")
Sujal Mandal
  • 975
  • 1
  • 14
  • 29
1

You have to declare your components like this:

@Component("Bubble")
public class BubbleSort implements SortAlgorithem {

    Log log=LogFactory.getLog(BubbleSort.class);

    public int[] sort(int[] numbers) {
        log.info("Bubble sort is called");
        return numbers;
    }
}

and

@Component("Quick")
public class QuickSort implements SortAlgorithem{

    Log log= LogFactory.getLog(QuickSort.class);

    public int[] sort(int[] numbers) {
        log.info("Quick Sort is called");
        return numbers;
    }

}

This should solve your issue.

Chaitanya
  • 15,403
  • 35
  • 96
  • 137
1

Hi Sorry for coming late to the party. I had 2 service class which were implementing a single interface like this,So I have done this with @Component("name") and it worked. though it is still a mystery why @Qualifier("name") did not worked?

Code:

@Service("signup")
//@Qualifier("signup")
public class SignUpGenerator implements ITestCaseGenerator {

}




@Component
public class SelUtil {

    @Autowired
    @Qualifier("login")
    ITestCaseGenerator login;

    @Autowired
    @Qualifier("signup")
    ITestCaseGenerator signUp;

}
ThinkTank
  • 1,737
  • 5
  • 22
  • 36
0

I had this problem when using @RequiredArgsConstructor annotation, and had to remove it (and use manual constructor with a @Qualifier annotation as per @slim answer).

diman82
  • 702
  • 8
  • 11
  • In my IntelliJ 2021.2.3 there is a helpful message for this: `Lombok does not copy the annotation 'org.springframework.beans.factory.annotation.Qualifier' into the constructor`. – lisymcaydnlb Dec 12 '21 at 20:12