在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; }
}
我们将通过这两个类分析以下两个问题:
- TaskTimer是如何实例化过程
- 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实例