美文网首页
初始化bean(一)—— 首次加载

初始化bean(一)—— 首次加载

作者: 编程易行 | 来源:发表于2018-08-05 17:10 被阅读21次

    上一篇博客,讲了下spring如何解析xml,并将我们的配置转换成BeanDefinition,最终注册到BeanDefinitionRegistry中(默认实现,DefaultListableBeanFactory)接下来就要说明,spring拿到这个BeanDefinition后,如何将其初始化。由于内容比较多,会分几次来说明。

    从一个入口开始

    public class TestDemo {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            Person person = (Person) context.getBean("person");
            System.out.println("person name:" + person.getName());
        }
    }
    

    回到第一篇博客,讲spring如何加载配置文件的地方开始spring资源文件的加载

    使用spring获取bean的入口,就在context.getBean()

    @Override
    public Object getBean(String name) throws BeansException {
        //断言spring是否已初始化,并且未关闭
        assertBeanFactoryActive();
        //这里的beanFactory默认是DefaultListableBeanFactory
        return getBeanFactory().getBean(name);
    }
    

    ClassPathApplicationContext将获取bean的方法,委托给了DefaultListableBeanFactory

    获取bean流程

    由于bean的加载及获取流程很长,本次分析,只考虑第一次加载的情况。去掉这些缓存及些异常处理,日志的代码,然后也只分析 singleton的情况,整个流程大概这样。

    protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {
        //1、转换beanName
        final String beanName = transformedBeanName(name);
        Object bean;
    
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            // 有缓存 ...
        } else {
            // ... 
    
            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // 父 factory 里加载
            }
    
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }
    
            //2、转换成RootBeanDefinition
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);
    
            //依赖,先不考虑
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                //...
            }
    
            // Create bean instance.
            if (mbd.isSingleton()) {
                //3、关键:单例类的初始化
                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()) {
                //prototype ...
            }
            else {
                // 其他scope ...
            }
        }
    
        // Check if required type matches the type of the actual bean instance.
        if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
            // 如果类型不一致,做类型转换
            return getTypeConverter().convertIfNecessary(bean, requiredType);
        }
        return (T) bean;
    }
    

    1、转换beanName

    // 1、对于factoryBean &bean  -> bean
    // 2、对于别名,将其转换成真实的名字
    final String beanName = transformedBeanName(name);
    

    这一步主要是将FactoryBean的前缀替换掉,比如context.getBean("&factoryBean"),beanName会去掉前缀,被替换成factoryBean,关于FactoryBean和BeanFactory的区别,这里就不介绍了。

    2、转换成RootBeanDefinition

    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    //如果要初始化的是一个抽象类,就抛异常
    checkMergedBeanDefinition(mbd, beanName, args); 
    

    之前分析BeanDefinition时候,我们知道了注册的BeanDefinition实现类是GenericBeanDefinition。但是获取Bean的步骤中,使用的都是RootBeanDefinition,所以这里做了一次GenericBeanDefinition到RootBeanDefinition的转换。

    3、单例类的初始化

    随后,我们最后要分析的就是,最关键的一步,单例对象的初始化。

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

    这里,调用了getSingleton方法。

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory);
    

    这里先记着,调用getSingleton时候传了两个参数:
    1)beanName
    2)一个ObjectFactory的匿名内部类,getObject的实现是直接调用createBean方法

    随后我们继续看getSingleton方法。同样,这里也会去掉很多干扰的代码(比如日志、异常处理)

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "'beanName' must not be null");
        synchronized (this.singletonObjects) {
            //先试着从缓存中加载
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName,
                            "Singleton bean creation not allowed while the singletons of this factory are in destruction " +
                            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    //调用ObjectFactory的getObject生成这个对象
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    //添加到缓存里
                    addSingleton(beanName, singletonObject);
                }
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }
    

    如果忽略掉所有的异常处理逻辑,这个方法其实就做了三件事。
    1)试着从缓存里获取这个对象(缓存就是个map)
    2)回调objectFactory的getObject方法创建这个对象(模板方法模式)
    3)存到缓存里

    3.1 bean真正创建的地方createBean方法

    默认实现是在AbstractAutowireCapableBeanFactory里。这个方法很长,也做了很多的回调。这里只分析最重要的一部分,初始化。跟到最后,源码是在InstantiationStrategy里。

    同样默认实现在SimpleInstantiationStrategy里

    public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (bd.getMethodOverrides().isEmpty()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                                @Override
                                public Constructor<?> run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[]) null);
                                }
                            });
                        }
                        else {
                            constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            //反射调用 Constructor.newInstance方法
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }
    

    这里的实现,就是简单的通过反射调用 Constructor.newInstance 来初始化对象。

    3.2 回到getObjectForBeanInstance方法

    已经跟到createBean这一步了,我们回头看看获取到这个对象后,spring又做了什么。

    // 省略匿名内部类
    sharedInstance = getSingleton(beanName, ...);
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    

    下一步就是调用getObjectForBeanInstance

    protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
    
        // 以 & 开头,但是创建的对象不是 FactoryBean就抛异常
        if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
        }
    
        // 不是FactoryBean 或者 要获取的就是FactoryBean (以&开头的name)
        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }
    
        Object object = null;
        if (mbd == null) {
            object = getCachedObjectForFactoryBean(beanName);
        }
        if (object == null) {
            // Return bean instance from factory.
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            if (mbd == null && containsBeanDefinition(beanName)) {
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            //获取FactoryBean生成的bean
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }
    

    这一步,涉及到FactoryBean和BeanFactory的区别。这里只说明如下几点。
    1)如果一个bean的class 配置了实现FactoryBean接口的类,比如
    <bean id="person" class="com.hdj.PersonFactoryBean"/> 那么,通过context.getBean("person") 获取到的就不是PersonFactoryBean而是PersonFactoryBean.getObject返回的对象。
    2)如果想要获取PersonFactoryBean对象,就需要这样调用context.getBean("&person")

    说明了这两点,这里就挺容易理解的了。
    1)不是FactoryBean 或者 要获取的就是FactoryBean (以&开头的name) 直接返回createBean创造的对象即可
    2)是FactoryBean 并且要获取的是getObject 对象,需要返回 FactoryBean.getObject 对象。

    此外,这里省略了getObjectFromFactoryBean的细节,因为涉及了很多缓存。这些缓存的关系,下面几篇博客会分析。

    总结

    绕了很半天,终于得以发现spring的初始化逻辑的很小一部分。
    1)获取beanName对应的Class类。
    2)通过反射方式,初始化它。
    3)如果是FactoryBean 还要返回getObject 对象
    4)缓存最后的结果

    当然这里的分析省略了很多,比如:
    1)构造函数的选择,如果一个对象有多个构造函数,那么如何选择构造函数
    2)依赖注入如何实现
    3)各种PostProcessor

    这些内容都会在后续博客里一点点说明

    思考

    学习spring时候,都会学到有一步骤,beanFactory.getBean("person")通过这一行代码,一步步跟踪代码,我们很自然地会知道spring的入口在哪,spring是如何帮我们初始化bean的,又是如何实现依赖注入、aop等一系列强大的功能。

    但是,现在开发大多是web工程,对于一个web工程,spring的入口又在哪里?甚至对于springboot来说,连xml都没了,spring的入口又在哪里?

    spring为我们简化了开发的很多步骤,阅读源码又是个挺困难过程。希望能坚持下去,一一弄清楚一系列平常开发所接触不到的问题。

    相关文章

      网友评论

          本文标题:初始化bean(一)—— 首次加载

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