Spring BeanFactory getBean 源码剖析
首先看一张时序图
最开始,一切都来自这里:
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext3.xml"); context.getBean("newsBean"); }
如果我们是通过BeanFactory来构造IoC容器的话,那就是直接从上面的时序图的第五步开始
- public static void main(String[] args){
- Resource resource=new ClassPathResource("applicationContext2.xml");
- BeanFactory factory=new DefaultListableBeanFactory();
- BeanDefinitionReader bdr=new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);
- bdr.loadBeanDefinitions(resource);
- String url=((FXNewsBean) factory.getBean("newsBean2")).getUrl();
- System.out.println(url);
- }
当然,ApplicatonContext有缓存机制,在容器启动的时候,就加载了所有的bean,并缓存之,如果有了显式或隐式的调用,直接从缓存里拿就是了
********************
我们从上面的代码很容易追踪到AbstractBeanFactory的doGetBean方法
- @SuppressWarnings("unchecked")
- protected <T> T doGetBean(
- final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
- throws BeansException {
- //*************************1
- final String beanName = transformedBeanName(name);
- Object bean;
- // Eagerly check singleton cache for manually registered singletons.
- //*************************2
- Object sharedInstance = getSingleton(beanName);
- if (sharedInstance != null && args == null) {
- if (logger.isDebugEnabled()) {
- if (isSingletonCurrentlyInCreation(beanName)) {
- logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
- "' that is not fully initialized yet - a consequence of a circular reference");
- }
- else {
- logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
- }
- }
- //判断要获得的bean是否实现了FactoryBean,是获得工厂本身还是获得工厂生产的产品
- //如果要获得工厂本身 beanName就要以&开头
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
- }
- else {
- // Fail if we're already creating this bean instance:
- // We're assumably within a circular reference.
- //下面这个是关于循环依赖的处理
- if (isPrototypeCurrentlyInCreation(beanName)) {
- throw new BeanCurrentlyInCreationException(beanName);
- }
- // Check if bean definition exists in this factory.
- //如果存在父容器,且当前容器中不存在要获取的对象 那就去父容器去找
- BeanFactory parentBeanFactory = getParentBeanFactory();
- if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
- // Not found -> check parent.
- String nameToLookup = originalBeanName(name);
- if (args != null) {
- // Delegation to parent with explicit args.
- return (T) parentBeanFactory.getBean(nameToLookup, args);
- }
- else {
- // No args -> delegate to standard getBean method.
- return parentBeanFactory.getBean(nameToLookup, requiredType);
- }
- }
- //typeCheckOnly参数 调用是就是false
- //标明这个beanname已经被创建了
- //后面会用到
- if (!typeCheckOnly) {
- markBeanAsCreated(beanName);
- }
- //*************************3
- final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- checkMergedBeanDefinition(mbd, beanName, args);
- // Guarantee initialization of beans that the current bean depends on.
- //*************************4
- String[] dependsOn = mbd.getDependsOn();
- if (dependsOn != null) {
- for (String dependsOnBean : dependsOn) {
- getBean(dependsOnBean);
- registerDependentBean(dependsOnBean, beanName);
- }
- }
- // Create bean instance.
- //*************************5
- if (mbd.isSingleton()) {
- sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
- public Object getObject() throws BeansException {
- try {
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
- }
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
- else if (mbd.isPrototype()) {
- // It's a prototype -> create a new instance.
- Object prototypeInstance = null;
- try {
- beforePrototypeCreation(beanName);
- prototypeInstance = createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
- }
- else {
- String scopeName = mbd.getScope();
- final Scope scope = this.scopes.get(scopeName);
- if (scope == null) {
- throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
- }
- try {
- Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
- public Object getObject() throws BeansException {
- beforePrototypeCreation(beanName);
- try {
- return createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- }
- });
- bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
- }
- catch (IllegalStateException ex) {
- throw new BeanCreationException(beanName,
- "Scope '" + scopeName + "' is not active for the current thread; " +
- "consider defining a scoped proxy for this bean if you
"+ - "intend to refer to it from a singleton",
- ex);
- }
- }
- }
- // Check if required type matches the type of the actual bean instance.
- if (requiredType != null && bean != null &&
- !requiredType.isAssignableFrom(bean.getClass())) {
- try {
- return getTypeConverter().convertIfNecessary(bean, requiredType);
- }
- catch (TypeMismatchException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("Failed to convert bean '" + name +
- "' to required type [" +
- ClassUtils.getQualifiedName(requiredType) + "]", ex);
- }
- throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
- }
- }
- return (T) bean;
- }
代码1处,转化有两个步骤,首先处理beanName为&XXX的格式,这里表示要取指定name的factoryBean。在这里先把&符号取消,先获取bean再处理。然后,针对bean的alias机制,这里传入的参数可能是一个bean别名,那么我们先获取这个bean的主要id,只需要根据id值取bean就可以了。
代码2处,就是依次检查缓存。如果缓存里有,就直接拿出来。
spring的缓存有3种
之前已经获取过的bean
手动注入的bean
手动注入的ObjectFactory
第一种缓存好理解,看一下下面的代码,大家就知道手动注入bean与ObjectFctory是怎么回事了。
- public static void main(String[] args){
- Resource resource=new ClassPathResource("applicationContext2.xml");
- BeanFactory factory=new DefaultListableBeanFactory();
- BeanDefinitionReader bdr=new XmlBeanDefinitionReader(
- (BeanDefinitionRegistry) factory);
- bdr.loadBeanDefinitions(resource);
- FXNewsBean fx=new FXNewsBean();
- fx.setUrl("Thank glt");
- //同时还有一个方法叫addSingletonFactory
- ((DefaultSingletonBeanRegistry) factory).registerSingleton("newsBean2",fx);
- FXNewsBean news=(FXNewsBean) factory.getBean("newsBean2");
- System.out.println(news.getUrl()); //打印出Thank glt
- }
- final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- checkMergedBeanDefinition(mbd, beanName, args);
代码3处,麻烦的是是getMergedLocalBeanDefinition(beanName)。这个处理的主要是bean的present信息与scope信息。
说实话,这部分我没有看。
下面就是检查bean了。
首先,这个bean不能是abstract的。其次,如果getBean时还传递了要获取bean的参数(这里指构造方法的参)并且这个bean是singleton,那就得报错。
为什么?你想呀
factory.getBean("myBean","aaaa");
factory.getBean("myBean","bbbb");
如果myBean这个对象是singleton,上面的两行代码获得的对象能相等吗?
所以只能报错了。
- String[] dependsOn = mbd.getDependsOn();
- if (dependsOn != null) {
- for (String dependsOnBean : dependsOn) {
- getBean(dependsOnBean);
- registerDependentBean(dependsOnBean, beanName);
- }
- }
代码4处,如果BeanA中引用了BeanB,那么再getBean("BeanA")时就会先getBean("BeanB");
之后,注册依赖关系。
这个依赖关系有什么用呢?
似乎是在销毁类时用的,我不是很清楚。
代码5
如果要获取的bean是单例的
- if (mbd.isSingleton()) {
- sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
- public Object getObject() throws BeansException {
- try {
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
- }
- });
- }
注意上面的代码其实只有一行。
即调用getSingleton方法。
只不过getSingleton的第二个参数是一个匿名内部类。
这个内部类有一个getObject方法。,里面调用的是外部类的方法createBean。(外部类是谁?是AbstractBeanFactory)
我们先看getSingleton
- public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
- singletonObject = singletonFactory.getObject();
- addSingleton(beanName, singletonObject);//添加到已成功创建列表中 这是缓存
- return (singletonObject != NULL_OBJECT ? singletonObject : null);
- }
getSingleton省略了一些代码,主要是验证
getSingleton里面调用getObject就跑到了匿名类里面,最后的是AbstractBeanFactory的createBean方法。
不过AbstractBeanFactory中creatBean是abstract的。
实现在它的子类----AbstractAutowireCapableBeanFactory
- @Override
- protected Object createBean(final String beanName,
- final RootBeanDefinition mbd, final Object[] args)
- throws BeanCreationException {
- if (logger.isDebugEnabled()) {
- logger.debug("Creating instance of bean '" + beanName + "'");
- }
- // Make sure bean class is actually resolved at this point.
- //*************************5.1 保证RootBeanDefinition里面有beanclass
- resolveBeanClass(mbd, beanName);
- // Prepare method overrides.
- try {
- //*************************5.2 方法注入
- mbd.prepareMethodOverrides();
- }
- catch (BeanDefinitionValidationException ex) {
- throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
- beanName, "Validation of method overrides failed", ex);
- }
- try {
- // Give BeanPostProcessors a chance to return
- // a proxy instead of the target bean instance.
- //*************************5.3
- Object bean = resolveBeforeInstantiation(beanName, mbd);
- if (bean != null) {
- return bean;
- }
- }
- catch (Throwable ex) {
- throw new BeanCreationException(
- mbd.getResourceDescription(), beanName,
- "BeanPostProcessor before instantiation of bean failed", ex);
- }
- //*************************5.4
- Object beanInstance = doCreateBean(beanName, mbd, args);
- if (logger.isDebugEnabled()) {
- logger.debug("Finished creating instance of bean '" + beanName + "'");
- }
- return beanInstance;
- }
关于5.2处的代码,它处理的类似这样的bean。 更多信息可查看 http://blog.csdn.net/dlf123321/article/details/47862175
- <bean id="mockPersister" class="..impl.MockNewsPersister">
- <lookup-method name="getNewsBean" bean="newsBean"/>
- </bean>
关于5.3处的代码,得提到一个接口InstantiationAwareBeanPostProcessor,它本身也继承了BeanPostProcessor接口。
我们看到了在5.3代码的下面一旦resolveBeforeInstantiation的返回值不是null,那么直接就return。
resolveBeforeInstantiation类似于一个"短路器",执行了resolveBeforeInstantiation后(我是指getBean的对象实现了InstantiationAwareBeanPostProcessor接口,并且postProcessBeforeInstantiation方法的返回值不为null)下面的流程就不走了,直接返回bean。
这个方法一般情况下返回的都是null,通常情况下都是Spring容器内部使用这种特殊类型的BeanPostProcessor做一些动态对象代理等工作,我们使用普通的BeanPostProcessor实现就可以。这里简单提及一下,目的是让大家有所了解。
历尽千辛万苦,我们终于到了5.4了
让我歇会,下一节我们聊doCreatBean。
Spring BeanFacoty doCreateBean方法分析
参考资料
http://michael-softtech.iteye.com/blog/816469 spring源码分析之——spring bean的获取
http://www.iflym.com/index.php/code/201208290001.html Spring中获取一个bean的流程-1
<<spring揭秘>> 第四章 79页
版权声明:本文为博主原创文章,未经博主允许不得转载。
Spring BeanFactory getBean 源码剖析的更多相关文章
- Spring源码剖析依赖注入实现
Spring源码剖析——依赖注入实现原理 2016年08月06日 09:35:00 阅读数:31760 标签: spring源码bean依赖注入 更多 个人分类: Java 版权声明:本文为博主原 ...
- 转 Spring源码剖析——核心IOC容器原理
Spring源码剖析——核心IOC容器原理 2016年08月05日 15:06:16 阅读数:8312 标签: spring源码ioc编程bean 更多 个人分类: Java https://blog ...
- Spring 源码剖析IOC容器(一)概览
目录 一.容器概述 二.核心类源码解读 三.模拟容器获取Bean ======================= 一.容器概述 spring IOC控制反转,又称为DI依赖注入:大体是先初始化bean ...
- Spring源码剖析9:Spring事务源码剖析
转自:http://www.linkedkeeper.com/detail/blog.action?bid=1045 声明式事务使用 Spring事务是我们日常工作中经常使用的一项技术,Spring提 ...
- Spring源码剖析3:Spring IOC容器的加载过程
本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...
- Spring源码剖析2:初探Spring IOC核心流程
本文转载自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutor ...
- Spring源码剖析4:懒加载的单例Bean获取过程分析
本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...
- Spring IOC 容器源码分析
声明!非原创,本文出处 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 S ...
- Spring IOC 容器源码分析(转)
原文地址 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢 ...
随机推荐
- formData的实现
参考:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest <!doctype ...
- 【Java集合系列】---ArrayList
开篇前言--ArrayList中的基本方法 前面的博文中,小编主要简单介绍java集合的总体架构,在接下来的博文中,小编将详细介绍里面的各个类,通过demo.对比,来对java集合类进行更加深入的理解 ...
- [BBS]搭建开源论坛之Jforum搭配开源CKEDITOR
本文作者:sushengmiyan 本文地址:http://blog.csdn.net/sushengmiyan/article/details/47946065 使用默认的编辑器的时候,格式都无法保 ...
- 【SSH系列】Hibernate映射 -- 一对一单向关联映射
映射原理 一对一关联映射:两个实体对象之间是一对一的关联映射,即一个对象只能与另外唯一的一个对象相对应.有两种策略可以实现一对一的关联映射: a.主键关联:即让两个对象具有相 ...
- win10+ubuntu双系统安装方案
网上有很多教程,大多是win7,win8的,我折腾了一天,今天终于都安装好了,折腾的够呛,很多人都说挺简单的,嗯其实的确很简单,很多人回复说安装不成功,很有可能就是电脑安全权限的问题,我用的是华硕的电 ...
- 输入一个正数n,输出所有和为n连续正数序列。例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。
输入一个正数n,输出所有和为n连续正数序列.例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5.4-6和7-8. #define N 15 void findS ...
- ORACLE数据库管理常用查询语句
/*查看表空间的名称及大小*/ SELECT t.tablespace_name, round(SUM(bytes / (1024 * 1024)), 0) ts_size FROM dba_tabl ...
- Scikit-learn:模型选择Model selection
http://blog.csdn.net/pipisorry/article/details/52250983 选择合适的estimator 通常机器学习最难的一部分是选择合适的estimator,不 ...
- BeanUtils Exception 之 FastHashMap
这里仅仅是为了记录一件十分奇怪的事情,在使用BeanUtils的过程中,所有的依赖包都添加了, common logging common collections ··· 在为boolean 这种基本 ...
- EBS应付(AP)模块常用表
select * from ap_invoices_all INVOICE头 select * from ap_invoice_distributions_all INVOICE行 select ...