spring高质量系列-IOC (二)

bean放入到ioc容器 需要先解析bean的xml或者相关注解获取到beanDefinition然后根据beanDefinition生产对应的bean实例并放入容器中。

容器中都是存放的都是单例bean

spring高质量系列-IOC (二)_第1张图片
image.png

BeanDefinition相关接口和类的介绍

    1. BeanDefinition继承了AttributeAccessor(存放和取一些key-value)和BeanMetadataElement(可以配置bean的元数据资源,即获取beanDefinition的数据源,比如xml的标签和注解)
  • 2.一级实现类或者子接口:AbstractBeanDefinition(完成了大部分的方法)和AnnotatedBeanDefinition(子接口,增加了获取注解元数据和方法的元数据)
  • 3.加载beanDefinition:BeanDefinitionReader接口读取资源loadBeanDefinition,AbstractBeanDefinitionReader(实现了其大部分方法),XmlBeanDefinitionReader(对xml进行解析获取bean)。AnnotatedBeanDefinitionReader是一个单独的类和BeanDefinitionReader没有关系,但是其提供的方法和其相似
  • 注册:AliasRegistry(将beanName和别名进行映射),BeanDefinitionRegistry(提供了对beanName和BeanDefinition的赠删改查的操作)

因为IOC容器想要最终获取beanName和BeanDefinition需要三步骤

  • 1.BeanDefinition的Resource定位
  • 2.Resource的载入
  • 3.注册进入IOC容器

笔者对注解和xml 两个解析细节统一进行详细阐述.

此处我们讲的beanDefinition是我们开发人员通过xml配置 或者@Component等注解注入的beanDefinition,这里不包含spring在启动时自动注入的一系列beanDefinition

笔者终于找到了spring.xml和我们自定义注解bean是什么时候处理

BeanDefinition的Resource定位

    1. AbstractApplicationContext--->refresh()---> invokeBeanFactoryPostProcessors(beanFactory)-->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors-->
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)-->ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry-->processConfigBeanDefinitions-->ConfigurationClassParser parser () --->ConfigurationClassParser processConfigurationClass--->ConfigurationClassParser doProcessConfigurationClass
    1. 在ConfigurationClassParser类的 方法 processConfigurationClass,将我们的@Configuration类包装成source
        SourceClass sourceClass = asSourceClass(configClass);
  do {
            sourceClass = doProcessConfigurationClass(configClass, sourceClass);
        }
        while (sourceClass != null);

SourceClass asSourceClass(@Nullable Class classType) throws IOException {
        if (classType == null) {
            return new SourceClass(Object.class);
        }
        try {
            // Sanity test that we can reflectively read annotations,
            // including Class attributes; if not -> fall back to ASM
            for (Annotation ann : classType.getAnnotations()) {
                AnnotationUtils.validateAnnotation(ann);
            }
            return new SourceClass(classType);
        }
        catch (Throwable ex) {
            // Enforce ASM via class name resolution
            return asSourceClass(classType.getName());
        }
    }

上述代码就是获取@Configuration注解类的class同时校验该class上面的注解是否配置正确即注解的属性是否合理。并把他包装成source然后交给doProcessConfigurationClass进行处理

Resource和beanDefinition的载入

概述
  • 1 ConfigurationClassParser doProcessConfigurationClass(),即对获取到的@Configuration 注解进行处理,而我们的@ImportResource注解则是放在启动类上,我们导入spring.xml是依靠@ImportResource,即此时在把相关soucre载入
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {

        首先递归处理任何成员(嵌套)类,即从sourceClass获取内部类看是否属于@configuration,属于就递归调用doProcessConfigurationClass处理子类
        processMemberClasses(configClass, sourceClass);

        处理@PropertySource 注解,将properties文件属性注入到具体bean中,但是前提是environment必须是ConfigurableEnvironment 否则忽略
        for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), PropertySources.class,
                org.springframework.context.annotation.PropertySource.class)) {
            if (this.environment instanceof ConfigurableEnvironment) {
                processPropertySource(propertySource);
            }
            else {
                logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                        "]. Reason: Environment must implement ConfigurableEnvironment");
            }
        }

        处理@ComponentScan annotations
        Set 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 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) {
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(
                            holder.getBeanDefinition(), this.metadataReaderFactory)) {
                        parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }

         处理 any @Import annotations
        processImports(configClass, sourceClass, getImports(sourceClass), true);

        处理 any @ImportResource annotations
        AnnotationAttributes importResource =
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        if (importResource != null) {
            String[] resources = importResource.getStringArray("locations");
            Class readerClass = importResource.getClass("reader");
            for (String resource : resources) {
                String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                configClass.addImportedResource(resolvedResource, readerClass);
            }
        }

          处理 individual @Bean methods
        Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
        for (MethodMetadata methodMetadata : beanMethods) {
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

        // 处理实现接口的方法
        processInterfaces(configClass, sourceClass);

        // 处理 父类, if any
        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;
    }

