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

Java技术栈

www.javastack.cn

关注阅读更多优质文章

来源:小小木的博客

www.cnblogs.com/wyc1994666/p/10650480.html

本次主要想写spring bean的实例化相关的内容。创建spring bean 实例是spring 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 实例化的入口。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、盘点 6 个被淘汰的 Java 技术,曾经风光过!

2、Spring Boot 太狠了,一次发布 3 个版本!

3、Spring Boot Redis 实现分布式锁,真香!

4、Spring Boot 如何快速集成 Redis?

5、Java 14 祭出神器,Lombok 被干掉了?

6、Java 14 祭出增强版 switch,真香!!

7、Spring Boot 2.3 优雅关闭新姿势,真香!

8、Spring Boot 干掉了 Maven 拥抱 Gradle!

9、公司来了个新同事不会用 Lombok!

10、Spring Cloud 2020 版本重大变革!

扫码关注Java技术栈公众号阅读更多干货。

点击「阅读原文」获取面试题大全~

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值