Spring Bean加载流程分析(通过 XML 方式加载)(12)
< beanDefinition.getRole()) {// 空实现} else if (!beanDefinition.equals(existingDefinition)) {// 空实现} else {// 空实现}// 重新放入到 concurrentHashMap 中this.beanDefinitionMap.put(beanName, beanDefinition);} else {if (hasBeanCreationStarted()) { //检查工厂 bean 的创建阶段是否已经开始了 , 创建阶段已经开始了synchronized (this.beanDefinitionMap) { //在这里对 beanDefinitionMap 这个 concurrentHashMap 做了同步处理 , 其实是为了防止并发的情况产生 , 导致 bean 没有注册上去 。// 将 beanDefinition 放置到缓存中去this.beanDefinitionMap.put(beanName, beanDefinition);// 定义一个 updated 集合List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);//将所有的 beanDefinitionNames 放到 updatedDefinitions 中updatedDefinitions.addAll(this.beanDefinitionNames);// 将要添加的 beanName 放置到 updatedDefinitionsupdatedDefinitions.add(beanName);// 重新给 beanDefinitionNames 赋值this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}} else {// 仍然在启动注册阶段this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}// 重置 BeanDefinition 的缓存if (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}}复
文章插图
到这里 , 我们所有的在 xml 中定义的 bean 对象都已经被解析出来了 , 所有的 bean 都被存放在 registry 中的 beanDefinitionMap 中 , 它是一个 concurrentHashMap , 它的 key 是 beanName , value 是关于该 bean 的全部定义 , 其中包含 className/class, scope, init-method ... 等等属性 。 至此整个 bean 的加载过程也就结束了 。 但是注意:此时我们的 bean 并没有被创建 。 那么该 bean 是在什么时候被创建的呢?
文章插图
Bean 的创建过程通过上面的过程 , 我们可以知道以上的三行代码 Spring 从 applicationContext.xml 文件中加载了 bean 的定义 , 并存放到了 beanDefinitionMap 中 , 此时我们的 bean 对象并没有被初始化 。
文章插图
下面来看看 defaultListableBeanFactory.getBean 方法 。 看看是如何实现的 。 点进去我们发现 Spring 的 BeanFactory 为我们提供了各种各样的 getBean 方法 。 但是他们的本质都是调用了 doGetBean 方法 。 我们直接去看 doGetBean 方法做了什么事情 。
文章插图
看看 doGetBean 的源码实现 (doGetBean 的源码非常多 , 因为源码太多的原因 , 这里删除了一些无用的日志逻辑)
protectedT doGetBean(final String name, @Nullable final Class requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {// 转换 bean 的 beanNamefinal String beanName = transformedBeanName(name);Object bean; //这里定义成了一个 Object 对象 , 因为 Spring 并不知道我们要获取对象的类型 , 直接使用了 Object 对象来接收// 检查已经注册在 Spring 容器中是否存在这样的 beanObject sharedInstance = getSingleton(beanName); //第一次访问 sharedInstance 为 null , 这里面判断了 scope = singleton 形式时是否会出现循环引用if (sharedInstance != null} else {if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 判断 beanDefinition 是否已经存在了 。 目前根据 parentBeanFactory 返回为 nullBeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != nullif (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);} else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);} else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);} else {return (T) parentBeanFactory.getBean(nameToLookup);}}// 标记当前对象为已经创建 , 实际上就是将该 beanName 添加到 Set 集合中if (!typeCheckOnly) {markBeanAsCreated(beanName);}try {// 将 BeanDefinitionMap 中的 beanDefinition 转换成 RootBeanDefinitionfinal RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// 获取合并后的 bean 的依赖信息String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);try {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// 创建 bean 的实例if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {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 name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {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);}}} catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.if (requiredType != nullif (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isTraceEnabled()) {logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}复制代码
推荐阅读
- FlinkSQL 动态加载 UDF 实现思路
- 基于Spring+Angular9+MySQL开发平台
- 网络比15年前更慢错误更多?开发者加载了100万个网站实测
- 别不拿GateWay当回事,SpringCloud告诉你错了
- web 安全之 Spring Security 入门教程
- Spring Application实例化流程和构造方法参数
- SpringBoot常用注解
- 阿里爆款SpringBoot项目实战PDF+源码+视频分享
- Firefox 83将默认启用Warp更新:大幅提升响应时间和加载速度
- SpringCloud下skywalking的快速入门