Spring源码学习(二)哎呦,按菜谱做菜与AbstractAutowireCapableBeanFactory.createBean流程差不多
记得跟老婆谈恋爱时,有一天心血来潮给老婆做饭,按照菜谱一步一步的做,结果差点把厨房烧了!!! 这事至今老婆还记得。
入口
上一篇说了,AbstractBeanFactory.getBean的主流程 ,今天来说下其中的createBean方法,程序入口如下:
/**. * 这个类的核心方法,创建一个bean实例, 填充bean实例,执行后处理等 * @see #doCreateBean 详见doCreateBean */ @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { //..... }
根据注释核心逻辑在doCreateBean 中,下面咱们就聊聊doCreateBean.
doCreateBean 逻辑说明
主流程 | 类比按菜谱做菜 |
---|---|
1. 实例化Bean BeanDefinition->BeanWrapper(如果是单例,先尝试从缓存中清楚并获取BeanWrapper) | 找到菜谱,先尝试从收藏中获取 |
2.处理MergedBeanDefinitionPostProcessors | 融合老婆的要求,到菜谱中。例如:少放盐 |
3.允许提前暴露ObjectFactory,解决循环引用问题(必须满足:单例&&允许循环引用&&对应的bean在创建) | 提前告诉老婆菜的大概味道,方便美丽的她准备饮料,也可以方便自己提前找出盘子 |
4.填充属性 | 炒菜 |
5.执行 init-method方法 | 试吃 |
6.有其他bean依赖当前完整bean(必须填充完属性),移除这些bean,有不能移除的抛出异常 | 发现之前准备的盘子太小了,换个新的。 |
7.注册DisposableBean接口或destroy—method | 做个好男人,吃完饭记得刷碗 |
源码注释在最后!!
一图胜千言(时序图)
补充说明
1.实例化Bean BeanDefinition->BeanWrapper流程
BeanDerfinition已经上篇已经做过介绍(上一篇blog的传送门),下面说下BeanWrapper。 org.springframework.beans,BeanWrapper的注释翻译如下:
BeanWrapper是Spring中底层javaBean的核心接口 通常不直接使用,而是被BeanFactory和DataBinder使用 它提供对标准javabean的分析和操作的功能。 可以获取,设置属性值,获取属性值的descriptors,用于查询可读,可写的属性。 支持无限层嵌套属性。 BeanWrapper默认不支持对属性的旧值进行编辑,这样可以避免getter被调用时产生的副作用 设置 extractOldValueForEditor 为true,可以开启对旧属性值进行编辑。
知道了BeanWrapper就不难猜出BeanDefinition->BeanWrapper的大致逻辑了
BeanDefinition->BeanWrapper 就是将bean的元数据,转换为Bean,但是Bean没有对属性进行赋值。
-- 温安适 胡乱总结于20190921
createBeanInstance的源码:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // 确认 bean的class,在这时已被解析了 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); } // 判断 是否重新创建同一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, null, null); } else { return instantiateBean(beanName, mbd); } } // 自动注入的构造器的,候选者 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 获得首选的构造器,可能是默认构造器 ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // 没有特别处理简单实用,无参构造器 return instantiateBean(beanName, mbd); }
我们可以看到,这个createBeanInstance ,有3种处理方式
- instantiateUsingFactoryMethod 按照工厂方法进行实例化
- autowireConstructor 构造函数注入, 构造器选择大致逻辑:
- 如果仅有默认构造器,没有指定参数,BeanDefinition中也没有构造器参数,使用默认构造方法。
- 如果有多个构造函数,对多个构造方法按照public优于private,同可见性的情况下,参数多的优于参数少的进行排序。一般采用宽松匹配模式(可以设置为严格匹配),优先选择参数原值类型匹配的,参数转换后类型匹配的次之,最后是都不匹配的。如果出现匹配度一致性的,选择第一个匹配的构造器。
- instantiateBean 实例化bean,使用默认构造方法。
咱们看下instantiateBean 的逻辑
一般按照反射生成实例的方式如下方式:
public static void main(String[] args) throws Exception{ Class<CouponApplicationTests> clazz=CouponApplicationTests.class; clazz.newInstance(); Constructor constructorToUse = clazz.getDeclaredConstructor(); constructorToUse.newInstance() }
Spring是如何实现的基本类似。查阅instantiateBean的源码发现,其核心逻辑委托给InstantiationStrategy的instantiate方法。Spring5.X的默认InstantiationStrategy 是CglibSubclassingInstantiationStrategy,而instantiate的实现并不在CglibSubclassingInstantiationStrategy中而是在,其父类SimpleInstantiationStrategy中。如下:
@Override public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. // 如果没有override方法, 即没有使用CGLIB重写 if (!bd.hasMethodOverrides()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged( (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor); } else { constructorToUse = clazz.getDeclaredConstructor(); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } //就是执行constructorToUse.newInstance() return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(bd, beanName, owner); } }
如果没有MethodOverride,其大致逻辑,跟咱们写的差不多。 但是如果有MethodOverride则不同。
看到这里的时候我很好奇hasMethodOverrides和instantiateWithMethodInjection,看名称我猜测是对@Override注解进行处理,但是看了源码这个MethodOverrides是指replace-method和lookup-method,这个2配置,我没有使用过,查询了源码与网上的blog。简单说就是在这里利用Cglib实现方法注入。 对lookup-method和replace-method的说明您可以查阅
2.循环引用是什么?Spring 如何解决的?
什么是循环引用,举个列子:BeanA引用BeanB,BeanB也引用BeanA
@Component class BeanA{ @Autowired BeanB beanB; } @Component class BeanB{ @Autowired BeanA beanA; }
Spring只能解决单例类型的循环,其解决帮扶就是提前暴露ObjectFactory,将未填充完属性的bean提前暴露出来。 流程图如下:
啰嗦几句
写blog比看源码要费劲的多,我总是尝试使用生活中的例子比喻主流程,再加一点的补充说明。 以做菜比喻Spring源码,感觉自己源码学虽然不透彻,但是胃口好多了。
做菜喻源码
轮廓渐清晰
理解未透彻
肚子叫呱呱
美餐一顿去
下回咱再聊
populateBean下一篇解决 预计10月2号前
源码注释
/** * Actually create the specified bean. Pre-creation processing has already happened * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks. * <p>Differentiates between default bean instantiation, use of a * factory method, and autowiring a constructor. * 创建指定的Bean,此时创建前预处理已经执行了(查看postProcessBeforeInstantiation)。 * 区分默认bean实例化,工厂方法,并自动注入构造函数。 * @param beanName the name of the bean * @param mbd the merged bean definition for the bean * @param args explicit arguments to use for constructor or factory method invocation * @return a new instance of the bean * @throws BeanCreationException if the bean could not be created * @see #instantiateBean * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */ protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. // 1.实例化bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { //Cache of unfinished FactoryBean instances 从FactoryBean name to BeanWrapper. //移除未完成的bean的缓存中的实例 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //创建Bean实例 将BeanDefinition替换成BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { //Bean if (!mbd.postProcessed) { try { //@AutoWired注解在这里,应用 //2.处理MergedBeanDefinitionPostProcessors applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. // 3.允许提前暴露ObjectFactory,解决循环引用问题 // 单例,允许循环引用,对应的bean在创建 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. // 实例化bean Object exposedObject = bean; try { //4.填充属性 populateBean(beanName, mbd, instanceWrapper); //5.执行 init-method方法 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } //6.有其他bean依赖当前完整bean(填充过属性),移除这些bean,有不能移除的抛出异常 if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { //不允许注入未被完全装载的bean,并且有其他Bean依赖当前这个Bean String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { //移除依赖于当前Bean的其他bean if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { //还有其他bean,依赖于当前Bean,未被移除 throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { //7.如果实现了 DisposableBean接口或者提供了destroy—method 在这里注册 registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
深入理解 Spring 事务原理
Spring事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行: 获取连接 Connection con = DriverManager.getConnection() 开启事务con.setAutoCommit(true/false); 执行CRUD 提交事务/回滚事务 con.commit() / con.rollback(); 关闭连接 conn.close(); 使用Spring的事务管理功能后,我们可以不再写步骤 2 和 4 的代码,而是由Spirng 自动完成。那么Spring是如何在我们书写的 CRUD 之前和之后开启事务和关闭事务的呢?解决这个问题,也就可以从整体上理解Spring的事务管理实现原理了。下面简单地介绍下,注解方式为例子 配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional标识。 spring 在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据...
- 下一篇
前端如何做测试驱动开发-vue版
最近和测试杠上了,写了的文章都和测试相关。当然,这里的「测试」并不是具体的某个角色,而是验证程序正确性的工作。曾经,前端如何 TDD 困扰了我很久,随着时间的推移,前端框架开始成熟,我对前端测试有了更深刻的理解,把我做前端 TDD 的方法分享给大家。 理论篇 测试驱动开发,英文全称 Test-Driven Development(简称 TDD),是由Kent Beck 先生在极限编程(XP)中倡导的开发方法。以其倡导先写测试程序,然后编码实现其功能得名。 TDD 能从技术的角度帮我们提高代码质量,使代码执行结果正确,容易理解。由于有测试的保障,开发者可以更放心的重构自己的代码。 当有变更时,测试同学最关心变更的影响范围,然而开发者也很难精准确定变更所带来的影响。虽然我们不追求测试覆盖率,但足够的测试覆盖总是能够给我们更多的信心,TDD 则是增加测试覆盖的唯一途径。 在面对一个完全没有思路的算法的时候,TDD 则变成了测试驱动设计(Test-Driven Design)。选一个最简单的用例,用最简单的代码通过测试。逐渐增加测试 Case、通过测试 、重构来驱动出设计。 TDD 的步骤 写...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS8编译安装MySQL8.0.19
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Hadoop3单机部署,实现最简伪集群
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- 2048小游戏-低调大师作品
- Windows10,CentOS7,CentOS8安装Nodejs环境