Spring---Bean实例化过程(源码)分析

本文深入分析Spring容器中Bean的实例化过程,包括Bean的获取、创建、依赖注入和初始化。从doGetBean方法入口,探讨了Bean的缓存、工厂Bean的处理、BeanDefinitionMap的作用,并详细解释了如何通过反射和BeanPostProcessor进行对象创建和依赖注入。
摘要由CSDN通过智能技术生成

Spring---组件扫描一文中,我们了解到Spring在容器启动时,是如何扫描到带有@Component注解的类并将其添加到beanDefinitionMap之中的。存在于beanDefinitionMap之中的只是所有bean的定义,这些定义包括“是否单例”,“是否懒初始化”,“类元数据信息”等等。当我们需要从容器中获取一个bean时,便会根据该bean定义信息,实例化bean对象,接下来我们看看整个实现过程。

一、过程分析

我们会从源码角度分析,这里假设我们有:

组件类Task和TimerTask

@Component
public class Task {
    public void run() {
    }
}

@Component
public class TaskTimer {
    @Autowired
    Task task;

    public void task() {
        task.run();
    }
}

工厂bean类

@Component
public class TimerFactoryBean implements SmartFactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new TaskTimer();
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
    @Override
    public boolean isEagerInit() { return false; }
}

我们将通过这两个类分析以下两个问题:

  1. TaskTimer是如何实例化过程
  2. Task类是如何被注入到TimerTask类中的

1.1Bean实例化入口

首先一切通过名称获取Bean的入口都是AbstractBeanFactory.doGetBean方法,无论你通过ApplicationContext还是BeanFactory,

接下来我们看看该方法干了什么(请自行打开源码跟着以下步骤分析):

1.1.1bean名称转换

在Spring容器中组件分为两种:

1.普通组件

即普通Bean,如上面的Task和TimerTask,我们直接可以通过名称从容器中获取到该Bean。

TimerTask timerTask = (TimerTask) context.getBean("timerTask");

2.工厂组件

即FactoryBean,如上面的TimerFactoryBean,这个Bean我们是无法通过Bean名称直接获取的。如下:

TimerFactoryBean timerFactoryBean = (TimerFactoryBean) context.getBean("timerFactoryBean");

如果你按照上述方式获取该Bean,将抛出如下异常:

Exception in thread "main" java.lang.ClassCastException: net.xz.go.spring.task.TaskTimer cannot be cast to net.xz.go.spring.TimerFactoryBean
	at net.xz.go.BootApplication.main(BootApplication.java:29)

也就是直接通过工厂bean的名称获取到的Bean是该工厂生成的Bean,而不是工厂Bean本身,那么我们可以通过如下方式获取工厂Bean本身:

TimerFactoryBean timerFactoryBean = (TimerFactoryBean) context.getBean("&timerFactoryBean");

3.名称转换

//org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
public static String transformedBeanName(String name) {
	Assert.notNull(name, "'name' must not be null");
	String beanName = name;
    //如果Bean名称以“&”开头,则去掉“&”符号
	while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
		beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
	}
	return beanName;
}

1.1.2先判断是否可以直接从缓存中获取Bean

这里需要注意一点,缓存指的是单例模式的Bean缓存,因为如果是原型模式,是不能直接从缓存获取的。

1.从缓存中根据名称获取缓存对象

//org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

//该方法只是从以下缓存中获取对象
Object sharedInstance = getSingleton(beanName);

//缓存
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

2.如果缓存对象不为空

//org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected Object getObjectForBeanInstance(
	Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

	// Don't let calling code try to dereference the factory if the bean isn't a factory.
    //如果name以“&”开头,则实例对象必须是FactoryBean类型
	if (BeanFactoryUtils.isFactoryDereference(name)) {
		if (beanInstance instanceof NullBean) {
			return beanInstance;
		}
		if (!(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
		}
	}

	// Now we have the bean instance, which may be a normal bean or a FactoryBean.
	// If it's a FactoryBean, we use it to create a bean instance, unless the
	// caller actually wants a reference to the factory.
    //1.如果name以“&”开头,且实例对象确实是FactoryBean类型,直接返回FactoryBean实例࿰
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值