Understanding Spring Boot Configuration Class Parsing and Core Annotations
Spring Boot parses @SpringBootApplication by combining @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan, then processes @Import, @Conditional, and bean post‑processors, uses SpringFactoriesLoader for auto‑configuration, and follows an eight‑step configuration‑class parsing flow to register beans during startup.
Spring Boot is a popular Java framework that provides zero‑configuration integration of many third‑party libraries. While it enables rapid development, customizing or extending the framework requires a deep understanding of how Spring Boot parses configuration classes and registers beans.
Basic concepts
The article focuses on Spring Boot 2.1.7.RELEASE and explains how the framework processes configuration classes annotated with @SpringBootApplication. This meta‑annotation is composed of three core annotations:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication { }@SpringBootConfiguration
It is essentially a @Configuration class, which tells Spring that the class defines bean definitions.
@Configuration
public @interface SpringBootConfiguration { }@EnableAutoConfiguration
Through @Import it registers AutoConfigurationImportSelector and AutoConfigurationPackages.Registrar, which import auto‑configuration classes.
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { }@ComponentScan
Works like <context:component-scan> and scans the package of the main configuration class for components annotated with @Component, @Service, @Repository, etc.
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })@Import
The article demonstrates custom import mechanisms, such as a user‑defined @EnableAnimal annotation that imports several classes via @Import:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({Dog.class, AnimalRegisterConfiguration.class, AnimalImportSelector.class, AnimalImportBeanDefinitionRegistar.class})
public @interface EnableAnimal { }Four import styles are covered:
Direct class import ( @Import({Dog.class}))
Configuration class import
ImportSelector implementation
ImportBeanDefinitionRegistrar implementation
@Conditional
Conditional annotations like @ConditionalOnMissingBean control whether a bean is created based on the presence of another bean.
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() { }Custom conditions can be created by implementing Condition and defining a meta‑annotation:
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String[] value = (String[]) metadata.getAnnotationAttributes(MyConditionAnotation.class.getName()).get("value");
for (String property : value) {
if (StringUtils.isEmpty(context.getEnvironment().getProperty(property))) {
return false;
}
}
return true;
}
}
@Conditional(MyCondition.class)
public @interface MyConditionAnotation {
String[] value() default {};
}SpringFactoriesLoader
Spring Boot uses a SPI‑like mechanism to load auto‑configuration classes from META-INF/spring.factories. The loader reads the properties file and returns a list of class names for a given key (e.g., EnableAutoConfiguration).
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}Bean post‑processing
The article shows examples of BeanFactoryPostProcessor and BeanPostProcessor implementations that add or modify bean definitions before and after bean instantiation.
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(Chicken.class);
registry.registerBeanDefinition("beanFactoryPostProcessor-Chicken", beanDefinition);
BeanDefinition snake = registry.getBeanDefinition("snake");
snake.setLazyInit(true);
}
}
@Component
public class CatBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Cat) {
((Cat) bean).setName("changeNameCat");
}
return bean;
}
}Spring Boot startup flow
The article outlines the bootstrapping steps, highlighting that during refreshContext the fifth step invokes postProcessBeanDefinitionRegistry of ConfigurationClassPostProcessor. This processor parses configuration classes, registers bean definitions, and handles imports, property sources, component scans, and conditional logic.
Configuration class parsing process
The parsing consists of eight sub‑steps:
Detect if a class is a configuration candidate.
Process member (nested) classes.
Handle @PropertySource annotations.
Handle @ComponentScan annotations.
Handle @Import annotations (including ImportSelector and ImportBeanDefinitionRegistrar).
Handle @ImportResource (XML) annotations.
Process individual @Bean methods.
Process default methods on interfaces and superclass hierarchy.
Each step is illustrated with code excerpts and explanations of how Spring builds ConfigurationClass objects, resolves imports, and ultimately registers bean definitions into the application context.
Deferred import selectors and auto‑configuration
Auto‑configuration is performed by a DeferredImportSelector ( AutoConfigurationImportSelector) that loads candidate classes from spring.factories, applies exclusions and filters, and finally registers the selected auto‑configuration classes.
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
List<String> configurations = getCandidateConfigurations(annotationMetadata, getAttributes(annotationMetadata));
configurations = filter(configurations, autoConfigurationMetadata);
return new AutoConfigurationEntry(configurations, exclusions);
}After parsing, ConfigurationClassPostProcessor calls loadBeanDefinitions to register beans defined by configuration classes, @Bean methods, imported resources, and registrars.
In summary, the article provides a comprehensive walkthrough of how Spring Boot discovers, parses, and registers configuration classes, covering core annotations, import mechanisms, conditional processing, and the auto‑configuration infrastructure.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
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.
