1. 回顾

通过前两天的学习,我们知道了spring的ioc框架的实现原理。我们常用的各种容器类,其实是最顶层接口BeanFactory的一个实现,它负责注册并管理所有的bean对象。读取并解析xml文件也有一整套机制来实现,另外bean对象在spring中是通过最顶层的接口BeanDefinition来定义的。

今天,在前两天学习的基础上,我们还是要带着问题去找答案:

  1. 容器是如何根据bean的名称或类型去获取对象的?

根据前面的学习,可以推测一下这个问题的答案。前面我们说,spring通过一个Map来存储bean-namebean-definition,那么根据名称获取bean,就是从一个Map中获取指定key的value:

Map<String, Object> beans = new HashMap<String, Object>();
Map<Class, Object> classBeans = new HashMap<String, Object>();

Object bean = beans.get(name);
  1. spring是否是通过反射来根据classname获取一个真正的对象的?

2. 从容器中获取对象

User user = applicationContext.getBean(User.class);

跟踪代码进去,找到DefaultListableApplicationContext类的getBean方法:

protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

    final String beanName = transformedBeanName(name);
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        ......
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // Fail if we're already creating this bean instance:
        // We're assumably within a circular reference.
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // Check if bean definition exists in this factory.
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Not found -> check parent.
            String nameToLookup = originalBeanName(name);
            if (args != null) {
                // Delegation to parent with explicit args.
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else {
                // No args -> delegate to standard getBean method.
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }

        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // Guarantee initialization of beans that the current bean depends on.
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dependsOnBean : dependsOn) {
                    if (isDependent(beanName, dependsOnBean)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
                    }
                    registerDependentBean(dependsOnBean, beanName);
                    getBean(dependsOnBean);
                }
            }

            // Create bean instance.
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // Check if required type matches the type of the actual bean instance.
    if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
        try {
            return getTypeConverter().convertIfNecessary(bean, requiredType);
        }
        catch (TypeMismatchException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to convert bean '" + name + "' to required type [" +
                        ClassUtils.getQualifiedName(requiredType) + "]", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
}

这是spring获取bean的核心方法,获取一个bean的逻辑是:

  1. 从单例对象缓存中获取该注册对象;
  2. 如果单例缓存中没有获取到该对象,那么走else的逻辑;

经过调试,会发现最终是从单例的缓存中获取到了User对象,并没有走到else里面去。结合前两天学到的东西,由此便产生了两个疑惑:

  1. 究竟是什么时候,实例化的User bean,并放到了singoton缓存中?
  2. spring中的bean默认是单例?

2.1. bean默认是否是单例?

要搞清楚bean默认是否是单例的问题很简单,我们只需要找到创建BeanDefinition对象的代码,查看它的属性scope是如何赋值的即可知道:

找到BeanDefinitionParseDelegate解析xml部分的代码:

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
            BeanDefinition containingBean, AbstractBeanDefinition bd) {

    if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
        error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
    }
    else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
        bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
    }
    else if (containingBean != null) {
        // Take default from containing bean in case of an inner bean definition.
        bd.setScope(containingBean.getScope());
    }
    ...
}

就看上面的代码片段,会发现是否单例是由xml文件中beanscope属性决定的。我们的demo中user并没有配置此属性,所以其采用的是默认值。

看一下默认值是的源代码和判断是否为单例的源代码:

private String scope = SCOPE_DEFAULT;

/**
 * Constant for the default scope name: {@code ""}, equivalent to singleton
 * status unless overridden from a parent bean definition (if applicable).
 */
public static final String SCOPE_DEFAULT = "";

@Override
public boolean isSingleton() {
    return SCOPE_SINGLETON.equals(scope) || SCOPE_DEFAULT.equals(scope);
}

通过上面代码片段发现:

  1. 如果没有指定scope属性的值,默认值为""。
  2. 如果scope的值为singleton或空,则为单例;

由此可见,==只要在配置bean的时候没有特意指定scope属性,spring默认把bean做为单例做管理;==

2.2. 单例在什么时候实例化?

我们在获取user对象的时候,直接便从单例的缓存中获取到了,这说明在容器初始化完成的时候,所有的单例已被实例化,并缓存在了单例的缓存中。

所以要搞清楚这个问题,又不得不回到容器初始化的整个过程中。在之前研究容器初始化的时候,我们留了一个问题。容器初始化的关键函数是refresh,此方法在AbstractApplicationContext抽象类中实现,在ConfigurableApplicationContext接口中定义,它是整个容器初始化的一个模板模式定义。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            ...
}

当时研究初始化的时候,重点研究了第二句代码:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();,其他代码当时作为了遗留问题保留下来。

通过调试会发现,单例对象实例化是在

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

这句代码中做的。这句代码最终还是调用了上面的getBean方法,并且由是从单例缓存中没有找到此对象,所以走了else分支,else分支里有创建实例的逻辑。

最低层创建对象的方式是:

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
    try {
        ReflectionUtils.makeAccessible(ctor);
        return ctor.newInstance(args);
    }
    ...
}

具体的细节代码就不贴出来了,最终是使用构造函数对象创建的实例。

3. 总结

学习到今天,我们基本上已经明白了spring ioc容器的整个原理:

  1. spring提供了一整套工具类来从资源Resource(xml、注解等)文件中读取、解析所有的bean
  2. spring使用BeanDefinition接口的实现类来表示bean的配置;
  3. spring表从配置文件中读取的bean存储在BeanFactory工厂中,此工厂提供了一系列的注册、管理方法,这也是spring的核心。
  4. spring容器的初始化,使用了一个模板方法,我们重点研究了两个方法,一个用来创建工厂容器,另外一个我们知道的是提供了单例初始化的功能;

理解到这个程度,我们已初步理解spring的原理,当然具体的一些细节用到的时候再去看代码即可。接下来可以做的事情是:

  1. 可以尝试自己实现一个最简单的ioc容器了;
  2. 可以研究一些细节方面的问题,比如说<bean id="user" ...的其他属性是怎么实现的,如init-method等;
  3. 可以研究更详细的一些功能,如BeanPostProcessorInitializingBeanDisposableBean接口功能是如何实现的,等。
Copyright © wychuan.com 2017 all right reserved,powered by Gitbook该文件修订时间: 2018-01-09 08:05:07

results matching ""

    No results matching ""