zoukankan      html  css  js  c++  java
  • 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 容器中了。

    2.@Configuration配置的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     }
    View Code
    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     }
    View Code
    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     }
    View Code
     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     }
    View Code

    注意到对@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 }
    View Code

    显然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     }
    View Code
  • 相关阅读:
    [Redis]主从同步可能遇到的坑
    Redis_如何保证原子操作
    .Net Core 5.0 Json序列化和反序列化 | System.Text.Json 的json序列化和反序列化
    JavaScript Error对象整理_JavaScript 异常处理整理
    Canvas 事件绑定|Canvas事件处理
    Css3 常用布局方式 一行两列&高度自适应&垂直方向居中
    Css3 实现锯齿效果整理
    Css3 currentColor 变量使用
    Css3 实现任意角扇形|Css3实现六角扇形
    实现 Application_Start 和 Application_End
  • 原文地址:https://www.cnblogs.com/dragonfei/p/5925114.html
Copyright © 2011-2022 走看看