Spring annotation-based startup
There are two main Class implementations for annotation startup
- AnnotationConfigApplicationContext
- AnnotationConfigWebApplicationContext
We take Annotation Config Application Context as our research object.

Introducing Spring Minimum Dependency
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency>
Writer startup code
public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(BeanConfig.class); applicationContext.refresh(); Date date = applicationContext.getBean("date",Date.class); System.out.println(date); }
Annotation Config Application Context constructor
public AnnotationConfigApplicationContext() { //Register Class, Reader this.reader = new AnnotatedBeanDefinitionReader(this); //Responsible for scanning Class under the specified class path and registering bean s this.scanner = new ClassPathBeanDefinitionScanner(this); }
Construction Method of Annotated Bean Definition Reader
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; //Initialize ConditionEvaluator this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); /** Register all relevant post processors in a given registry * Determine whether the container already has beans for a given registry, if no beans are registered, and place the beans in the container * List all processing processors * ConfigurationClassPostProcessor Configuration Annotation Processor for Internal Management * AutowiredAnnotationBeanPostProcessor Internal Management @Autowire Processor * RequiredAnnotationBeanPostProcessor @Required Processor * CommonAnnotationBeanPostProcessor JSR-250 Annotation Processor, first determine whether it supports jsr, if it supports registration * PersistenceAnnotationBeanPostProcessor JPA Management first uses the class loader to find out if there is a package, and if there is one, register. * EventListenerMethodProcessor @EventListener Processor * DefaultEventListenerFactory Managing EventListenerFactory Processors */ AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }
What does ConditionEvaluator do? Click in.
public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry, @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) { this.context = new ConditionContextImpl(registry, environment, resourceLoader); } //ConditionContextImpl implements the ConditionContext interface, ConditionEvaluator static internal class public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry, @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) { this.registry = registry; this.beanFactory = deduceBeanFactory(registry); this.environment = (environment != null ? environment : deduceEnvironment(registry)); this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry)); this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory); }
As you can see, ConditionEvaluator initializes the Spring container top-level object using an external parameter passing method
BeanFactory, Environment, Resource Loader, Class Loader. Prepare for the next analysis of the @Conditional annotation by passing these to ConditionContextImpl
ClassPathBean Definition Scanner constructor
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { this(registry, true); } public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { this(registry, useDefaultFilters, getOrCreateEnvironment(registry)); } public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) { this(registry, useDefaultFilters, environment, (registry instanceof ResourceLoader ? (ResourceLoader) registry : null)); } public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; if (useDefaultFilters) { registerDefaultFilters(); } setEnvironment(environment); setResourceLoader(resourceLoader); } protected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
Circle the Earth a few times, in fact, Spring top-level interface Environment, Resource Loader assignment, the use of default annotation filter, the first @Component into the List, to determine whether the current environment supports JSR-250, JSR-330, the corresponding filter. That is, by default, the scanner only scans Class es for @Component or JSR-250, JSR-330 tags.
applicationContext.register(BeanConfig.class)
public void register(Class<?>... annotatedClasses) { Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified"); this.reader.register(annotatedClasses); //Call the reader just initialized } | ============================AnnotatedBeanDefinitionReader Reader code====================================================================================================== public void register(Class<?>... annotatedClasses) { for (Class<?> annotatedClass : annotatedClasses) { registerBean(annotatedClass); } } public void registerBean(Class<?> annotatedClass) { doRegisterBean(annotatedClass, null, null, null); } /** *Resolve the annotations given by Class from a given bean, perform the corresponding initialization, and save them in the Spring container */ <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) { //Obtain metadata Annotation Metadata from Class Annotated AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); /** * Determine whether registered lass contains the @Conditional annotation, and if you get all the value s, put them in the List * After sorting, it traverses all the Conditions implementations, uses reflection to get objects, and executes matches method. * If a return false is found, the interrupt loop returns true directly. */ if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { //If the @Conditional condition is not satisfied, no registration is required. return; } abd.setInstanceSupplier(instanceSupplier); //Resolve whether Class has @Scope, Resolve @Scope annotation returns ScopeMetadata object, not empty directly ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); //To determine whether the Value on the annotation is valued, use this as BeanName if you have it, and take the class name if you don't. String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); //Continue to parse AnnotationMetadata's @Lazy,@Primary,@DependsOn,@Role,@Description annotations and put the results into the object's properties AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); //This class is just a BeanDefinition wrapper class BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); //Whether the proxy class is needed, and if so, modify the internal properties to regenerate the BeanDefinition object definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); //Call the DefaultListableBeanFactory.registerBeanDefinition method, do some security checks, and then put the definition Holder into the register container BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
This method is to register beans, parse annotations on Class, initialize annotation data, do the corresponding processing, convert them into Bean Definition, and store them in Spring container.
Let's see how BeanDefinition can be registered in Spring containers, mainly by DefaultListableBeanFactory.registerBeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { //Check the bean Definition to determine that Method Overrides cannot be empty and must have factory methods ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { //This method is to determine whether renamed beans are allowed and whether they can be overridden by different definitions of beans. if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } else { //Call alreadyCreated.isEmpty(), alreadyCreated Set object, and save the created beanName //Documents show that created, registered here should not be the same behavior, this is to see what the latter will know. if (hasBeanCreationStarted()) { synchronized (this.beanDefinitionMap) {//Update data this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { //Spring bean Definition container, a Map reprint this.beanDefinitionMap.put(beanName, beanDefinition); //Save beanName, which is mainly used to record the order of registration for each bean this.beanDefinitionNames.add(beanName); //Delete the singleton and register it as a regular bean this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { //Update beanName in Spring Container resetBeanDefinition(beanName); } }
Registering bean Definition into Spring containers doesn't have much complex logic, just some security checks.
BeanDefinition
A BeanDefinition describes an instance of a bean, including attribute values, constructor parameter values, and more information about classes inherited from it. BeanDefinition is just the simplest interface. Its main function is to allow BeanFactoryPostProcessor, such as PropertyPlaceHolder Configure, to retrieve and modify attribute values and metadata of other beans.
Spring container bean Definition is mainly divided into RootBean Definition and Annotated GenericBean Definition.
- Specific bean s in RootBean Definition Spring Factory
- Annotated GenericBean Definition User-defined Beans
Summary of Spring Startup Process

These BeanDefinition s are only put into Spirng containers, without any initialization of the object. The real IOC operation is refresh(), which is available for further analysis.