其中@PropertySources和@PropertySource 以及@ComponentScans和@ComponentScan 分别是集合和单个元素的关系

处理类中类
    1. 首先递归处理任何成员(嵌套)类,即从sourceClass获取内部类看是否属于@configuration,属于就递归调用doProcessConfigurationClass处理子类
处理@PropertySource 注解
  • 1.将properties文件属性注入到具体bean中,但是前提是environment必须是ConfigurableEnvironment 否则忽略
  • 2.最终把PropertySource放入propertySourceList
  • 3.更多的细节请看第三节
处理@ComponentScan
  • 1.首先从@ComponentScans和@ComponentScan获取,而我们的启动类上有注解@SpringBootApplication,其内部定义了默认的scanBasePackages()和scanBasePackageClasses(),我们可以从中获取到@ComponetScan
  • 2.this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());的主要逻辑是设置ClassPathBeanDefinitionScanner的属性:registry,resourceLoader,beanNameGenerator,scopedProxyMode,resolverClass,resourcePattern,includeFilters(必须是@Component和@ManagedBean注解才可以),excludeFilters(componentScan注解所在的类不需要扫描),lazyInit,basePackages。
    1. 其中获取basePackages的逻辑是:从basePackages和basePackageClasses获取他们的包名字放入到集合中,如果集合还是为空就把我们注解类的包名放入进去。
    1. scanner.doScan(StringUtils.toStringArray(basePackages)); 这是最终开始扫描的我们包的操作
    1. ClassPathScanningCandidateComponentProvider类的Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath)是获取到了我们注入的bean资源
  • 6.获取到资源后(也就是basePackages下面的class文件),通过文件流加载class然后获取class和注解的相关属性。然后获取注解是@Component和@ManagedBean的类
    1. 将上述获取的候选类包装成ScannedGenericBeanDefinition,继续判断该类是否不依赖其他bean且该类要么是具体的实现类 要么是抽象类的同时其方法上面还有@Lookup的注解
  • 8.通过scopeMetadataResolver获取ScopeMetadata 即判断是否是单例 同时是否需要包装成proxy默认是不需要。
  • 9 .依次判断候选BeanDefinition是否是AbstractBeanDefinition和AnnotatedBeanDefinition 然后分别调用
    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
    其中前者将beanDefinitionDefaults设置到候选BeanDefinition,后者是将注解上面的一些属性设置到候选BeanDefinition
  • 10.将beanName和候选BeanDefinition包装成definitionHolder,同时看是否需要代理然后将该候选bean注册到beanFactory,但是目前看@bean 和@component 都没有提供ScopeMetadata 的属性配置
  • 11.ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass) 就是创建bean的proxy的地方。
  • 12.创建动态代理的方式是把原先的BeanDefinition和targetBeanName(在原来的名字上加个前缀)注册到beanFactory中,且该BeanDefinition的AutowireCandidate和Primary属性被置为false
  • 13.创建一个proxyDefinition 其beanClass是ScopedProxyFactoryBean,然后将原先的BeanDefinition和proxyDefinition 进行绑定 最终返回一个definitionHolder是proxyDefinition 和originalBeanName组成的
  • 14 若返回的bean是属于@Component ,@ComponentScan ,@Import,@ImportResource.继续调用ConfigurationClassParser的parse方法进行重新处理
  • 15我们通过上述可知@ComponentScan 有ScopedProxyMode 可以指定类是否启用代理。
