Deep Dive into Spring Boot: IoC Container, SpringFactoriesLoader, Event Publishing, and Auto‑Configuration
This extensive tutorial explains Spring Boot’s core mechanisms—including the IoC container, class loading, SpringFactoriesLoader, event publishing, and automatic configuration—through detailed explanations and code examples, helping developers understand and extend Spring Boot’s startup process.
The article begins with an introduction to Spring Boot, emphasizing that its most exciting feature is the rapid startup of Spring applications and that understanding the underlying Spring IoC container is essential for mastering Spring Boot.
Spring IoC Container Basics
The IoC container is compared to a restaurant: you request a bean and the container provides a fully‑initialized instance without you needing to know how it was created. Bean definitions are stored in BeanDefinition objects, which hold class type, constructor arguments, and other metadata. The relationship between BeanFactory , BeanDefinitionRegistry , and BeanDefinition is illustrated with a diagram (omitted).
Example code showing how a bean is registered and retrieved:
// Default container implementation
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
// Create BeanDefinition for Business class
AbstractBeanDefinition definition = new RootBeanDefinition(Business.class, true);
// Register the bean definition
beanRegistry.registerBeanDefinition("beanName", definition);
// Retrieve the bean instance
BeanFactory container = (BeanFactory) beanRegistry;
Business business = (Business) container.getBean("beanName");Another example demonstrates loading bean definitions from an XML file using XmlBeanDefinitionReader :
BeanDefinitionRegistry beanRegistry = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReaderImpl(beanRegistry);
beanDefinitionReader.loadBeanDefinitions("classpath:spring-bean.xml");
BeanFactory container = (BeanFactory) beanRegistry;
Business business = (Business) container.getBean("beanName");The container lifecycle is split into two phases: the container‑startup phase (loading configuration metadata) and the bean‑instantiation phase (creating bean instances on demand).
Class Loading and SPI
The article explains Java’s three class loaders (Bootstrap, Extension, Application) and the parent‑delegation model. It also discusses the limitation of the delegation model for Service Provider Interfaces (SPI) and introduces the thread‑context class loader as a solution.
Example of loading a resource using the thread‑context class loader:
String name = "java/sql/Array.class";
Enumeration
urls = Thread.currentThread().getContextClassLoader().getResources(name);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
System.out.println(url);
}SpringFactoriesLoader
SpringFactoriesLoader scans all JARs for META-INF/spring.factories files, parses key‑value pairs, and returns the class names associated with a given key. The core method is:
public static List
loadFactoryNames(Class
factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
Enumeration
urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List
result = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties props = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String names = props.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(names)));
}
return result;
}Typical content of a spring.factories file:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfigurationAuto‑Configuration Mechanism
The annotation @EnableAutoConfiguration imports EnableAutoConfigurationImportSelector , which uses SpringFactoriesLoader to obtain all auto‑configuration classes listed under the key EnableAutoConfiguration . Each auto‑configuration class is a @Configuration class guarded by conditional annotations such as @ConditionalOnClass , @ConditionalOnMissingBean , etc. Example:
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class})
public class DataSourceAutoConfiguration { }These conditions ensure that the configuration is applied only when the required classes are present and when no user‑defined bean of the same type exists.
Spring Boot Startup Flow
The entry point is a class annotated with @SpringBootApplication , which combines @SpringBootConfiguration , @EnableAutoConfiguration , and @ComponentScan . The SpringApplication.run() method performs the following high‑level steps (simplified):
Create SpringApplicationRunListeners via SpringFactoriesLoader and invoke starting() (publishes ApplicationStartedEvent ).
Prepare the Environment (profiles, property sources) and publish ApplicationEnvironmentPreparedEvent .
Print the banner.
Create an appropriate ApplicationContext (web or non‑web).
Instantiate FailureAnalyzer implementations.
Prepare the context: set the environment, apply ApplicationContextInitializer instances, and publish ApplicationContextInitializedEvent .
Refresh the context, which among other things invokes all BeanFactoryPostProcessor s (including those added by the auto‑configuration import selector).
Call any CommandLineRunner or ApplicationRunner beans.
Publish the final ApplicationReadyEvent via finished() on the listeners.
During the refresh phase, ConfigurationClassPostProcessor processes annotations like @ComponentScan , @Import , and ultimately triggers the auto‑configuration logic described earlier.
Extending Spring Boot
Developers can add custom ApplicationListener implementations either programmatically via SpringApplication.addListeners() or declaratively by adding entries to their own META-INF/spring.factories file under the org.springframework.context.ApplicationListener key.
The article concludes with a reminder that mastering the Spring container’s lifecycle makes understanding Spring Boot’s startup process straightforward.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.