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.