1. Simple example:

@Configuration
2 @EnableConfigurationProperties({DemoProperties.class})
3 public class DemoConfiguration {
4 
5     @Bean
6     public Book getBook(a){
7        return new Book();
8     }
9 }
Copy the code

 

Configuration

 
Copy the code
@Autowired Book book;
2 
3     @Test
4     public void testBook(a){
5         System.out.println(book.toString());
6     }
Copy the code

Unit testing

As a result, the Book object is printed, proving that book has been injected into the Spring container.

How are 2.@Configuration configured beans injected into the Spring container?

First, a brief introduction to bean injection via BeanDefinitionRegistry

 
Copy the code
@Autowired
2     public void registBeanDefinition(BeanFactory factory){
3         if(factory instanceof BeanDefinitionRegistry) {
4             BeanDefinitionRegistry registry = (BeanDefinitionRegistry)factory;
5             BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Book.class).getBeanDefinition();
6             registry.registerBeanDefinition("mybook", beanDefinition);
7         }
8     }
Copy the code

View Code

 @Autowired @Qualifier("mybook") Book book2;
2     @Test
3     public void testBook2(a){
4         System.out.println(book2);
5     }
Copy the code
 
Copy the code

Unit testing

The result also prints the book object, here, The author to convert the beanFactory strong BenDefinitionRegistry, because the author used in the Demo is the beanFactory, default — DefaultListableBeanFactory, he realized the BeanDefinitionRegi Stry interface.

3. Entry. The following figure shows a simplification of the ApplicaitonContext Refresh method, with only partial functions reserved for BeandefinitionRegistry registered beans.

Well, it doesn’t seem to work, right? Here is called BeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry method. To solve a problem is, how BeanDefinitionRegistryPostProcessor type of bean injection. Take SpringBoot initialization as an example.

Notice that the ApplicationContext constructor:

public AnnotationConfigEmbeddedWebApplicationContext(a) {
2         this.reader = new AnnotatedBeanDefinitionReader(this);
3         this.scanner = new ClassPathBeanDefinitionScanner(this);
4     }
Copy the code
 
Copy the code

ApplicationContext structure

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
2         this(registry, getOrCreateEnvironment(registry));
3     }
Copy the code
 
Copy the code

View Code

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
2         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
3         Assert.notNull(environment, "Environment must not be null");
4         this.registry = registry;
5         this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
6         AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
7     }
Copy the code
 
Copy the code

View Code

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
 2             BeanDefinitionRegistry registry, Object source) {
 3 
 4         DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
 5         if(beanFactory ! =null) {
 6             if(! (beanFactory.getDependencyComparator()instanceof AnnotationAwareOrderComparator)) {
 7                 beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
 8             }
 9             if(! (beanFactory.getAutowireCandidateResolver()instanceof ContextAnnotationAutowireCandidateResolver)) {
10                 beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
11             }
12         }
13 
14         Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
15                 // Annotation @configuration processing
16         if(! registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {17             RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
18             def.setSource(source);
19             beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
20         }
21 
22.// Omit some code
23 
24         return beanDefs;
25     }
Copy the code
 
Copy the code

View Code

Notice to @ the processing of a Configuration for ConfigurationClassPostProcessor.

Notice ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor interface, apparently for postProcessBeanDefinitionRegistry key method. ConfigurationClassPostProcessor postProcessBeanDefinitionRegistry will call ConfirgurationClassParser parse method. Annotations are parsed in turn, and we look at the parsing of each annotation step by step.

(1) @propertysources and @propertysource

@Target(ElementType.TYPE)
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 public @interface PropertySources {
5 
6     PropertySource[] value();
7 
8 }
Copy the code
 
Copy the code

PropertySources definition

The @propertysource annotations only contain multiple @propertysource annotations. The main function of the @Propertysource annotations is to introduce a configuration file and merge the configured property key-value pairs with the configuration in the environment variables. One of the most critical classes is MutablePropertySources

public class MutablePropertySources implements PropertySources {
2 
3     private final Log logger;
4 
5     private finalList<PropertySource<? >> propertySourceList =newCopyOnWriteArrayList<PropertySource<? > > ();6.7 }
Copy the code
 
Copy the code

View Code

Obviously MutablePropertySources contains a list of PropertySource. MutablePropertySources simply encapsulates the iterator functionality. PropertySources is a collection of PropertySource, adding common collection operations.

(2) @ ComponentScan

Define packages that are automatically scanned. The simplified sequence diagram is as follows:

The most critical method is the doScan method, which registers the BeanDefinition into the container.

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
 2         Assert.notEmpty(basePackages, "At least one base package must be specified");
 3         Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
 4         for (String basePackage : basePackages) {
 5             Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
 6             for (BeanDefinition candidate : candidates) {
 7                 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
 8                 candidate.setScope(scopeMetadata.getScopeName());
 9                 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
10                 if (candidate instanceof AbstractBeanDefinition) {
11                     postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
12                 }
13                 if (candidate instanceof AnnotatedBeanDefinition) {
14                     AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
15                 }
16                 if (checkCandidate(beanName, candidate)) {
17                     BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
18                     definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
19                     beanDefinitions.add(definitionHolder);
20                     registerBeanDefinition(definitionHolder, this.registry);
21                 }
22             }
23         }
24         return beanDefinitions;
25     }
Copy the code

 

 
Copy the code

DoScan method

registerBeanDefinition(definitionHolder, this.registry); It says it all.

(3) @ Import

The @import annotation can configure the class that needs to be imported (assuming it is configured as A, which can be an array) in three ways. Its flow chart is as follows:

If A is A subclass of ImportSelector, call selectImports(), return an array of class names, loop through each imported class, If A is BeanDefinitionRegistrar then call registerBeanDefinition to inject the bean directly into the container. If A is A common class (other than the two types mentioned above), treat A as the @Configuration class and parse Configuration again.

(4) @ ImportSource

The main function is to import resource files.

(5) @bean, simpler, like child FactoryMethod

// Process individual @Bean methods
 2         Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
 3         for (MethodMetadata methodMetadata : beanMethods) {
 4             configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
 5         }
 6 
 7         // Process default methods on interfaces
 8         for (SourceClass ifc : sourceClass.getInterfaces()) {
 9             beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName());
10             for (MethodMetadata methodMetadata : beanMethods) {
11                 if(! methodMetadata.isAbstract()) {12                     // A default method or other concrete method on a Java 8+ interface...
13                     configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
14                 }
15             }
16         }
Copy the code
 
Copy the code

@ Bean parsing

The last real loading beanDefinition is loadBeanDefinitionsForConfigurationClass method:

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
 2             TrackedConditionEvaluator trackedConditionEvaluator) {
 3 
 4         if (trackedConditionEvaluator.shouldSkip(configClass)) {
 5             String beanName = configClass.getBeanName();
 6             if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
 7                 this.registry.removeBeanDefinition(beanName);
 8             }
 9             this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName());
10             return;
11         }
12 
13         if (configClass.isImported()) {
14             registerBeanDefinitionForImportedConfigurationClass(configClass);
15         }
16         for (BeanMethod beanMethod : configClass.getBeanMethods()) {
17             loadBeanDefinitionsForBeanMethod(beanMethod);
18         }
19         loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
20         loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
21     }
Copy the code
 
Copy the code