preface

Spring 5.0.x AnnotationConfigApplicationContext class a no-parameter constructor function introduced AnnotationConfigApplicationContext class, no arguments constructor again under review has the following main functions:

  1. Initialize the Spring Bean factoryDefaultListableBeanFactory
  2. throughAnnotatedBeanDefinitionReaderSpring6 built-in beans toRootBeanDefinition to beanOf the types registered to the factory, remember the most important onesConfigurationClassPostProcessor
  3. Initialize theClassPathBeanDefinitionScan(This is useless, the real scan logic doesn’t use it.)
  4. Several AnnotationConfigApplicationContext identity:BeanDefinitionRegistryandGenericApplicationContext

The next to enter text: AnnotationConfigApplicationContext class register method

I. Project Demo

A, AnnotationConfigApplicationContext class register method API

  • The source code from top to bottom is how it is executed, with comments
    // AnnotationConfigApplicationContext.java
    / * * *, as the name implies, the incoming is annotated classes, and do them with the existence of annotations validation (can upload multiple classes) * according to the code, it is registered by AnnotatedBeanDefinitionReader * /
    public void register(Class
       ... annotatedClasses) {
        Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
        this.reader.register(annotatedClasses);
    }
Copy the code
    // AnnotationConfigApplicationContext.java
    /** * Because the API above provides a mutable argument, we register it here */
    public void register(Class
       ... annotatedClasses) {
        for (Class<?> annotatedClass : annotatedClasses) {
            registerBean(annotatedClass);
        }
    }
Copy the code
    // AnnotationConfigApplicationContext.java
    /** * delegate code logic to another method */
    public void registerBean(Class
        annotatedClass) {
        doRegisterBean(annotatedClass, null.null.null);
    }
Copy the code
    // AnnotationConfigApplicationContext.java
	/** * when this method is called, only the first argument has a value, all others are null *@param annotatedClass
	 * @param instanceSupplier
	 * @param name
	 * @param qualifiers
	 * @param definitionCustomizers
	 * @param <T>
	 */
    <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
            @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {

    	// Remember the API for creating beanDefinition, which is very common.
		// Useful when using Spring extension points to dynamically add some BeanDefinitions to bean factories
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }

        // set to null, because null is passed in
        abd.setInstanceSupplier(instanceSupplier);
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());

        / / this is the current class default beanNameGenerator = > AnnotationBeanNameGenerator
		// private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
		// Our custom beanNameGenerator will not be used, why?
		// Because we haven't resolved @ComponentScan annotation yet.
		// Refer to this class on Github:
		// https://github.com/AvengerEug/spring/blob/develop/ioc/src/main/java/com/eugene/sumarry/ioc/annotationtype/MyBeanNameGene rator.javaString beanName = (name ! =null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

        // Start with some generic annotations: @lazy, @primary, @dependson, @role, @description
		The values in the / / to get these annotations, and fill to the incoming AnnotatedGenericBeanDefinition
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

        // This is almost useless if you follow the spring process, because null is passed in
        if(qualifiers ! =null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(newAutowireCandidateQualifier(qualifier)); }}}// The value passed in is also null, which is ignored as normal
        for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
            customizer.customize(abd);
        }

        // Create a new object.
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

        // I don't know...
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

        // Register beanDefinition, passing in Registry, based on the last blog post,
		/ / easy to know the registry is AnnotationConfigApplicationContext
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }
Copy the code

	// BeanDefinitionReaderUtils.java
    /** * Register beanDefinition */
    public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        
        // It has a definitionHolder, now unpack it......
        / / the register is a AnnotationConfigApplicationContext
        / / but at the moment is to call the superclass GenericApplicationContext registerBeanDefinition method
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if(aliases ! =null) {
            for(String alias : aliases) { registry.registerAlias(beanName, alias); }}}Copy the code

	// DefaultListableBeanFactory.java
	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");

        // This will check the current beanDefinition.
        // But beanDefinition instanceof AbstractBeanDefinition is valid
        / / because it inherited AbstractBeanDefinition (AnnotatedGenericBeanDefinition)
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex); }}// beanDefinitionMap: This attribute is the beanDefinition where the bean factory holds the definition
        // The beanDefinition is registered, so verify that it exists, as normal
        // This is basically null
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if(existingDefinition ! =null) {
            if(! isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + existingDefinition + "] bound.");
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (logger.isWarnEnabled()) {
                    logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]"); }}else if(! beanDefinition.equals(existingDefinition)) {if (logger.isInfoEnabled()) {
                    logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]"); }}else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]"); }}this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            // Check whether the bean is being created. In a normal register process, the return value is basically false
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    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 {
                // Register beanD with bean factory,
                // 1. Add beanDefinitionMap
                // 2. Add beanDefinitionNames
                // 3. Remove from manualSingletonNames, manualSingletonNames is not clear
                // What does the property do, but guess by name: the name of the manually created singleton bean?
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if(existingDefinition ! =null|| containsSingleton(beanName)) { resetBeanDefinition(beanName); }}Copy the code

Second, operation results

Third, summary

  • Q1: After the above code is executed (the register method is executed), how many BeanDefinitions are registered in the bean factory. How many BeanDefinitions are there?
  • A1: Two Beandefinitions are registered, so there are 6 + 2 = 8 Beandefinitions in the bean factory.

  • Q2: How many types of BeanDefinitions exist in the bean factory today? Is respectively?
  • A2: two. Is RootBeanDefinition and AnnotatedGenericBeanDefinition respectively.

  • Q3: Are there any beans being created now?
  • A3: No, the register method just registers the beanDefinition. You typically register a configuration class (eg: a class with @ComponentScan annotations) to facilitate subsequent spring operations (eg: scanning package parsing annotations, etc.).

  • Q4: What common annotations does Spring consider to describe beans?
  • A4: @lazy, @primary, @dependson, @role, @description

  • Spring source code to learn the corresponding GitHub address github.com/AvengerEug/…
  • I am a slow walker, but I never walk backwards.