Bean Instantiation Workflow
Within the Spring Framework, the AbstractAutowireCapableBeanFactory class orchestrates the creation of bean instances. The core method responsible for this operation evaluates the RootBeanDefinition to determine the appropriate instantiation strategy. The execution flow follows a specific sequence of checks to identify how to instantiate the object.
protected BeanWrapper generateBeanInstance(String identifier, RootBeanDefinition beanDef, @Nullable Object[] arguments) {
// 1. Resolve the actual class object for the bean
Class> targetClass = resolveBeanClass(beanDef, identifier);
// 2. If a Supplier is attached to the definition, use it to generate the instance
Supplier> instanceSupplier = beanDef.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, identifier);
}
// 3. Handle instantiation via factory methods (e.g., @Bean configuration)
if (beanDef.getFactoryMethodName() != null) {
return constructViaFactoryMethod(identifier, beanDef, arguments);
}
// 4. Optimization: Reuse resolved constructor information for prototype beans
boolean constructorResolved = false;
boolean requiresAutowiring = false;
if (arguments == null) {
synchronized (beanDef.constructorArgumentLock) {
if (beanDef.resolvedConstructorOrFactoryMethod != null) {
constructorResolved = true;
requiresAutowiring = beanDef.constructorArgumentsResolved;
}
}
}
if (constructorResolved) {
return requiresAutowiring
? autowireConstructor(identifier, beanDef, null, null)
: instantiateWithNoArg(identifier, beanDef);
}
// 5. Detect candidate constructors using SmartInstantiationAwareBeanPostProcessors
// This allows extensions like @Autowired detection to influence the choice
Constructor>[] selectedConstructors = determineConstructorsFromPostProcessors(targetClass, identifier);
// 6. Trigger constructor autowiring if candidates are found, or if configuration demands it
boolean shouldAutowire = (selectedConstructors != null
|| beanDef.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR
|| beanDef.hasConstructorArgumentValues()
|| !ObjectUtils.isEmpty(arguments));
if (shouldAutowire) {
return autowireConstructor(identifier, beanDef, selectedConstructors, arguments);
}
// 7. Check for explicitly preferred constructors (e.g., in Kotlin)
selectedConstructors = beanDef.getPreferredConstructors();
if (selectedConstructors != null) {
return autowireConstructor(identifier, beanDef, selectedConstructors, null);
}
// 8. Default strategy: Use the no-argument constructor
return instantiateWithNoArg(identifier, beanDef);
}
The logic above prioritizes explicit instantiation mechanisms like Suppliers and factory methods. If those are absent, it attempts to reuse cached constructor metadata to save processing time for prototype beans. When no cached data exists, it delegates to SmartInstantiationAwareBeanPostProcessor implementations (such as AutowiredAnnotationBeanPostProcessor) to find annotated constructors. If specific candidates exist or the definition implies constructor injection, it invokes the autowiring logic; otherwise, it defaults to the no-argument constructor.
Identifying Candidate Constructors
The process of identifying which constructor to use is largely handled by AutowiredAnnotationBeanPostProcessor. This component scans the target class for @Autowired annotations and applies specific rules to filter the available constructors.
public Constructor>[] resolveEligibleConstructors(Class> targetClass, final String beanName) {
Constructor>[] cachedCandidates = this.candidateConstructorsCache.get(targetClass);
if (cachedCandidates == null) {
synchronized (this.candidateConstructorsCache) {
cachedCandidates = this.candidateConstructorsCache.get(targetClass);
if (cachedCandidates == null) {
Constructor>[] allDeclaredConstructors;
try {
allDeclaredConstructors = targetClass.getDeclaredConstructors();
} catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Constructor resolution failed for class [" + targetClass.getName() + "]", ex);
}
List> potentialList = new ArrayList<>(allDeclaredConstructors.length);
Constructor> requiredConstructor = null;
Constructor> noArgConstructor = null;
for (Constructor> ctor : allDeclaredConstructors) {
// Check for @Autowired annotation on the constructor
MergedAnnotation> autowiredAnnotation = findAutowiredAnnotation(ctor);
if (autowiredAnnotation != null) {
// Validation: Only one 'required=true' constructor is allowed
if (requiredConstructor != null) {
throw new BeanCreationException(beanName,
"Invalid autowire configuration: found multiple required constructors.");
}
boolean isRequired = autowiredAnnotation.getBoolean("required");
if (isRequired) {
if (!potentialList.isEmpty()) {
throw new BeanCreationException(beanName,
"Invalid autowire configuration: found required constructor with other candidates.");
}
requiredConstructor = ctor;
}
potentialList.add(ctor);
} else if (ctor.getParameterCount() == 0) {
// Track the default no-argument constructor
noArgConstructor = ctor;
}
}
// Finalize the list of candidates based on analysis
if (!potentialList.isEmpty()) {
// If no single 'required' constructor exists, add the no-arg constructor as a fallback
if (requiredConstructor == null && noArgConstructor != null) {
potentialList.add(noArgConstructor);
}
cachedCandidates = potentialList.toArray(new Constructor>[0]);
}
// Edge case: Only one constructor exists and it has arguments
else if (allDeclaredConstructors.length == 1 && allDeclaredConstructors[0].getParameterCount() > 0) {
cachedCandidates = new Constructor>[] { allDeclaredConstructors[0] };
}
else {
cachedCandidates = new Constructor>[0];
}
this.candidateConstructorsCache.put(targetClass, cachedCandidates);
}
}
}
return (cachedCandidates.length > 0 ? cachedCandidates : null);
}
The algorithm iterates through all declared constructors. It distinguishes between constructors marked with @Autowired and those without. It enforces the rule that only one constructor can have required=true. If multiple constructors are marked as optional (required=false), or if only a single non-default constructor exists in the class, they are added to the candidate list. The result is cached to optimize subsequent instantiations.
Constructor Selection Scenarios
The behavior of the container varies depending on the number of constructors defined in the class.
If a class contains only one constructor, the container attempts to use it. If it is a no-argument constructor, instantiation is trivial. If it is a parameterized constructor, the container attempts to resolve the dependencies:
- In AnnotationConfigApplicationContext, dependencies are resolved automatically by type.
- In ClassPathXmlApplicationContext, dependencies can be explicitly defined via XML tags, or resolved automatically if the
autowireattribute is set.
When multiple constructors are present, the ambiguity must be resolved. Spring follows a hierarchy of preference:
- Explicit Annotation Selection: Developers use
@Autowiredon the desired constructor. If one constructor is markedrequired=true, it is chosen exclusively. If multiple are markedrequired=false, the container attempts to satisfy the one with the most resolvable parameters. - XML-Based Configuration: Using the
<constructor-arg>tag in XML allows developers to explicitly specify which constructor to use by matching argument count and types. - Constructor Autowiring Mode: In XML configuration, setting
autowire="constructor"instructs the container to inspect the available constructors and choose the one with the most arguments that can be satisfied by the Spring context. - Default Fallback: If no explicit guidance is provided, the container defaults to the no-argument constructor. If a no-argument constructor is absent in this scenario, the application context will fail to start.
Understanding these rules is crucial for configuring beans effectively, particularly when dealing with complex dependency graphs or legacy codebases. The interaction between annotation-based injection and XML configuration offers flexibility but requires a clear understanding of the resolution order.