专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

IOC(二)DefaultListableBeanFactory

前言

上一章我们已经初步认识了BeanFactory和BeanDefinition,一个是IOC的核心工厂接口,一个是IOC的bean定义接口。

spring无法让BeanFactory持有一个Map<String,Object>来完成bean工厂的功能,是因为spring的初始化是可以控制的,可以到用的时候才将bean实例化供开发者使用,除非我们将bean的lazy-init属性设置为true,初始化bean工厂时采用延迟加载。

那么知道了上述两个接口,我相信不少人甚至不看源码都已经猜到spring是如何做的了。没错,就是让bean工厂持有一个Map<String,BeanDefinition>,这样就可以在任何时候我们想用哪个bean,取到它的bean定义,我们就可以创造出一个新的实例。

接口当然不可能持有这样一个对象,那么这个对象一定是在BeanFactory的某个实现类或者抽象实现类当中所持有的,来看DefaultListableBeanFactory。

一、源码分析

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    private static Class<?> javaUtilOptionalClass = null;

    private static Class<?> javaxInjectProviderClass = null;

    static {
        try {
            javaUtilOptionalClass =
                    ClassUtils.forName("java.util.Optional", DefaultListableBeanFactory.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            // Java 8 not available - Optional references simply not supported then.
        }
        try {
            javaxInjectProviderClass =
                    ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            // JSR-330 API not available - Provider interface simply not supported then.
        }
    }

    /** Map from serialized id to factory instance */
    private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
            new ConcurrentHashMap<String, Reference<DefaultListableBeanFactory>>(8);

    /** Optional id for this factory, for serialization purposes */
    private String serializationId;

    /** Whether to allow re-registration of a different definition with the same name */
    private boolean allowBeanDefinitionOverriding = true;

    /** Whether to allow eager class loading even for lazy-init beans */
    private boolean allowEagerClassLoading = true;

    /** Optional OrderComparator for dependency Lists and arrays */
    private Comparator<Object> dependencyComparator;

    /** Resolver to use for checking if a bean definition is an autowire candidate */
    private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();

    /** Map from dependency type to corresponding autowired value */
    private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<Class<?>, Object>(16);

    /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

    /** Map of singleton and non-singleton bean names, keyed by dependency type */
    private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);

    /** Map of singleton-only bean names, keyed by dependency type */
    private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);

    /** List of bean definition names, in registration order */
    private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);

    /** List of names of manually registered singletons, in registration order */
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<String>(16);

    /** Cached array of bean definition names in case of frozen configuration */
    private volatile String[] frozenBeanDefinitionNames;

    /** Whether bean definition metadata may be cached for all beans */
    private volatile boolean configurationFrozen = false;

该类的属性就在这里了,至于方法此处被我省略,因为太长了…

看它名字就知道,这是一个默认的bean工厂实现类,根据类注释可以知道这是一个完整的,成熟的IOC容器。也就是说,如果你需要的功能非常单一,这个实现类已经足够可以满足你了,而以后如果你想要对spring的容器扩展,那么只需要扩展或者持有这个对象即可。

/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

看到这一行,其实已经证明了我的猜测,它所注释的意思是bean定义的map对象,采用beanName作为key值。

只继承了一个抽象类,同时把集大成的接口ConfigurableListableBeanFactory给实现了,同时还有BeanDefinitionRegistry接口,从名字上看就知道是BeanDefinition的注册接口。

看到这里,思路已经很明确了,bean工厂的初始化其实就是往这个Map里填充东西。只要把我们XML文件中定义的bean都填充到这里,其实这个工厂就已经可以工作了。

那么从现在来看,我们需要什么才能把Map填充呢?也就是初始化bean工厂呢,或者说建立IOC容器。

二、IOC容器实现原理讨论

这次先不关注源码,先首先看看它的用法。

ClassPathResource res=new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
Performer performer=(Performer) factory.getBean("dancer");
performer.perform();

简单解释一下:

1、我们在beans.xml中定义了一个Bean,id为“dancer”,实现了Performer接口。参考自例子.

2、利用Resource抽象实现类,来包装这个包含了BeanDefinition的定义信息。

3、创建一个BeanFactory,DefaultListableBeanFactory

4、创建一个载入BeanDefinition的读取器,通过回调配置给BeanFactory.

5、 从定义好的Resource中,读取配置信息。由XmlBeanDefinitionReader完成解析,完成整个载入和注册Bean定义的过程。

6、 通过BeanFactory的getBean()方法,获取对应的Bean。这里涉及到了Bean的实例化以及依赖注入

依赖注入的过程

以上过程可以看成是IOC容器初始化的过程,这个初始化过程完成的主要工作是完成BeanDefinition在IOC容器中的数据映射。而这里并没有实例化任何对象,也从来没有完成过依赖注入的过程。

依赖注入是通过getBean方法完成的,当然如果将lazy-init属性设置为true,则可以在初始化的过程中完成依赖注入,实现预实例化。

Performer performer=(Performer) factory.getBean("dancer");通过getBean获得名为“dancer”的对象,而factory现在保有的仅仅是键为“dancer”的BeanDefinition对象(保存在Map中)。

而通过源码可以发现getbean()方法是由AbstractBeanFactory实现的,而具体实现又跳到了doGetBean()方法,

doGetBean()方法负责从Bean工厂中获取bean对象的具体实现,下面来看看该方法的具体实现:

1、 检查手动注册的单例集合缓存中是否含有该bean对象,若有,则取出返回,否则继续执行;
2、 检查该bean是否已经创建,从而判断是否属于循环引用,若是,抛出异常返回,否则继续执行;
3、 判断bean工厂中是否存在该bean definition,若存在,则取出返回,否则继续执行;
4、 初始化该bean所依赖的bean对象;
5、 判断该bean是否是单例模式(singleton),若是,创建单例对象,否则继续执行;
6、 判断该bean是否是原型模式(prototype),若是,创建原型对象,否则继续执行;
7、 创建自定义类型(scope)bean对象。
8、 从上面对doGetBean方法分析,可看出创建并获取bean对象是一个非常复杂的过程,并不是简简单单的放入Map中再从其中取出。

三、小结

整篇文章下来,可以看到DefaultListableBeanFactory实现了IOC容器的初始化并且通过getbean()完成了依赖注入。整个IOC容器初始化过程总结起来就是定位(定位到配置文件)、解析(解析配置文件,一般是XML)、注册(将读出来的数据放到map中)。

这篇文章里我并没有仔细记录源码的执行过程,因为内容实在是太多,我只是通过一个demo讲述了DefaultListableBeanFactory获取一个bean的大概过程。具体源码执行过程还需要各位读者自己去探索,我也是自己通过源码把这个过程探索了一遍才知道IOC容器初始化的过程,不过其中有些地方还没有完全领悟,待自己有所提升后再回来看看应该会有不一样的感受。

依赖注入过程参考自

blog.ygingko.top/2017/08/19/…

文章永久链接:https://tech.souyunku.com/41195

未经允许不得转载:搜云库技术团队 » IOC(二)DefaultListableBeanFactory

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们