BeanDefinitionLoader

作者: 王勇1024 | 来源:发表于2019-02-24 20:36 被阅读1次

    BeanDefinitionLoader用于从源加载Bean的定义信息,并封装成BeanDefinition对象,并注册到ApplicationContext中,加载的源可以是类注解、XML文件、package、classpath、Groovy文件等。
    下面我们看一下BeanDefinitionLoader的构造方法:

        BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
            Assert.notNull(registry, "Registry must not be null");
            Assert.notEmpty(sources, "Sources must not be empty");
            this.sources = sources;
            // 读取注解
            this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
            // 读取XML配置
            this.xmlReader = new XmlBeanDefinitionReader(registry);
            if (isGroovyPresent()) {
                this.groovyReader = new GroovyBeanDefinitionReader(registry);
            }
            // 读取classpath
            this.scanner = new ClassPathBeanDefinitionScanner(registry);
            this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
        }
    

    从构造方法中我们可以看到,在BeanDefinitionLoader分别代理了AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、GroovyBeanDefinitionReader、ClassPathBeanDefinitionScanner分别用于从类注解、XML文件、Groovy文件和classpath中加载类的定义信息。
    另外,在构造BeanDefinitionLoader时要指定一个BeanDefinition注册中心,这个注册中心一般就是当前的ApplicationContext,还需要执行要加载的源。

    加载过程

    针对不同的源,进行了不同的实现:

        private int load(Object source) {
            Assert.notNull(source, "Source must not be null");
            if (source instanceof Class<?>) {
                return load((Class<?>) source);
            }
            if (source instanceof Resource) {
                return load((Resource) source);
            }
            if (source instanceof Package) {
                return load((Package) source);
            }
            if (source instanceof CharSequence) {
                return load((CharSequence) source);
            }
            throw new IllegalArgumentException("Invalid source type " + source.getClass());
        }
    

    从类注解加载

    在加载类时,BeanDefinitionLoader首先会判断该类是否包含@Component注解,如果包含则执行注册逻辑,否则不注册。

        private int load(Class<?> source) {
            // 忽略Groovy文件加载逻辑
            if (isComponent(source)) {
                this.annotatedReader.register(source);
                return 1;
            }
            return 0;
        }
    

    从XML文件加载

    加载XML文件的过程实际由XmlBeanDefinitionReader完成,首先会将目标XML文件加载到内存中,并用dom方式进行解析,然后将得到的BeanDefinition进行注册,并返回注册成功的数量。

        // 省略非核心代码
        Document doc = doLoadDocument(inputSource, resource);
        int count = registerBeanDefinitions(doc, resource);
        return count;
    

    从包名加载

    ClassPathBeanDefinitionScanner首先从classpath:{packageName}目录加载所有的类文件,确定给定的bean定义是否符合候选条件(默认实现检查该类是否不是接口而不依赖于封闭类),然后将符合候选条件的BeanDefinition进行注册。

        MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
        if (isCandidateComponent(metadataReader)) {
            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
            sbd.setResource(resource);
            sbd.setSource(resource);
            if (isCandidateComponent(sbd)) {
                if (debugEnabled) {
                    logger.debug("Identified candidate component class: " + resource);
                }
                candidates.add(sbd);
            }
            else {
                if (debugEnabled) {
                    logger.debug("Ignored because not a concrete top-level class: " + resource);
                }
            }
        }
    

    从字符串加载

    这里的字符串可以是一个类名,也可以是XML文件路径,也可以是一个包名。
    BeanDefinitionLoader在拿到这个字符串后,会先替换字符串中的占位符。首先将字符串认为是类名,尝试按照从类注解中加载的方式进行加载;
    如果加载失败,则认为该字符串是文件路径,尝试按照从文件加载的方式进行加载;
    否则尝试按照从包名加载的方式进行加载;
    如果以上方式都无法加载,则抛出异常信息。

        private int load(CharSequence source) {
            // 替换字符串中的占位符
            String resolvedSource = this.xmlReader.getEnvironment()
                    .resolvePlaceholders(source.toString());
            // 首先将字符串认为是类名
            try {
                return load(ClassUtils.forName(resolvedSource, null));
            }
            catch (IllegalArgumentException | ClassNotFoundException ex) {
                // swallow exception and continue
            }
            // 既然不是类名,就认为是文件路径
            Resource[] resources = findResources(resolvedSource);
            int loadCount = 0;
            boolean atLeastOneResourceExists = false;
            for (Resource resource : resources) {
                if (isLoadCandidate(resource)) {
                    atLeastOneResourceExists = true;
                    loadCount += load(resource);
                }
            }
            if (atLeastOneResourceExists) {
                return loadCount;
            }
            // 既不是类名,也不是文件路径,则认为是包名
            Package packageResource = findPackage(resolvedSource);
            if (packageResource != null) {
                // 加载包下的Bean定义信息
                return load(packageResource);
            }
            throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
        }
    

    相关文章

      网友评论

        本文标题:BeanDefinitionLoader

        本文链接:https://www.haomeiwen.com/subject/utebyqtx.html