美文网首页
4. bean的加载

4. bean的加载

作者: 凭窗听风 | 来源:发表于2019-04-14 18:39 被阅读0次

    spring源码学习笔记,要点归纳和代码理解

    前言

    经过了三节的的铺垫,终于到了对象创建的核心部分了,bean的整个加载过程代码量非常大,逻辑错综复杂,因此这部分我的原则是以理解和把控关键过程和逻辑为基本,不局限于细节实现,以AbstractBeanFactorydoGetBean方法为骨架阅读抽象出的方法代码,对于重点逻辑详细解析.

    bean加载的整体流程

    首先贴出精简了异常处理逻辑的doGetBean方法代码

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
            // 转换beanName,将alias或id或className统一转换为beanName
            final String beanName = transformedBeanName(name);
            Object bean;
    
            // Eagerly check singleton cache for manually registered singletons.
            // 从缓存中尝试获取实例
            Object sharedInstance = getSingleton(beanName);
            if (sharedInstance != null && args == null) {
                // 缓存中取出的不一定是创建好的实例,有可能是提早曝光加入到缓存的factoryBean
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
            } else {
                // Fail if we're already creating this bean instance:
                // We're assumably within a circular reference.
                // 当前bean如果在创建过程中,则发生循环依赖,抛出异常
                if (isPrototypeCurrentlyInCreation(beanName)) {
                    throw new BeanCurrentlyInCreationException(beanName);
                }
    
                // Check if bean definition exists in this factory.
                // 检测父类工厂,此处为了保证bean的父类能够被先于子实例加载
                BeanFactory parentBeanFactory = getParentBeanFactory();
                if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                    // Not found -> check parent.
                    String nameToLookup = originalBeanName(name);
                    if (parentBeanFactory instanceof AbstractBeanFactory) {
                        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                                nameToLookup, requiredType, args, typeCheckOnly);
                    }
                    else if (args != null) {
                        // Delegation to parent with explicit args.
                        return (T) parentBeanFactory.getBean(nameToLookup, args);
                    }
                    else if (requiredType != null) {
                        // No args -> delegate to standard getBean method.
                        return parentBeanFactory.getBean(nameToLookup, requiredType);
                    }
                    else {
                        return (T) parentBeanFactory.getBean(nameToLookup);
                    }
                }
                // 标记已创建
                if (!typeCheckOnly) {
                    markBeanAsCreated(beanName);
                }
    
                    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 dep : dependsOn) {
                            if (isDependent(beanName, dep)) {
                                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                            }
                            registerDependentBean(dep, beanName);
                            getBean(dep);
                            
                        }
                    }
    
                    // Create bean instance.
                    if (mbd.isSingleton()) { // 创建单例bean
                        sharedInstance = getSingleton(beanName, () -> {
                            return createBean(beanName, mbd, args);
                        });
                        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 + "'");
                        }
                            Object scopedInstance = scope.get(beanName, () -> {
                                beforePrototypeCreation(beanName);
                                return createBean(beanName, mbd, args);
                            });
                            bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                        
                    }
                
            }
    
            // Check if required type matches the type of the actual bean instance.
            // 类型检查
            if (requiredType != null && !requiredType.isInstance(bean)) {
                    T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                    if (convertedBean == null) {
                        throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                    }
                    return convertedBean;
                
            }
            return (T) bean;
        }
    

    整个加载过程的步骤大致如下:

    1. 转换对应的beanName
    2. 尝试从缓存中加载实例
    3. bean的实例化
    4. prototype的依赖检查
    5. 检测parentBeanFactory
    6. 将GernericBeanDefinition 转换为RootBeanDefinition
    7. 寻找依赖
    8. 针对不同scope创建bean
    9. 类型转换

    循环依赖的处理

    • 首先解释下什么是循环依赖:
      对于两个class A和B,以及他们对应的bean a和b,A中有类型为B的属性b,B中有类型为A的属性a,两个对象都通过spring管理并且属性都通过spring自动注入,此时便形成了循环依赖.
    • spring对于循环依赖的解决:
      整体思路是:
    1. 使用factoryBean封装创建对象的逻辑
    2. 将正在创建的bean对应的factoryBean暴露到缓存中
    3. 创建bean时先尝试从几个缓存中取出bean或bean的factoryBean

    主要应用到四个对象和两个方法:

    1. singletonObjects: 保存创建的bean和beanName的关系
    2. singletonFactories: 保运BeanName和创建bean的工厂之间的关系
    3. earlySingletonObjects: 保存beanName和bean实例之间的关系,与singletonObjects的区别是,当一个单例bean被放到这里后,那么即使bean还在创建过程中,也可以通过getBean方法从这个缓存中获取
    4. registeredSingletons: 用来保存的当前所有已注册的bean
      DefaultSingletonBeanRegistry中的两个方法
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(singletonFactory, "Singleton factory must not be null");
            synchronized (this.singletonObjects) {
                if (!this.singletonObjects.containsKey(beanName)) {
                    this.singletonFactories.put(beanName, singletonFactory);
                    this.earlySingletonObjects.remove(beanName);
                    this.registeredSingletons.add(beanName);
                }
            }
        }
    
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null && allowEarlyReference) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return singletonObject;
        }
    
    • 具体步骤:
    1. spring容器创建单例TestBeanA, 根据无参构造器创建,并通过ObjectFactory接口暴露getEarlyBeanReference()方法,将beanName和封装该方法的ObjectFactory添加到singletonFactory缓存中
    2. 在执行A的populateBean方法,即装配A的属性时,发现B的依赖,执行getBean(B)方法
    3. 创建单例B,将B的ObjectFactory加入到singletonFactory,之后继续执行B的populateBean,
    4. 发现依赖注入中存在TestBeanA,调用getBean(A),这时会通过getSingle(A)的方法调用到之前暴露的getEarlyBeanReference方法,这个方法如下,会返回a的实例.
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
            Object exposedObject = bean;
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                        exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                    }
                }
            }
            return exposedObject;
        }
    

    这里附一张调用栈的截图,一目了然.


    • 需要注意的:
      spring只会针对singleton的setter注入解决循环依赖,对于构造器循环依赖和prototype的循环依赖会直接抛出异常,这是因为spring只缓存single scope的bean,也就是说上述代码逻辑对prototype不适用.而spring将在创建过程中的bean放入一个threadLocal的prototypesCurrentlyInCreation对象中,通过构造器注入的对象会无法通过此处校验而抛出异常.
      可以通过((XmlBeanFactory) bf).setAllowCircularReferences(false);来禁用循环引用

    创建bean

    创建bean的实现通过lambda表达式将AbstractBeanFactory的doGetBean方法封装到factoryBean中进行的.

    • 主要步骤:
    1. 创建bean的实例
      a. 确定构造函数
      b. 判断beanDefinition中的getMethodOverrides,决定直接反射构造bean还是通过动态代理的方式构造bean,是AOP实现的切入点
    2. 记录创建bean的ObjectFactory, 循环依赖的解决
    3. 属性注入
    4. 初始化bean
      a. 激活aware方法 -- 为实现Aware接口的的bean对象注入对应的对象
      b. 处理器的应用 -- 为BeanFactory增加BeanPostProcessor,一种增强器,在执行初始化方法之前和之后执行,可以添加业务逻辑
      c. 激活自定义的init方法 -- 配置文件的init-method配置
    5. 注册DisposableBean

    后记

    bean的加载可以说是spring的两大核心之一了,也是aop实现的切入点,这部分代码多多阅读,可以把控spring的整体运行原理,对于一些定制的需求也可以更灵活的实现.
    对于复杂的逻辑进行分解,分成N个小函数的嵌套,每一层都是对下一层逻辑的总结和概要,这样使得每一层逻辑会变得简单容易理解.

    附 测试代码

    • bean加载的各个情况
    public class MyTestBean {
    
        private String name = "testName";
    
        private Integer id;
    
        public MyTestBean(Integer id) {
            this.id = id;
        }
    
        @Override
        public String toString() {
            return "MyTestBean{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    '}';
        }
    
        public MyTestBean() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        private void init() {
            System.out.println("测试bean被初始化了");
        }
    }
    
    public abstract class LookupTestBean {
    
        public void doSomething() {
            System.out.println(getTestBean());
        }
    
        public abstract MyTestBean getTestBean();
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean name="myTestBean" class="com.pctf.beans.MyTestBean" init-method="init">
            <property name="name" value="测试对象"></property>
            <constructor-arg index="0" value="10"></constructor-arg>
    
        </bean>
    
        <bean name="lookupTestBean" class="com.pctf.beans.LookupTestBean">
            <lookup-method name="getTestBean" bean="myTestBean"></lookup-method>
        </bean>
    
        <beans profile="dev">
    
        </beans>
    </beans>
    
    public class AppTestLaunch {
    
        public static void main(String[] args) {
            XmlBeanFactory bf = new XmlBeanFactory( new ClassPathResource("spring-config.xml"));
            bf.addBeanPostProcessor(new PostProcessorTest());
            MyTestBean myTestBean = (MyTestBean) bf.getBean("myTestBean");
            LookupTestBean lookupTestBean = (LookupTestBean) bf.getBean("lookupTestBean");
            lookupTestBean.doSomething();
            System.out.println(myTestBean.getName());
        }
    }
    
    • 循环依赖的测试代码
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean class="com.pctf.beans.TestBeanA" name="testBeanA">
            <property name="name" value="测试BeanA"></property>
            <!--<property name="testBeanB" ref="testBeanB"></property>-->
            <constructor-arg value="testBeanB" index="0"></constructor-arg>
        </bean>
    
        <bean class="com.pctf.beans.TestBeanB" name="testBeanB">
            <property name="name" value="测试BeanB"></property>
            <!--<property name="testBeanA" ref="testBeanA"></property>-->
            <constructor-arg value="testBeanA" index="0"></constructor-arg>
        </bean>
    
    </beans>
    
    import com.pctf.beans.TestBeanA;
    import com.pctf.beans.TestBeanB;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    
    public class BeanLoadingTest {
    
        public static void main(String[] args) {
            BeanFactory bf = new XmlBeanFactory(new ClassPathResource("bean-loading-config.xml"));
            //((XmlBeanFactory) bf).setAllowCircularReferences(false);
            TestBeanA testBeanA = (TestBeanA) bf.getBean("testBeanA");
            TestBeanB testBeanB = bf.getBean(TestBeanB.class);
            System.out.println(testBeanA.getName());
            System.out.println(testBeanB.getName());
        }
    }
    
    

    相关文章

      网友评论

          本文标题:4. bean的加载

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