对于加载 bean 的功能,在 Spring 中的调用方式为 转换对应 beanName 这里传入的参数可能是别名,也可能是 FactoryBean ,所以需要进行一系列的 解析,这些解析内容包括如下内容 去除FactoryBean 的修饰符,也就是如果name=“&aa”,那么会首先去除&而使 name=“aa” 取指定 alias 所表示的最终 beanName,例如别名A 指向名称为B的 bean 则返回 B; 若别名A 指向别名B ,别名B又指向名称为C的 bean 则返回C 尝试从缓存中加载单例 单例在 Spring 的同一个容器内只会被创建一次,后续再获取 bean ,就直接从单例缓存中获 取了 当然这里也只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从 singletonFactories 中加载 因为在创建单例 bean 的时候会存在依赖注入的情况,而在创建依赖 的时候为了避免循环依赖,在 Spring 中创建 bean 的原则是不等 bean 创建完成就会将创建 bean 的ObjectFactory 提早曝光加入到缓存中, 一旦下一个 bean 创建时候需要依赖上一个 bean则直 接使用 ObjectFactory bean 的实例化 如果从缓存中得到了 bean 的原始状态,则需要对 bean 进行实例化 这里有必要强调一下, 缓存中记录的只是最原始的 bean 状态, 井不一定是我们最终想要的 bean 举个例子,假如我 们需要对工厂 bean 进行处理,那么这里得到的其实是工厂 bean 的初始状态,但是我们真正需 要的是工厂bean中定义的 factory-method 方法中返回的 bean ,而 getObjectForBeanlnstance就 是完成这个工作的 原型模式的依赖检查 只有在单例情况下才会尝试解决循环依赖,如果存在A中有B的属性, B中有A的属性, 那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建 A, 造成循环依赖,也就是情况: isPrototypeCurrentlyInCreation(beanName)判断 true 检测parentBeanFactory 从代码上看,如果缓存没有数据的话直接转到父类工厂上去加载了,这是为什么呢? 这里有一个很重要的判断条件: parentBeanFactory !=null && !containsBean Definition (beanName), parentBeanFactory != null parentBeanFactory 如果为空, 则其他一切都是浮云, 这个没什么说的,但是 !containsBeanDefinition(beanName)就比较重要了,它是在检测如果当前 加载的 XML 配置文件中不包含 beanName 所对应的配置,就只能到 parentBeanFactory 去尝试 下了,然后再去递归的调用 getBean 方法 将存储 XML 配置文件的 GernericBeanDefinition 转换为 RootBeanDefinition 因为从 XML 置文件中读取到的 bean 信息是存储在 GernericBeanDefinition 中的 ,但是所 有的 bean 后续处理都是针对于 RootBeanDefinition ,所以这里需要进行一个转换,转换的同时 如果父类 bean 不为空的话,则会一并合并父类的属性 寻找依赖 因为 bean 的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并 且配置成依赖于其他的 bean ,那么这个时候就有必要先加载依赖的 bean ,所以,在 Spring的 加载顺序中,在初始化某一个 bean 的时候首先会初始化这个 bean 所对应的依赖 针对不同的 scope 进行 bean 的创建 我们都知道,在 Spring 中存在着不同的 scope ,其巾默认的是 singleton ,但是还有些其他 的配置诸如 prototype request 之类的 在这个步骤中, Spring 会根据不同的配置进行不同的初 始化策略 类型转换 程序到这里返回 bean 后已经基本结束了,通常对该方法的调用参数 requiredType 是为空的, 但是可能会存在这样的情况,返回的 bean 其实是个 String ,但是 reqquiredType 却传人 Integer 类型,那么这时候本步骤就会起作用了,它的功能是将返回的 bean 转换为 requiredType 所指定 的类型 当然, String 转换为 Integer 是最简单的一种转换,在 Spring 中提供了各种各样的转换 器,用户也可以自己扩展转换器来满足需求
bean的获取过程
FactoryBean的使用 如果用FactoryBean的方式实现就会灵活一些 有了这个 CarFactoryBean 后,就可以在配置文件中使用下面这种自定义的配置方式配置 Car Bean 了 当调用 getBean(“car”)时, Spring 通过反射机制发现 CarFactoryBean 实现了 FactoryBean 的接口,这时 Spring 容器就调用接口方法 CarFactoryBean#getObject()方法返回 如果希望获取 CarFactoryBean 的实例,则需要在使用 getBean(beanName) 方法时在 beanName 前显示的加上 ”&”前缀,例如 getBean(”&car”)
缓存中获取单例 bean 因为在创建单例 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖, Spring 创建 bean 的原则是不等 bean 创建完成就会将创建 bean的ObjectFactory 提早曝光加入到 缓存中,一旦下一个 bean 创建时需要依赖上个 bean ,则直接使用 ObjectFactory 这个方法首先尝试从 singletonObjects 里面获取实例,如果获取不到再从 earlySingleton Objects 里面获取,如果还获取不到,再尝试从 singletonFactories 里面获取 beanName 对应的 ObjectFactory ,然后调用这个 ObjectFactory的getObject 来创建 bean ,并放到 earlySingletonObjects 里面去 ,并且从 singletonFacotories 里面 remove 掉这个 ObjectFactorγ ,而对于后续的所 有内存操作都只为了循环依赖检测时候使用,也就是在 allowEarlyReference 为true的情况下才 会使用 这里涉及用于存储 bean 的不同的 map singletonObjects :用于保存 BeaName 和创建 bean 实例之间的关系, bea name 一> bean instance singletonFactories :用于保存 BeanName 和创建 bean 的工厂之间的关系, bean name 一> ObjectFactory earlySingletonObjects :也是保存 BeanName 和创建 bean 实例之间的关系,与 singletonObjects 的不同之处在于,当一个单例 bean 被放到这里面后,那么当 bean 在创建过程中,就可以通过 getBean 方法获取到了,其目的是用来检测循环引用 registeredSingletons :用来保存当前所有已注册的 bean
从bean的实例中获取对象 无论是从缓存中获取到的 bean 还是通过不同的 scope 策略加载的 bean 都只是最原始的 bean 状态,并不一定是我们最终想要的 bean 。举个例子,假如我们需要对工厂 bean 进行处理,那 么这里得到的其实是工厂 bean 的初始状态,但是我们真正需要的是工厂 bean 中定义的 factory-method 方法中返回的 bean ,而 getObjectForBeanInstance 方法就是完成这个工作的 getObjectForBeanInstance 中所做的工作 对FactoryBean 正确性的验证 对非 FactoryBean 不做任何处理 对bean 进行转换 将从 Factory 中解析 bean 的工作委托给 getObjectFromFactoryBean 在这个方法里只做了一件事情, 就是返回的 bean 如果是单例的,那就必须要保证全局唯一 ,同时,也因为是单例的,所以不 必重复创建,可以使用缓存来提高性能,也就是说已经加载过就要记录下来以便于下次复用, 否则的话就直接获取了
在上面的方法中除了调用 object = factory.getObject() 得到我们想要的结果后并没有直接返回 而是接下来又做了些后处理的操作 在Spring获取bean的规则中有这样一条 尽可能保证所有bean初始化后都会调用注册的BeanPostProcessor的postProcessAfterInitialization方法进行处理 在实际开发中大可以针对此特性设计自己的业务逻辑
获取单例 Spring中是使用getSingleton的重载方法实现bean的加载过程 上述代码中其实是使用了回调方法, 使得程序可以在单例创建的前后做一些准备及处理操 作,而真正的获取单例 bean 的方法其实并不是在此方法中实现的, 其实现逻辑是在 ObjectFactory 类型的实例 singletonFactory 中实现的 而这些准备及处理操作包括如下内容 检查缓存是否已经加载过 若没有加载,则记录 beanName 的正在加载状态 加载单例前记录加载状态 beforeSingletonCreation这个函数中做了一个很重要的操作:记录加载状态,也就是通过 this.singletonsCurrentlylnCeation.add(beanName)将当前正要创建的 bean 记录在缓存中,这样便可以对循环依赖进行 检测 通过调用参数传入的 ObjectFactory 的个体 Object 方法实例化 bean 加载单例后的处理方法调用 当bean加载结束后需要移除缓存中对该 bean 的正在加载状态的记录 将结果记录至缓存并删除加载 bean 过程中所记录的各种辅助状态 返回处理结果 bean 的加载逻辑其实是在传入的 ObjectFactory 类型的参数 singletonFactory 中定义的,我们反推参数的获取,得到如下代码 ObjectFactory 的核心部分其实只是调用了 createBean 的方法
准备创建 bean 一个真正干活的函数其实是以 do 开头的, 比如 doGetObjectFromFactoryBean ;而给我们错觉的函数,比如 getObjectFromFactoryBean ,其 实只是从全局角度去做些统筹的工作 根据设置的class属性或者根据className来解析Class 对override属性进行标记及验证 应用初始化前的后处理器 解析指定bean是否存在初始化前的短路操作 创建bean
处理override属性 在 Spring 配置中存在 lookup-method 和 replace-method 两个配置功能,而这两个配置的加载其实就是将配置 统一存放在 BeanDefinition 中的 methodOverrides 属性里,这两个功能实现原理其实是在 bean实 例化的时候如果检测到存在 methodOverrides 属性,会动态地为当前 bean 生成代理并使用对应 的拦截器为 bean 做增强处理
实例化的前置处理 在真正调用 doCreate 方法创建 bean 的实例前使用了这样一个方法 resolveBeforelnstantiation (beanName, mbd) 对BeanDefinigiton 中的属性做些前置处理。当然,无论其中是否有相应的逻 辑实现我们都可以理解,因为真正逻辑实现前后留有处理函数也是可扩展的一种体现,但是, 这并不是最重要的,在函数中还提供了一个短路判断,这才是最为关键的部分 当经过前置处理后返回的结果如果不为空,那么会直接略过后续的 bean 的创建而直接返 回结果 这一特性虽然很容易被忽略,但是却起着至关重要的作用,我们熟知的 AOP 功能就 是基于这里的判断的
实例化前的后处理器应用 在 bean 的实例化前会调用后处理器的方法进行处理
实例化后的后处理器应用
循环依赖
什么是循环依赖 循环依赖就是循环引用,就是两个或多个 bean 相互之间的持有对方,比如 CircleA 引用 CircleB , CircleB 引用 CircleC, CircleC 引用 CircleA ,则它们最终反映为一个环、此处不是循 环调用,循环调用是方法之间的环调用, 循环调用是无法解决的 除非有终结条件 否则就是死循环 ,最终导致内存溢出错误
Spring 如何解决循环依赖 Spring 容器循环依赖包括构造器循环依赖和 stter 循环依赖
构造器循环依赖 表示通过构造器注入构成的循环依赖, 此依赖是无法解决的 ,只能抛出 BeanCurrentlylnCreationException 异常表示循环依赖 如在创建 TestA 类时, 构造器需要 TestB 类,那将去创建 TestB 在创建TestB 类时又发现 需要 TestC 类,则又去创建 TestC 最终在创建 TestC 时发现又需要 TestA ,从而形成一个环, 没办法创建 Spring 容器将每一个正在创建的 bean 标识符放在一个“当前创建 bean 池”中 bean 标识 符在创建过程中将一直保持在这个池中,因此如果在创建 bean 过程中发现自己已经在“当前 创建bean 池” 里时,将抛出 BeanCurrentlylnCreationException 异常表示循环依赖;而对于创建 完毕的 bean 将从“当前 bean 池”中清除掉 创建配置文件 创建测试用例 Spring 容器创建“testA” bean ,首先去“当前创建 bean 池” 查找是否当前 bean 正在 创建,如果没发现,则继续准备其需要的构造器参数“ testB ” 并将“testA”标识符 放到“当前创建 bean池”, Spring 容器创建“testB” bean 首先去“当前创建 bean 池”查找是否当前 bean 正在 创建,如果没发现,则继续准备其需要的构造器参数“testC” ,并将“testB ”标识符 放到“当前创建 bean池” Spring 容器创建“testC” bean 首先去“当前创建 bean池 ”查找是否当前 bean 正在 创建,如果没发现 ,则 继续准备其需要的构造器参数“testA ”,并将“testC ”标识符 放到 “当前创建 bean 池 到此为止 Spring 容器要去创建“testA” bean ,发现该 bean 标识符在“当前创建 bean 池”中,因为表示循环依赖,抛出 BeanCurrentlyInCreationException
setter循环依赖 表示通过 setter 注入方式构成的循环依赖 对于 setter 注入造成的依赖是通过 Spring 容器 提前暴露刚完成构造器注入但未完成其他步骤(如 setter 注入)的 bean 来完成的,而且只能解 决单例作用域的 bean 循环依赖 通过提前暴露一个单例工厂方法,从而使其他 bean 能引用到 该bean Spring 容器创建单例“testA” bean ,首先根据无参构造器创建 bean ,并暴露一个 “ObjectFactory ”用于返回一个提前暴露一个创建中的bean ,并将“testA ”标识符放到“当前 创建bean 池”, 然后进行 setter 注入“testB” Spring 容器创建单例 “testB” bean ,首先根据无参构造器创建 bean ,并暴露一个 “ObjectFactory ”用于返回一个提前暴露一个创建中的 bean ,并将“testB ”标识符放到“当前 创建bean 池”,然后进行 setter 注入“circle” Spring 容器创建单例“testC” bean ,首先根据无参构造器创建 bean ,并暴露一个 “ObjectFactory ”用于返回一个提前暴露一个创建中的 bean ,并将“testC ”标识符放到“当前 创建“bean 池”,然后进行 setter 注入“testA” 进行注入“testA ”时由于提前暴露了“ObjectFactory” 工厂,从而使用它返回提前暴露一个创建中的 bean 最后在依赖注入 “testB ”和“testA ”,完成 setter 注入
prototype 范围的依赖处理 对于“prototype ”作用域 bean, Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓 存“prototype ”作用域的 bean ,因此无法提前暴露一个创建中的 bean 创建配置文件 创建测试用例 对于“singleton” 作用域 bean ,可以通过“setAllowCircularReferences(false);”来禁用循环 引用
创建bean 当经历过resolveBeforelnstantiation 方法后,程序有两个选择 ,如果创建了代理或者说重写了 InstantiationAwareBeanPostProcessor的postProcessBeforelnstantiation 方法并在方法 postProcessBeforelnstantiation 中改变了 bean 则直接返回就可以了 否则需要进行常规 bean 的创建 而这常规 bean 的创建就是在 doCreateBean 中完成的 如果是单例则需要首先清除缓存 实例化 bean ,将 BeanDefinition 转换为 BeanWrapper 转换是一个复杂的过程,但是我们可以尝试概括大致的功能,如下所示 如果存在工厂方法则使用工厂方法进行初始化 一个类有多个构造函数,每个构造函数都有不同的参数,所以需要根据参数锁定构造 函数并进行初始化 如果既不存在工厂方法也不存在带有参数的构造函数,则使用默认的构造函数进行 bean 的实例化 MergedBeanDefinitionPostProcessor 的应用 bean 合并后的处理, Autowired 注解正是通过此方法实现诸如类型的预解析 依赖处理 在Spring中会有循环依赖的情况,例如,当A中含有B的属性,而B中又含有A的属性 时就会构成一个循环依赖,此时如果A和B都是单例,那么在 Spring 中的处理方式就是当创 建B的时候,涉及自动注入A的步骤时,并不是直接去再次创建A ,而是通过放入缓存中的 ObjectFactory 来创建实例,这样就解决了循环依赖的问题 属性填充 将所有属性填充至 bean 的实例中 循环依赖检查 之前有提到过,在 Spring 中解决循环依赖只对单例有效,而对于 prototype的bean, Spring 没有好的解决办法,唯一要做的就是抛出异常 在这个步骤里面会检测已经加载的 bean 是否 已经出现了依赖循环,并判断是否需要抛出异常 注册 DisposableBean 如果配置了 destroy-method ,这里需要注册以便于在销毁时候调用 完成创建井返回
创建 bean 的实例 如果在 RootBeanDefinition 中存在 factoryMethodName 属性,或者说在配置文件中配置 了factory-method ,那么 Spring 会尝试使用 instantiateUsingFactoryMethod(beanName,mbd,args)方法 根据 RootBeanDefinition 中的配置生成 bean 的实例 解析构造函数并进行构造函数的实例化 因为一个 bean 对应的类中可能会有多个构造 函数,而每个构造函数的参数不同,Spring 在根据参数及类型去判断最终会使用哪个构造函数 进行实例化 但是,判断的过程是个比较消耗性能的步骤,所以采用缓存机制,如果已经解析过则 不需要重复解析而是直接从 RootBeanDefinition 中的属性 resoIvedConstructorOrFactoryMethod 缓存 的值去取,否则需要再次解析, 并将解析的结果添加至 RootBeanDefinition 中的属性 resolvedConstructorOrFactoryMethod中
autowireConstructor 对于实例的创建 Spring 中分成了两种情况,一种是通用的实例化,另一种是带有参数的实例化 带有参数的实例化过程相当复杂,因为存在着不确定性,所以在判断对应参数上做了大量工作 构造函数参数的确定 根据 explicitArgs 参数判断 缓存中获取 配直文件获取 构造函数的确定 根据确定的构造函数转换对应的参数类型 构造函数不确定性的验证 根据实例化策略以及得到的构造函数及构造函数参数实例化 Bean
instantiateBean
实例化策略
记录创建 bean 的 ObjectFactory 变量 earlySingletonExposure 是否是单例 是否允许循环依赖 是否 对应的 bean 正在创建的条件的综合 当这3个条件都满足时会执行 addSingletonFactory 操作
处理循环依赖 这里最关键的是 ObjectFactory 的实现
属性注入 InstantiationAwareBeanPostProcessor 处理器的 postProcessAfterinstantiation 函数的应用 此函数可以控制程序是否继续进行属性填充 根据注人类型( byName/byType ),提取依赖的 bean ,并统一存入 PropertyValues中 应用InstantiationAwareBeanPostProcessor 处理器的 postProcessPropertyValues 方法 对属性 获取完毕填充前对属性的再次处理,典型应用是 RequiredAnnotationBeanPostProcessor 类中对 属性的验证 将所有PropertyValues 中的属性填充至 BeanWrapper 中
autowireByName
autowireByType 实现根据名称自动匹配的第一步就是寻找 bw 中需要依赖注入的属性,同样对于根据类型 自动匹配的实现来讲第一步也是寻找 bw 中需要依赖注入的属性,然后遍历这些属性并寻找类 型匹配的 bean 其中最复杂的就是寻找类型匹配的 bean 同时, Spring 中提供了对集合的类型 注入的支持,如使用注解的方式: Spring 将会把所有与 Test 匹配的类型找出来并注入到 tests 属性中,正是由于这一因素, 所以在 autowireByType 函数中,新建了局部遍历 autowiredBeanNames ,用于存储所有依赖的 bean ,如果只是对非集合类的属性注入来说,此属性并无用处 对于寻找类型匹配的逻辑实现封装在了 resolveDependency 函数中
applyPropertyValues
初始化 bean 在 bean 配置时 bean 中有一个 init-method 的属性,这个属性的作用是在 bean 实例化前调用 init-method 指定的方法来根据用户业务进行相应的实例化
激活 Aware 方法 Spring 中提供一些 Aware 相关接口,比如 BeanFactoryAware ApplicationContextAware ResourceLoaderAware ServletContextAware 等,实现这 些Aware 接口的 bean 在被初始之后,可以取得一些相对应的资源,例如实现 BeanFactoryAware的bean 在初始后, Spring 容器将会注入 BeanFactory 的实例,而实现 ApplicationContextAware 的 bean ,在 bean 被初始后,将会被注入 ApplicationContext 的实例等
定义普通 bean 定义BeanFactoryAware 类型的 bean 使用 main 方法测试 运行测试类,控制台输出 hello 按照上面的方法我们可以获取到 Spring 中 BeanFactory 并且可以根据 BeanFactory 获取 所有 bean 以及进行相关设置
处理器的应用 BeanPostProcessor的 使用位置就是这里,在调用客户自定义初始化方法前以及调用自定义初始化方法后分别会调用 BeanPostProcessor的postProcessBeforelnitialization和postProcessAfterlnitia li zation 方法 ,使用 户可以根据自己的业务需求进行响应的处理
激活自定义的 init 方法 客户定制的初始化方法除了我们熟知的使用配置 init-method 外,还有使自定义的 bean实现 InitializingBean 接口,并在 afterPropertiesSet 中实现自己 的初始化业务逻辑 init-method与afterPropertiesSet 都是在初始化 bean 时执行,执行顺序是 afterPropertiesSet 先执行,而 init-method 后执行。 在invokelnitMethods 方法中就实现了这两个步骤的初始化方法调用
注册 DisposableBean Spring 中不但提供了对于初始化方法的扩展入口 同样也提供了销毁方法的扩展入口,对 于销毁方法的扩展,除了我们熟知的配置属性 destroy -method 方法外 ,用户还可以注册后处理 器DestructionAwareBeanPostProcessor 来统一处理 bean 的销毁方法
|