Spring injects Bean objects, and the parent class can also be used as a configuration class

I. Introduction

     When Spring injects Bean objects, generally we only need to add @Component annotations (@Controller, @Service, @Configuration

etc. are actually Components). Others like @Import, @ComponentSacn, etc. are also commonly used. You can also not write @Configuration, but it can also be injected as a configuration class.

2. Code structure

BeanApp class:

@SpringBootApplication
public class BeanApp {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(BeanApp.class, args);
        BeanSon beanSon = (BeanSon)run.getBean("beanSon");
        beanSon.a();

        BeanD beanD = (BeanD)run.getBean("beanD");
        beanD.a();
        BeanFather beanFather = (BeanFather)run.getBean("beanFather" );
        beanFather.a();
}

 BeanFather class


public class BeanFather {
    public void a(){
        System.out.println("bean father method a invoked!");
    }
//
    @Bean
    public BeanD beanD(){
        return  new BeanD();
    }
}

BeanSon class

@Component
public class BeanSon extends  BeanFather{
    public void a(){
        System.out.println("bean son method a invoked!");
    }
}

BeanD class in beanImport2 package

@Component
public class BeanD {
    public void a(){
        System.out.println(" beanD method a !!");
    }
}

operation result:

 

 We see that BeanFather does not have any @Configuration annotations, but BeanD can also be injected.

reason:

1. BeanSon is the Bean object loaded through the BeanApp startup scanning path, and BeanFather is its parent class.
2. Even if BeanFather does not have any annotations, it can be used as a @Configuration annotation. If there is a
      @Bean annotation in the BeanFather class, it can also be injected into the object. The reason is that when Spring injects, it scans its parent class. The specific code is in the ConfigurationClassParser
      class, if (sourceClass.getMetadata().hasSuperClass()) { of the doProcessConfigurationClass method {Process
3, but the BeanFather class itself will not be injected into the Spring container as a Bean object

ConfigurationClassParser 类

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// 处理父类
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

3. Use of @Import and @ComponentScan

Modify BeanFather

@Import(BeanC.class)
@ComponentScan(basePackageClasses = {BeanD.class})
public class BeanFather {
    public void a(){
        System.out.println("bean father method a invoked!");
    }
//
//    @Bean
//    public BeanD beanD(){
//        return  new BeanD();
//    }
}

BeanC class

public class BeanC implements ImportBeanDefinitionRegistrar {
    public void a(){
        System.out.println(" beanC  method a !!");
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanC.class);
        beanDefinition.setSource(this);
        registry.registerBeanDefinition("beanC",beanDefinition);
    }
}

BeanE class under beanImport2 package

@Component
public class BeanE {
    public void a(){
        System.out.println(" beanE method a !!");
    }

}

Modify the startup class BeanApp again

@SpringBootApplication
public class BeanApp {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(BeanApp.class, args);
        BeanSon beanSon = (BeanSon)run.getBean("beanSon");
        beanSon.a();

        BeanD beanD = (BeanD)run.getBean("beanD");
        beanD.a();

        BeanC beanC = (BeanC)run.getBean("beanC");
        beanC.a();

        BeanE beanE = (BeanE)run.getBean("beanE" );
        beanE.a();
        
        BeanFather beanFather = (BeanFather)run.getBean("beanFather" );
        beanFather.a();
    }
}

operation result:

 You can see that through the BeanC class of @Import, and after scanning the package path of BeanD through @ComponentScan, BeanC, BeanD, and BeanE are all injected.

in conclusion:

1. BeanSon is the Bean object loaded through the BeanApp startup scanning path, and BeanFather is its parent class.
2. Even if BeanFather does not have any annotations, it can be used as a @Configuration annotation. If there is a
      @Bean annotation in the BeanFather class, it can also be injected into the object. The reason is that when Spring injects, it scans its parent class. The specific code is in the ConfigurationClassParser
      class, if (sourceClass.getMetadata().hasSuperClass()) { of the doProcessConfigurationClass method.
3. But the BeanFather class itself will not be injected into the Spring container as a Bean object.
4. When using the @Import annotation, if you want to If the imported class is regarded as a Bean object, the class needs to implement the ImportBeanDefinitionRegistrar interface, and then realize the bean injection by
     implementing the registerBeanDefinitions method.

Guess you like

Origin blog.csdn.net/yytree123/article/details/126421425