• 【转载】Spring 源码分析之 bean 实例化原理


    本次主要想写spring bean的实例化相关的内容。创建spring bean 实例是spring bean 生命周期的第一阶段。bean 的生命周期主要有如下几个步骤:

    • 创建bean的实例
    • 给实例化出来的bean填充属性
    • 初始化bea
    • 通过IOC容器使用bean
    • 容器关闭时销毁bean

    在实例化bean之前在BeanDefinition里头已经有了所有需要实例化时用到的元数据,接下来spring 只需要选择合适的实例化方法以及策略即可。实例化方法有两大类分别是工厂方法和构造方法实例化,后者是最常见的。spring默认的实例化方法就是无参构造函数实例化。
    如我们在xml里定义的 <bean id="xxx" class="yyy"/> 以及用注解标识的bean都是通过默认实例化方法实例化的。

    • 两种实例化方法(构造函数 和 工厂方法)
    • 源码阅读
    • 实例化策略(cglib or 反射)

    两种实例化方

    使用适当的实例化方法为指定的bean创建新实例:工厂方法,构造函数实例化。

    代码演示

    启动容器时会实例化所有注册的bean(lazy-init懒加载的bean除外),对于所有单例非懒加载的bean来说当从容器里获取bean(getBean(String name))的时候不会触发,实例化阶段,而是直接从缓存获取已准备好的bean,而生成bean的时机则是下面这行代码运行时触发的。

      @Test
        public void testBeanInstance(){
            // 启动容器
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
        }
    

    一 使用工厂方法实例化(很少用)

    1.静态工厂方法
    public class FactoryInstance {
    
        public FactoryInstance() {
            System.out.println("instance by FactoryInstance");
        }
    }
    
    public class MyBeanFactory {
    
        public static FactoryInstance getInstanceStatic(){
            return new FactoryInstance();
        }
    }
    
    <?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <bean id="factoryInstance" class="spring.service.instance.MyBeanFactory" 
              factory-method="getInstanceStatic"/>
    </beans>
    

    输出结果为:

    instance by FactoryInstance

    2.实例工厂方法
    public class MyBeanFactory {
    
        /**
         * 实例工厂创建bean实例
         *
         * @return
         */
        public FactoryInstance getInstance() {
            return new FactoryInstance();
        }
    }
    
    <?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 工厂实例 -- >   
        <bean id="myBeanFactory" class="MyBeanFactory"/>
        <bean id="factoryInstance" factory-bean="myBeanFactory" factory-method="getInstance"/>
    </beans>
    

    输出结果为:

    instance by FactoryInstance

    二 使用构造函数实例化(无参构造函数 & 有参构造函数)

    1.无参构造函数实例化(默认的)
    public class ConstructorInstance {
    
        public ConstructorInstance() {
            System.out.println("ConstructorInstance none args");
        }
    
    }
    
    <?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance"/>
    </beans>
    

    输出结果为:

    ConstructorInstance none args

    1.有参构造函数实例化
    public class ConstructorInstance {
    
        private String name;
    
        public ConstructorInstance(String name) {
            System.out.println("ConstructorInstance with args");
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }
    
    <?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
       <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance">
            <constructor-arg index="0" name="name" value="test constructor with args"/>
        </bean>
    </beans>
    

    输出结果为:

    ConstructorInstance with args

    源码阅读

    下面这段是 有关spring bean生命周期的代码,也是我们本次要讨论的bean 实例化的入口。

    doCreateBean方法具体实现在AbstractAutowireCapableBeanFactory类,感兴趣的朋友可以进去看看调用链。

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
            //第一步 创建bean实例 还未进行属性填充和各种特性的初始化
            BeanWrapper instanceWrapper = null;
            if (instanceWrapper == null) {
                instanceWrapper = createBeanInstance(beanName, mbd, args);
            }
            final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
            Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
    
            Object exposedObject = bean;
            try {
                // 第二步 进行属性填充
                populateBean(beanName, mbd, instanceWrapper);
                if (exposedObject != null) {
                    // 第三步 初始化bean 执行初始化方法
                    exposedObject = initializeBean(beanName, exposedObject, mbd);
                }
            }catch (Throwable ex) {
                //  抛相应的异常
            }
    
            // Register bean as disposable.
            try {
                registerDisposableBeanIfNecessary(beanName, bean, mbd);
            }catch (BeanDefinitionValidationException ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
            }
            return exposedObject;
        }
    

    我们这里只需关注第一步创建bean实例的流程即可
    instanceWrapper = createBeanInstance(beanName, mbd, args);

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
            // Make sure bean class is actually resolved at this point.
            Class<?> beanClass = resolveBeanClass(mbd, beanName);
            // 使用工厂方法进行实例化
            if (mbd.getFactoryMethodName() != null)  {
                return instantiateUsingFactoryMethod(beanName, mbd, args);
            }
            // Need to determine the constructor...
            Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
            // 使用带参构造函数初始化
            if (ctors != null ||
                    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
    
                return autowireConstructor(beanName, mbd, ctors, args);
            }
    
            // 默认实例化方式 无参构造实例化
            return instantiateBean(beanName, mbd);
        }
    

    上面代码就是spring 实现bean实例创建的核心代码。这一步主要根据BeanDefinition里的元数据定义决定使用哪种实例化方法,主要有下面三种:

    • instantiateUsingFactoryMethod 工厂方法实例化的具体实现
    • autowireConstructor 有参构造函数实例化的具体实现
    • instantiateBean 默认实例化具体实现(无参构造函数)

    实例化策略(cglib or 反射)

    工厂方法的实例化手段没有选择策略直接用了发射实现的
    实例化策略都是对于构造函数实例化而言的

    上面说到的两构造函数实例化方法不管是哪一种都会选一个实例化策略进行,到底选哪一种策略也是根据BeanDefinition里的定义决定的。

    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
    

    上面这一行代码就是选择实例化策略的代码,进入到上面两种方法的实现之后发现都有这段代码。

    下面选一个instantiateBean的实现来介绍

    protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
            try {
                Object beanInstance;
                final BeanFactory parent = this;
                if (System.getSecurityManager() != null) {
                    beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        @Override
                        public Object run() {
                            return getInstantiationStrategy().instantiate(mbd, beanName, parent);
                        }
                    }, getAccessControlContext());
                }
                else {
                    // 在这里选择一种策略进行实例化
                    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
                }
                BeanWrapper bw = new BeanWrapperImpl(beanInstance);
                initBeanWrapper(bw);
                return bw;
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
            }
        }
    

    选择使用反射还是cglib

    先判断如果beanDefinition.getMethodOverrides()为空也就是用户没有使用replace或者lookup的配置方法,那么直接使用反射的方式,简单快捷,但是如果使用了这两个特性,在直接使用反射的方式创建实例就不妥了,因为需要将这两个配置提供的功能切入进去,所以就必须要使用动态代理的方式将包含两个特性所对应的逻辑的拦截增强器设置进去,这样才可以保证在调用方法的时候会被相应的拦截器增强,返回值为包含拦截器的代理实例。---引用自《spring 源码深度剖析》这本书

       <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance" >
            <lookup-method name="getName" bean="xxx"/>
            <replaced-method name="getName" replacer="yyy"/>
        </bean>
    

    如果使用了lookup或者replaced的配置的话会使用cglib,否则直接使用反射。
    具体lookup-methodreplaced-method的用法可以查阅相关资料。

        public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
            // Don't override the class with CGLIB if no overrides.
            if (bd.getMethodOverrides().isEmpty()) {
                constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);
                return BeanUtils.instantiateClass(constructorToUse);
            }else {
                // Must generate CGLIB subclass.
                return instantiateWithMethodInjection(bd, beanName, owner);
            }
        }
    

    由于篇幅省略了部分代码

  • 相关阅读:
    数据切分——Atlas介绍
    HDU 5015 233Matrix (构造矩阵)
    Wincc操作数据库SQLSERVER
    UIWebView 设置背景为透明
    29个你必须知道的Linux命令
    【读书笔记】iOS-UIWindow-WindowLevel
    linux下uart应用编程
    Java Web HelloWorld!
    手把手图文教你eclipse下如何配置tomcat
    Tomcat安装及配置教程
  • 原文地址:https://www.cnblogs.com/jay763190097/p/10982365.html
Copyright © 2020-2023  润新知