Issue
I'm reading Spring Start Here (screenshot attached). The author says it's possible to add multiple beans of the same type to the context using context.registerBean(). I tried adding two Parrots to the context using the following code. The print statements are there to verify that there are two calls of context.registerBean(). Initially, I tried adding the beans like so and still only one Parrot was added.
for (Parrot p : parrots) {
context.registerBean(Parrot.class, parrotSupplier);
}
I'm confused by the results of running this code, as only one bean is registered. How can I add multiple beans of the same type using AnnotationConfigApplicationContext.registerBean()?
Results of running main
Kiki
Kiki
Iteration!
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.event.internalEventListenerFactory
org.springframework.context.event.internalEventListenerProcessor
parrot
projectConfig
Main class
package main;
import config.ProjectConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
import java.util.function.Supplier;
public class Main {
public static void main(String args[]) {
var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
Parrot x = new Parrot();
x.setName("Kiki");
Parrot y = new Parrot();
y.setName("Kiki");
Parrot[] parrots = new Parrot[]{x,y};
Iterator<Parrot> parrotIterator = Arrays.asList(parrots).iterator();
Supplier<Parrot> parrotSupplier = () -> {
if (parrotIterator.hasNext()) System.out.println("Iteration!");
return parrotIterator.next();
};
for (Parrot p : parrots) {
System.out.println(p.getName());
context.registerBean(Parrot.class, parrotSupplier);
}
Parrot p = context.getBean(Parrot.class);
Arrays.asList(context.getBeanDefinitionNames()).stream().sorted().forEach(System.out::println);
}
}
Parrot class
public class Parrot {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
ProjectConfig class
public class ProjectConfig {
}
Solution
You can't register two beans with the same name and the same type, because then there's no way for Spring to distinguish between them. How would Spring ever know, for example, which one to inject in a particular situation? You can qualify injections of a particular type by the bean name, but only if beans of the same type have different names.
If you don't supply a name, Spring computes one from the type of the object. So in your case, you're attempting to register two beans with both the same type and the same name.
I ran your example, and for me it doesn't even run. I get an error:
A bean with that name has already been defined and overriding is disabled.
There's an overload of registerBean
that takes the same two params as the one you're calling, but takes a third name parameter. I changed your code slightly to use this version and to pass two different names:
boolean first = true;
for (Parrot p : parrots) {
System.out.println(p.getName());
context.registerBean(first? "first" : "second", Parrot.class, parrotSupplier);
first = false;
}
This version of the code works without throwing an error. I assume that since it refused to do so before, it is now registering both beans.
UPDATE: Reading that error message again, I wondered if maybe you can't register two beans with the same name even if they have different types. It turns out that this is true. Every Spring bean has to have a unique name. Since the name is computed from the type of the object if not specified, you can register one bean of any particular type without specifying a name.
Answered By - CryptoFool
Answer Checked By - David Goodson (JavaFixing Volunteer)