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

    BeanDefinitionLoader用于从源加载Bean的定义信息,并封装成BeanDefinition对象,...

网友评论

    本文标题:BeanDefinitionLoader

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