每天用 Spring,bean 实例化原理你懂吗?

来源:小小木的博客
www.cnblogs.com/wyc1994666/p/10650480.html

本次主要想写spring bean的实例化相关的内容。建立spring bean 实例是spring bean 生命周期的第一阶段。html

bean 的生命周期主要有以下几个步骤:java

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

在实例化bean以前在BeanDefinition里头已经有了全部须要实例化时用到的元数据,接下来spring 只须要选择合适的实例化方法以及策略便可。实例化方法有两大类分别是工厂方法和构造方法实例化,后者是最多见的。面试

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 FactoryInstanceapp

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

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 argside

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 实例化的入口。bean 为何默认单例?推荐看下。关注Java技术栈公众号在后台回复Spring阅读更多Spring系列教程。

doCreateBean方法具体实如今doCreateBeanAbstractAutowireCapableBeanFactory类,感兴趣的朋友能够进去看看调用链。

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

因为篇幅省略了部分代码。

推荐去个人博客阅读更多:

1.Java JVM、集合、多线程、新特性系列教程

2.Spring MVC、Spring Boot、Spring Cloud 系列教程

3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程

4.Java、后端、架构、阿里巴巴等大厂最新面试题

以为不错,别忘了点赞+转发哦!