Spring之@Configuration配置解析

1.简单的示例:

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

Configuration

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

单元测试

结果打印出book对象,证明Book已经被注入到Spring 容器中了。

[email protected]配置的bean是如何注入到Spring容器中的呢?

首先先来简单介绍下通过BeanDefinitionRegistry注入bean

1     @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     }

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

单元测试

结果同样打印出book对象,这里,笔者将beanFactory强转为BenDefinitionRegistry,因为笔者的Demo中使用的是默认BeanFactory,----DefaultListableBeanFactory,他实现了BeanDefinitionRegistry接口。

3.入口,下图为ApplicaitonContext refresh方法的简化,只保留了BeandefinitionRegistry注册bean部分功能。

然,似乎并没什么用?此处会调用BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法。要解决的一个问题是,BeanDefinitionRegistryPostProcessor类型的bean是如何注入的。以SpringBoot初始化为例。

注意到,ApplicationContext的构造方法:

1 public AnnotationConfigEmbeddedWebApplicationContext() {
2         this.reader = new AnnotatedBeanDefinitionReader(this);
3         this.scanner = new ClassPathBeanDefinitionScanner(this);
4     }

ApplicationContext构造

1 public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
2         this(registry, getOrCreateEnvironment(registry));
3     }

1     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     }

 1     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                 //注解@Configuration处理
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         .......//省略部分代码
23
24         return beanDefs;
25     }

注意到对@Configuration的处理为ConfigurationClassPostProcessor。

注意到ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,显然关键方法为postProcessBeanDefinitionRegistry。ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry会调用ConfirgurationClassParser的parse方法。会依次解析注解,我们一步一步查看对各个注解的解析。

(1)@PropertySources和@PropertySource  

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

PropertySources定义

最终都是处理@PropertySource,@PropertySources仅仅只是包含多个@PropertySource,@PropertySource注解的主要功能是引入配置文件,将配置的属性键值对与环境变量中的配置合并。其中最关键的类为MutablePropertySources

1 public class MutablePropertySources implements PropertySources {
2
3     private final Log logger;
4
5     private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();
6 ......
7 }

显然MutablePropertySources中包含有一个PropertySource列表。MutablePropertySources仅仅是封装了迭代器功能。可以理解成PropertySources是PropertySource的集合,增加了常用的集合操作。

(2)@ComponentScan

定义自动扫描的包。简化的序列图如下:

其最关键的方法为doScan方法,会注册BeanDefinition到容器中。

 1     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     }

doScan方法

registerBeanDefinition(definitionHolder, this.registry);能说明一切。

(3)@Import

@Import注解可以配置需要引入的class(假设配置为A,可以是数组),有三种方式。其流程图如下:

如果A为ImportSelector的子类,调用selectImports()方法,返回class类名数组,循环解析每一个import的类,如果A为BeanDefinitionRegistrar则直接调用registerBeanDefinition直接注入bean到容器中。如果A为普通的类(非前面提到的两种类型),则将A当做@Configuration配置的类,重新解析Configuration.

(4)@ImportSource

主要功能为引入资源文件。

(5)@Bean,比较简单,童FactoryMethod一样

 1         // 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         }

@Bean解析

最后真实加载beanDefinition是loadBeanDefinitionsForConfigurationClass方法:

 1     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     }

时间: 2024-10-05 05:06:10

Spring之@Configuration配置解析的相关文章

spring配置文件头部配置解析(applicationContext.xml)

分享一个好的学习网站:http://how2j.cn?p=4509 相信大家对spring的配置文件应该都看的很多了,那么大家对配置文件头部的那一坨坨的东西到底是什么了解吗?下面我就把自己的一些见解和大家分享一下: 首先拿一段大家熟悉的头部配置文件来看: [html] view plain copy <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.spring

Spring经常使用配置解析

一,配置文件的延迟载入 Spring默认在创建BeanFactory时,将配置文件里全部的对象实例化并进行注入. 使用延迟初始化之后.等到用到bean的时候才去初始化. 使用default-lazy-init="true"来设置全部bean都延迟初始化,也能够单独在须要延迟初始化的bean单独配置lazy-init="true". 二,Spring bean的作用域设置 Scope的值: 1,sigleton:为默认值,每次调用getBean向IOC容器中取得的对象

Spring源码解析 – @Configuration配置类及注解Bean的解析

在分析Spring 容器创建过程时,我们知道容器默认会加载一些后置处理器PostPRocessor,以AnnotationConfigApplicationContext为例,在构造函数中初始化reader时,加载默认后置处理器.其中 ConfigurationClassPostProcessor这个后置处理器专门处理带有@Configuration注解的类,ConfigurationClassPostProcessor后置处理实现了BeanDefinitionRegistryPostProce

Spring Boot自动配置源码解析(基于Spring Boot 2.0.2.RELEASE)

在Spring Boot官方介绍中,首一段话是这样的(如下图).我们可以大概了解到其所表达的含义:我们可以利用Spring Boot写很少的配置来创建一个非常方便的基于Spring整合第三方类库的单体企业级应用.相信使用过Spring Boot的人都知道,她在这方面从前到后的一系列整合.本篇文字将带你进入具体的实现细节. 首先我们写一段Spring Boot应用启动类的代码如下: 1 package com.springTest; 2 3 import org.springframework.b

spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理

@Configuration注解提供了全新的bean创建方式.最初spring通过xml配置文件初始化bean并完成依赖注入工作.从spring3.0开始,在spring framework模块中提供了这个注解,搭配@Bean等注解,可以完全不依赖xml配置,在运行时完成bean的创建和初始化工作.例如: public interface IBean { } public class AppBean implements IBean{ } //@Configuration申明了AppConfig

dubbo源码分析7——dubbo的配置解析_与spring的整合

dubbo的配置其实就是建立在spring的命名空间的配置机制之上的.在dubbo的jar包的META-INF目录下会有spring.handlers这个文件,用来配置spring的命名空间和解析类的对应关系.打开spring.handlers文件,可知dubbo的命名空间配置的处理类为com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler,代码: public void init() { registerBeanDefiniti

使用@Configuration注解来代替Spring的bean配置

@Configuration 相当于beans.@bean相当于bean. 下面是一个典型的Spring配置文件(application-config.xml): <beans> <bean id="orderService" class="com.acme.OrderService"/> <constructor-arg ref="orderRepository"/> </bean> <be

Spring Boot启动原理解析

Spring Boot启动原理解析http://www.cnblogs.com/moonandstar08/p/6550758.html 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面纱,让它不在神秘. 正文 我们开发任何一个Spring Boot项目,都会用到如下的启动类 从上面代码可以看出,Annotation定义(@Sp

Spring Boot自动配置原理(转)

第3章 Spring Boot自动配置原理 3.1 SpringBoot的核心组件模块 首先,我们来简单统计一下SpringBoot核心工程的源码java文件数量: 我们cd到spring-boot-autoconfigure工程根目录下.执行 $ tree | grep -c .java$ 模块 java文件数 spring-boot 551 spring-boot-actuator 423 spring-boot-autoconfigure 783 spring-boot-devtools