Spring源码分析——Bean实例化策略

Spring源码分析——Bean实例化策略

本篇文章讨论Spring源码中BeanFactory和BeanDefinition的设计实现,分析Spring如何创建单例Bean和如何使用三级缓存解决循环依赖。

对应Github源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/02-bean-instance-strategy

引言

在上一节中我们提到了Bean创建的核心是AbstractAutowireCapableBeanFactory类,其中我们提到了doCreateBean是创建Bean实例:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
  ....
    beanInstance = this.doCreateBean(beanName, mbdToUse, args);
    ....
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
  ....
  instanceWrapper = this.createBeanInstance(beanName, mbd, args);
  ....
  this.addSingletonFactory(beanName, () -> {
      return this.getEarlyBeanReference(beanName, mbd, bean);
    });
  ....
}

从上面的代码中看出,创建Bean实例的核心是this.createBeanInstance()函数,接着看这个函数:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
  // Make sure bean class is actually resolved at this point.
  Class<?> beanClass = resolveBeanClass(mbd, beanName);

  if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
   throw new BeanCreationException(mbd.getResourceDescription(), beanName,
     "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
  }

  Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
  if (instanceSupplier != null) {
   return obtainFromSupplier(instanceSupplier, beanName);
  }

  if (mbd.getFactoryMethodName() != null) {
   return instantiateUsingFactoryMethod(beanName, mbd, args);
  }

  // Shortcut when re-creating the same bean...
  boolean resolved = false;
  boolean autowireNecessary = false;
  if (args == null) {
   synchronized (mbd.constructorArgumentLock) {
    if (mbd.resolvedConstructorOrFactoryMethod != null) {
     resolved = true;
     autowireNecessary = mbd.constructorArgumentsResolved;
    }
   }
  }
  if (resolved) {
   if (autowireNecessary) {
    return autowireConstructor(beanName, mbd, nullnull);
   }
   else {
    return instantiateBean(beanName, mbd);
   }
  }

  // Candidate constructors for autowiring?
  Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
  if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
   return autowireConstructor(beanName, mbd, ctors, args);
  }

  // Preferred constructors for default construction?
  ctors = mbd.getPreferredConstructors();
  if (ctors != null) {
   return autowireConstructor(beanName, mbd, ctors, null);
  }

  // No special handling: simply use no-arg constructor.
  return instantiateBean(beanName, mbd);
 }

上面其实核心主要涉及两种实例化方式:

  1. autowireConstructor:使用自动装配其构造函数实现实例化,例如使用@Autowire标记注入Bean
  2. instantiateBean:全面负责创建和初始化Bean实例,会选择合适的实例化策略等完成Bean初始化

注意:

Spring默认使用instantiateBean方式实例化Bean,当例如使用@Autowire方式注入bean将可能使用autowireConstructor方式注入bean;但是,这都不是决定性条件,Spring实例化一个Bean的时候会根据Bean的配置(构造函数、一些规则)等选择合适的方式去实例化Bean。

但是无论是使用哪种方式,都涉及一个调用:

 /**
  * Return the instantiation strategy to use for creating bean instances.
  */

 protected InstantiationStrategy getInstantiationStrategy() {
  return this.instantiationStrategy;
 }

选择一种合适的实例化策略(下面开始展开)。

扩展:Spring中装配一个Bean有多少种方式

  1. 构造函数注入;(注意Bean有默认的无参构造函数,但是使用如@AllArgsConstructor注解后则需要显示指定无参构造函数)
  2. Setter方法注入;
  3. 字段注入;(定义字段并使用如@Autowire注解注入)
  4. Java配置类;(使用@Configuration注解标记的配置类中,使用@Bean标记的函数将交由Spring装配)
  5. XML配置文件;(使用XML声明一个Bean)
  6. 自动装配;(使用例如@Autowire、@Qualifier、@Resource注解标记的字段或方法参数)

InstantiationStrategy

Spring源码分析——Bean实例化策略
img

在Spring源码中,主要有两种实例化策略:

  1. SimpleInstantiationStrategy:简单的Bean实例化方式,使用默认构造函数生成Bean实例
  2. CglibSubclassingInstantiationStrategy:使用Cglib实例化Bean(Spring默认方式),Cglib生成的bean实例是原始Bean的子类,Cglib会生成代理对象,用于AOP、事务处理等功能。

具体体现:

Spring源码分析——Bean实例化策略
image-20230419150409420

SimpleInstantiationStrategy

如下,SimpleInstantiationStrategy将使用构造函数创建Bean实例:

Spring源码分析——Bean实例化策略
image-20230419150646964
Spring源码分析——Bean实例化策略
image-20230419150948820

CglibSubclassingInstantiationStrategy

创建一个简单的测试类:

public class CglibTest {

 @Test
 public void test() {
  Enhancer enhancer = new Enhancer();
  enhancer.setSuperclass(CglibProxy.class);
  enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args));
  CglibProxy impl = (CglibProxy) enhancer.create();
  impl.say1();
  impl.say2();
 }
}
class CglibProxy {
 public void say1() {
  System.out.println("this CglibProxy say1()");
 }
 public void say2() {
  System.out.println("this CglibProxy say2()");
 }
}

打印结果:

my.CglibProxy$$EnhancerByCGLIB$$365af6e0@57f51dec
this CglibProxy say1()
this CglibProxy say2()

代理技术

Spring源码分析——Bean实例化策略
image-20230419163622091

核心: 都是通过代理类生成被代理类的对象引用,调用方通过调用代理类去调用具体的接口实现,代理类本身不提供任何服务,但是借助代理类可以实现AOP、事务等扩展功能。

  1. JDK动态代理:基于Java中的java.lang.reflect.Proxy类,通过反射机制再运行时动态的创建代理对象;它只能代理接口类型的目标对象,并且要求目标对象至少实现一个接口。
  2. Cglib动态代理:基于Cglib库,通过继承目标对象并重写其方法来创建代理对象;它可以代理任何类型的目标对象,并且不需要目标对象实现接口。

Spring源码专栏

此专栏将从Spring源码角度整体分析Spring设计思路以及常见的面试题

配套作者的手写Spring的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关Spring源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs

联系我

  • 个人博客:http://tycoding.cn/
  • GitHub:https://github.com/tycoding
  • 微信公众号:程序员涂陌
  • QQ交流群:866685601


原文始发于微信公众号(程序员涂陌):Spring源码分析——Bean实例化策略

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145621.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!