Understanding IoC and AOP in refresh(): How BeanFactory and BeanDefinition Power Your Application
The refresh() method is one of the most critical methods in the Spring framework. It serves as the entry point for the entire application context lifecycle, and it’s also where two core features of Spring—Inversion of Control (IoC) and Aspect-Oriented Programming (AOP)—are initialized and integrated.
Both Spring Framework and Spring Boot utilize BeanFactory and BeanDefinition concepts similarly.
Note (Spring Boot 3.x): Spring Boot has increasingly moved away from XML-based configuration in favor of annotations and Java-based configurations (
@Configuration,@ComponentScan, etc.), making traditional XML bean definitions less common in modern applications.
BeanFactory: The Core of IoC Container in Spring Framework and Spring Boot
A BeanFactory is the factory responsible for managing and instantiating beans in Spring. During the execution of refresh(), the method obtainFreshBeanFactory() is called to retrieve or initialize the BeanFactory.
In Spring Framework
// From AbstractApplicationContext
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
In AbstractRefreshableApplicationContext class, refreshBeanFactory() is implemented as follows:
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source", ex);
}
}
The default implementation of the BeanFactory in both Spring and Spring Boot is DefaultListableBeanFactory.
In Spring Boot
In contrast, Spring Boot’s GenericApplicationContext creates the BeanFactory during context initialization, not during the refresh() call.
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts");
}
this.beanFactory.setSerializationId(getId());
}
Spring Boot initializes the BeanFactory earlier to optimize startup performance and make auto-configuration features easy.
BeanDefinition: Metadata Driving Bean Creation in Spring IoC and Boot
A BeanDefinition is a meta-description of a bean. It includes the bean’s class name, scope (singleton/prototype), properties, constructor arguments, and more. Spring parses XML configurations, Java annotations, or Java configuration classes to generate BeanDefinition objects.
These definitions are then used to create actual bean instances.
XML-based Configuration Flow in Spring Framework
Here’s how Spring processes XML-based bean definitions:
obtainFreshBeanFactory()is invoked, which internally callsloadBeanDefinitions(beanFactory)- This triggers a series of function calls leading to the parsing of the XML document.
Call Chain
| Class | Method |
|---|---|
AbstractXmlApplicationContext |
loadBeanDefinitions(DefaultListableBeanFactory) |
AbstractXmlApplicationContext |
loadBeanDefinitions(XmlBeanDefinitionReader) |
AbstractBeanDefinitionReader |
loadBeanDefinitions(String... locations) |
XmlBeanDefinitionReader |
doLoadBeanDefinitions(InputSource, Resource) |
Key Implementation
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
return count;
}
Then, in XmlBeanDefinitionReader.
public int registerBeanDefinitions(Document doc, Resource resource) {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
Finally, in DefaultBeanDefinitionDocumentReader.
protected void doRegisterBeanDefinitions(Element root) {
this.delegate = createDelegate(getReaderContext(), root, parent);
parseBeanDefinitions(root, this.delegate);
}
And at last. This is where each <bean> tag is transformed into a BeanDefinition object.
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "bean")) {
processBeanDefinition(ele, delegate);
}
}
Annotation-based Configuration in Spring Boot (Auto-Configuration & Component Scan)
Spring Boot does not rely on XML files by default. Instead, it uses annotation-driven configuration with the @Configuration and @ComponentScan annotations.
The core registration logic moves to:
// From AbstractApplicationContext
invokeBeanFactoryPostProcessors(beanFactory);
This is where Spring Boot auto-configuration classes and other @Bean-defined methods are scanned, parsed, and turned into BeanDefinition instances.
Instead of XML files, use
@Configuration,@Component,@Service, and@Repositoryannotations with component scanning in Spring Boot.
Java Configuration Classes with @Configuration and @Bean in Spring Boot
Besides XML and annotation-based component scanning, Java-based configuration using @Configuration classes is another widely used approach for defining Spring beans.
This method uses explicit Java classes and @Bean methods to define beans.
Example
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserServiceImpl();
}
@Bean
public UserRepository userRepository() {
return new UserRepositoryImpl();
}
}
Each @Bean method returns a fully configured bean instance, and Spring internally registers a corresponding BeanDefinition for each method.
Whether beans are defined via XML, annotations, or Java config, Spring always converts them into
BeanDefinitionobjects during the context initialization phase.
How It Works Internally: ConfigurationClassPostProcessor and BeanFactoryPostProcessors
- During the call to
refresh(), Spring processes configuration classes throughConfigurationClassPostProcessor, a specialBeanFactoryPostProcessor. - It reads
@Configurationclasses, scans@Beanmethods, and generates correspondingBeanDefinitionentries. - This process is triggered in the method
invokeBeanFactoryPostProcessors()(same as for annotations).
Summary
| Configuration Style | Description | Common In |
|---|---|---|
| XML-based | Traditional, verbose, but highly explicit | Legacy projects |
| Annotation-based | Uses @Component, @Service, etc. |
Modern Spring apps |
| Java Configuration | Uses @Configuration and @Bean methods |
Spring Boot and test configurations |
Summary of Key Differences Between Spring Framework XML and Spring Boot Annotations
| Feature | Spring Framework (XML) | Spring Boot (Annotations) |
|---|---|---|
| BeanFactory Creation | During refresh() |
At context initialization |
| BeanDefinition Registration | loadBeanDefinitions() parses XML |
invokeBeanFactoryPostProcessors() parses annotations |
| Default Configuration Style | XML + manual setup | Auto-config + annotations |
| Flexibility | Fully customizable | Convention over configuration |