处理 @Import 注解
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection importCandidates, boolean checkForCircularImports) {

        if (importCandidates.isEmpty()) {
            return;
        }

        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
        else {
            this.importStack.push(configClass);
            try {
                for (SourceClass candidate : importCandidates) {
                    if (candidate.isAssignable(ImportSelector.class)) {
                        // Candidate class is an ImportSelector -> delegate to it to determine imports
                        Class candidateClass = candidate.loadClass();
                        ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                selector, this.environment, this.resourceLoader, this.registry);
                        if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                            this.deferredImportSelectors.add(
                                    new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                        }
                        else {
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            Collection importSourceClasses = asSourceClasses(importClassNames);
                            processImports(configClass, currentSourceClass, importSourceClasses, false);
                        }
                    }
                    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        // Candidate class is an ImportBeanDefinitionRegistrar ->
                        // delegate to it to register additional bean definitions
                        Class candidateClass = candidate.loadClass();
                        ImportBeanDefinitionRegistrar registrar =
                                BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                registrar, this.environment, this.resourceLoader, this.registry);
                        configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                    }
                    else {
                        // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                        // process it as an @Configuration class
                        this.importStack.registerImport(
                                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                        processConfigurationClass(candidate.asConfigClass(configClass));
                    }
                }
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration class [" +
                        configClass.getMetadata().getClassName() + "]", ex);
            }
            finally {
                this.importStack.pop();
            }
        }
    }
  • 0.也会坚持是否有循环导入,寻找的@Import,ImportSelector接口,ImportBeanDefinitionRegistrar接口 都是作为@Import一样处理
  • 1.可以导入@Configuration,ImportSelector接口,ImportBeanDefinitionRegistrar接口
    或者常规的 regular component classes 。
  • 2.@ImportSelector可以返回一些列的需要导入的类
  • 3.@ImportBeanDefinitionRegistrar 可以直接向beanFactory注册beanDefinition
  • 4.经过测试@Configuration类都会被Spring用Cglib增强,但是具体在哪增加笔者尚未知晓@Configuration类下面的@Bean 不会被增强,且Component类下面的@Bean注解不会注入SpringIOC容器
    1. 下面是@Import ImportSelector 类 importingClassMetadata是我们@Import注解所在的类,最终可以导入我们想导入的类
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        System.out.println(importingClassMetadata.toString());
        String[] importString = new String[]{"testImport.ImportBean"};
        return importString;
    }
}
    1. 下面是ImportBeanDefinitionRegistrar 可以在bean实例化前bean进行增删改查
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition =new RootBeanDefinition();
        ((RootBeanDefinition) beanDefinition).setBeanClass(ImportBean.class);
        registry.registerBeanDefinition("xujieBean",beanDefinition);
    }
}
处理@ImportResource
    1. 主要作用就是把xml的地址和readerClass放入importedResources集合,留作后期解析使用
处理@Bean
  • 1.获取当前正在处理的class的方法上有bean的,获取到MethodMetadata,然后将MethodMetadata和当前class 包装成BeanMethod 加入到当前class的beanMethods
processInterfaces(configClass, sourceClass)
  • 看样子是处理当前configClass实现的接口中的子类且加@Bean的方法(该注解放在父类或者子类方法都可以),但是这个方法至今笔者没太看懂他的意义
Process superclass,

-1 .即处理当前configclass 是否还有父类且非接口,如果有继续按照之前的流程进行处理。

此篇文章还留有疑问,后期笔者会在研读过程中进行解说
1.缺少spring.xml在哪一步加载
2.@configuration在哪一步开始进行了动态代理

  1. 何时处理beanMethods集合中的@Bean方法
    4 .如何让propertySource生效

总结:doProcessConfigurationClass处理了类中类,@PropertySource,@ComponentScan,@Import,@ImportResource,@Bean,父类和子类,
其中只有@ComponentScan这一块遇到@Component注解才会注册bean,其他都存入其内部集合中留待真正的注册beanDefinition,详情请关注下篇文章。

你可能感兴趣的:(spring高质量系列-IOC (二))