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