SpringBoot源码解析——BeanDefinitionLoader 源码分析

点击上方 "Java指南者"关注, 星标或置顶一起成长

免费送 1024GB 精品学习资源 

SpringApplication 源码分析 中分析 run 方法的时候,可以看到调用的 prepareContext 方法如下:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    listeners.contextLoaded(context);
}


该方法的最后通过调用 load 方法进行加载 bean 到应用上下文中:

/**
  * Load beans into the application context.
  * @param context the context to load beans into
  * @param sources the sources to load
  */
protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
        logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    //创建BeanDefinitionLoader
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    loader.load();
}


传入进 load 方法的 sources 参数是我们项目定义的启动类。

在 load 方法中会先根据上下文去获取 Bean definition registry

/**
  * Get the bean definition registry.
  * @param context the application context
  * @return the BeanDefinitionRegistry if it can be determined
  */
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
    if (context instanceof BeanDefinitionRegistry) {
        return (BeanDefinitionRegistry) context;
    }
    if (context instanceof AbstractApplicationContext) {
        return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
    }
    throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}


接着会创建 BeanDefinitionLoader 实例:

BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);

protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
    return new BeanDefinitionLoader(registry, sources);
}

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    Assert.notNull(registry, "Registry must not be null");
    Assert.notEmpty(sources, "Sources must not be empty");
    this.sources = sources;
    // 注解形式的Bean定义读取器
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    // XML形式的Bean定义读取器
    this.xmlReader = new XmlBeanDefinitionReader(registry);
    // Groovy形式的Bean定义读取器
    if (isGroovyPresent()) {
        this.groovyReader = new GroovyBeanDefinitionReader(registry);
    }
    // 类路径扫描器
    this.scanner = new ClassPathBeanDefinitionScanner(registry);
    // 扫描器添加要过滤的类
    this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}


在该构造器中,有三种形式的 Bean 定义读取器,可以通过不同的方式去读取已经定义的 Bean,另外还提供了类路径下的扫描器和过滤器。

我们来看下该类的属性除了有上面几个之外,还有一个 ResourceLoader :



另外我们来看最关键的 load 方法:

int load() {
    int count = 0;
    for (Object source : this.sources) {
        count += load(source);
    }
    return count;
}

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());
}


load 方法会统计所 load 的 source 数量,在 load 中有几种类别可以选择,比如 Class、Resource、Package、CharSequence。

而对应着这几种类别又分别有不同的 load 方法:

private int load(Class<?> source) {
    if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        // Any GroovyLoaders added in beans{} DSL can contribute beans here
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
        load(loader);
    }
    if (isComponent(source)) {
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

private int load(GroovyBeanDefinitionSource source) {
    int before = this.xmlReader.getRegistry().getBeanDefinitionCount();
    ((GroovyBeanDefinitionReader) this.groovyReader).beans(source.getBeans());
    int after = this.xmlReader.getRegistry().getBeanDefinitionCount();
    return after - before;
}

private int load(Resource source) {
    if (source.getFilename().endsWith(".groovy")) {
        if (this.groovyReader == null) {
            throw new BeanDefinitionStoreException("Cannot load Groovy beans without Groovy on classpath");
        }
        return this.groovyReader.loadBeanDefinitions(source);
    }
    return this.xmlReader.loadBeanDefinitions(source);
}

private int load(Package source) {
    return this.scanner.scan(source.getName());
}

private int load(CharSequence source) {
    String resolvedSource = this.xmlReader.getEnvironment().resolvePlaceholders(source.toString());
    // Attempt as a Class
    try {
        return load(ClassUtils.forName(resolvedSource, null));
    }
    catch (IllegalArgumentException | ClassNotFoundException ex) {
        // swallow exception and continue
    }
    // Attempt as resources
    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;
    }
    // Attempt as package
    Package packageResource = findPackage(resolvedSource);
    if (packageResource != null) {
        return load(packageResource);
    }
    throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
}


对于 Class 类型的在其方法内会做判断是否为 GroovyBeanDefinitionSource 和判断是否有 Component 注解。

private int load(Class<?> source) {
    if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        // Any GroovyLoaders added in beans{} DSL can contribute beans here
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
        load(loader);
    }
    if (isComponent(source)) {
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

private int load(GroovyBeanDefinitionSource source) {
    int before = this.xmlReader.getRegistry().getBeanDefinitionCount();
    ((GroovyBeanDefinitionReader) this.groovyReader).beans(source.getBeans());
    int after = this.xmlReader.getRegistry().getBeanDefinitionCount();
    return after - before;
}

对于 Resource 类型的只需要根据文件结尾来判断就行了,比如是否有 .groovy 结尾。

private int load(Resource source) {
    if (source.getFilename().endsWith(".groovy")) {
        if (this.groovyReader == null) {
            throw new BeanDefinitionStoreException("Cannot load Groovy beans without Groovy on classpath");
        }
        return this.groovyReader.loadBeanDefinitions(source);
    }
    return this.xmlReader.loadBeanDefinitions(source);
}


对于 Package 类型只需要根据给定的包名进行扫描该包就行了。

private int load(Package source) {
    return this.scanner.scan(source.getName());
}


对于 CharSequence 类型的就是先将其转成字符串,然后通过反射将其转成 Class,然后调用 Class 类型的 load 方法;如果先将其用 Class load 失败的话,接着会用 Resource 的方式去 load,如果还是失败的话,最后是通过 Package 的方式去 load,如果还是不行,则会抛出异常说明这是目前不支持的 source 形式。

private int load(CharSequence source) {
    String resolvedSource = this.xmlReader.getEnvironment().resolvePlaceholders(source.toString());
    // Attempt as a Class
    try {
        return load(ClassUtils.forName(resolvedSource, null));
    }
    catch (IllegalArgumentException | ClassNotFoundException ex) {
        // swallow exception and continue
    }
    // Attempt as resources
    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;
    }
    // Attempt as package
    Package packageResource = findPackage(resolvedSource);
    if (packageResource != null) {
        return load(packageResource);
    }
    throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
}

而这我们通常有哪几种方式传递进来呢?比如:

public class BootStrapMain {

    public static void main(String[] args) {
        // class 形式
        SpringApplication.run(BootStrapMain.class, args);
        // Resource 形式
        //SpringApplication.run(new ClassPathResource("applicationContext.xml"), args); 
        // Package 形式
        //SpringApplication.run(Package.getPackage("com.zhisheng.springboot.learning"), args);
        // CharSequence 类型
        //SpringApplication.run("classpath:/applicationContext.xml", args);
    }

}

关注我

关注我,Java 学习不迷路!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值