锁定老帖子 主题:Spring源代码解析(一):IOC容器
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-06-30
我们看看是用的wrapper类是怎样被创建的,这个对象被创建的时候已经为我们的bean创建了JAVA对象:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args) throws BeansException { BeanWrapper instanceWrapper = null; //这里使用BeanWrapper的不同创建方法 if (mergedBeanDefinition.getFactoryMethodName() != null) { instanceWrapper = instantiateUsingFactoryMethod(beanName, mergedBeanDefinition, args); } else if (mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mergedBeanDefinition.hasConstructorArgumentValues() ) { instanceWrapper = autowireConstructor(beanName, mergedBeanDefinition); } else { // No special handling: simply use no-arg constructor. // 这是最正常的创建,使用Spring默认的BeanWrapper实现BeanWrapperImp instanceWrapper = instantiateBean(beanName, mergedBeanDefinition); } return instanceWrapper; } protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mergedBeanDefinition) throws BeansException { //这里是创建bean对象的地方,同时把这个bean对象放到BeanWrapper中去 Object beanInstance = getInstantiationStrategy().instantiate(mergedBeanDefinition, beanName, this); BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } 我们注意到在这里定义的实例化的策略是 private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); 一般而言可以直接实例化也可以通过cglib来完成bean对象的重新实例化,在 CglibSubclassingInstantiationStrategy中: public Object instantiate( RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. // 这里是重新实例化bean对象的地方,返回后放到BeanWrapper对象当中去 if (beanDefinition.getMethodOverrides().isEmpty()) { return BeanUtils.instantiateClass(beanDefinition.getBeanClass()); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(beanDefinition, beanName, owner); } } 这里我们看到对bean的JAVA对象的创建过程,如果没有什么依赖关系的话,那主要的bean创建过程已经完成了,但是如果存在依赖关系的话,这些依赖关系还要进行注入, |
|
返回顶楼 | |
发表时间:2007-06-30
回到AbstractAutowireCapableBeanFactory中的populate方法,这里是处理bean的依赖注入的地方:
protected void populateBean(String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw) throws BeansException { //首先取得我们在loadBeanDefinition中取得的依赖定义propertyValues PropertyValues pvs = mergedBeanDefinition.getPropertyValues(); ...... checkDependencies(beanName, mergedBeanDefinition, filteredPds, pvs); //主要地依赖注入处理在这里 applyPropertyValues(beanName, mergedBeanDefinition, bw, pvs); } private void applyPropertyValues( String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw, PropertyValues pvs) throws BeansException { if (pvs == null) { return; } BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mergedBeanDefinition); // Create a deep copy, resolving any references for values. // 这里把那些相关的有依赖关系的property内容copy过来 MutablePropertyValues deepCopy = new MutablePropertyValues(); PropertyValue[] pvArray = pvs.getPropertyValues(); for (int i = 0; i < pvArray.length; i++) { PropertyValue pv = pvArray[i]; //这个队property的resolve过程包含了一个对依赖bean的迭代解析和创建 Object resolvedValue = valueResolver.resolveValueIfNecessary("bean property '" + pv.getName() + "'", pv.getValue()); deepCopy.addPropertyValue(pvArray[i].getName(), resolvedValue); } // 这里把copy过来的propertyValue置入到BeanWrapper中去,这个set其实并不简单,它通过wrapper完成了实际的依赖注入 try { // Synchronize if custom editors are registered. // Necessary because PropertyEditors are not thread-safe. if (!getCustomEditors().isEmpty()) { synchronized (this) { bw.setPropertyValues(deepCopy); } } else { bw.setPropertyValues(deepCopy); } } ......... } 这里有一个迭代的解析和bean依赖的创建,注入: Object resolvedValue = valueResolver.resolveValueIfNecessary("bean property '" + pv.getName() + "'", pv.getValue()); 在BeanDefinitionValueResolver中是这样实现这个resolve的: public Object resolveValueIfNecessary(String argName, Object value) throws BeansException { if (value instanceof BeanDefinitionHolder) { // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases. BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value; return resolveInnerBeanDefinition(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition()); } else if (value instanceof BeanDefinition) { // Resolve plain BeanDefinition, without contained name: use dummy name. BeanDefinition bd = (BeanDefinition) value; return resolveInnerBeanDefinition(argName, "(inner bean)", bd); } else if (value instanceof RuntimeBeanNameReference) { String ref = ((RuntimeBeanNameReference) value).getBeanName(); if (!this.beanFactory.containsBean(ref)) { throw new BeanDefinitionStoreException( "Invalid bean name '" + ref + "' in bean reference for " + argName); } return ref; } else if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; return resolveReference(argName, ref); } else if (value instanceof ManagedList) { // May need to resolve contained runtime references. return resolveManagedList(argName, (List) value); } else if (value instanceof ManagedSet) { // May need to resolve contained runtime references. return resolveManagedSet(argName, (Set) value); } else if (value instanceof ManagedMap) { // May need to resolve contained runtime references. return resolveManagedMap(argName, (Map) value); } else if (value instanceof ManagedProperties) { Properties copy = new Properties(); copy.putAll((Properties) value); return copy; } else if (value instanceof TypedStringValue) { // Convert value to target type here. TypedStringValue typedStringValue = (TypedStringValue) value; try { Class resolvedTargetType = resolveTargetType(typedStringValue); return this.beanFactory.doTypeConversionIfNecessary( this.typeConverter, typedStringValue.getValue(), resolvedTargetType, null); } catch (Throwable ex) { // Improve the message by showing the context. throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Error converting typed String value for " + argName, ex); } } else { // No need to resolve value... return value; } } 这里可以看到对各种依赖类型的resolve,我们看看怎样解析reference bean的,非常清楚的向IOC容器去请求 - 也许会触发下一层依赖的bean的创建和依赖注入过程: private Object resolveReference(String argName, RuntimeBeanReference ref) throws BeansException { .......... try { //向父工厂请求bean return this.beanFactory.getParentBeanFactory().getBean(ref.getBeanName()); } else { //向自己所在的工厂请求bean Object bean = this.beanFactory.getBean(ref.getBeanName()); if (this.beanDefinition.isSingleton()) { this.beanFactory.registerDependentBean(ref.getBeanName(), this.beanName); } return bean; } } ......... } 假设我们经过穷推,已经到最后一层的bean的依赖创建和注入,这个具体的注入过程,也就是依赖注入过程要依靠BeanWrapperImp的实现我们回到applyPropertyValues中来,这里是已经迭代对依赖进行完解析的地方,也就是需要对依赖进行注入的地方 - 注意这个token是在wrapper中已经对属性做过处理了: private void setPropertyValue(PropertyTokenHolder tokens, Object newValue) throws BeansException { String propertyName = tokens.canonicalName; if (tokens.keys != null) { // Apply indexes and map keys: fetch value for all keys but the last one. PropertyTokenHolder getterTokens = new PropertyTokenHolder(); getterTokens.canonicalName = tokens.canonicalName; getterTokens.actualName = tokens.actualName; getterTokens.keys = new String[tokens.keys.length - 1]; System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1); //这里取得需要的propertyValue, 这个getPropertyValue同样不简单,这个getProperty实际上已经取出了bean对象中的属性引用 //所以下面可以直接把依赖对象注入过去 Object propValue = null; try { propValue = getPropertyValue(getterTokens); } ........ // 如果根据token取不到propertyValue,直接抛出异常 String key = tokens.keys[tokens.keys.length - 1]; if (propValue == null) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + "in indexed property path '" + propertyName + "': returned null"); } //这里处理属性是List的注入 else if (propValue.getClass().isArray()) { ..... Array.set(propValue, Integer.parseInt(key), convertedValue); ....... } //这里处理对List的注入 else if (propValue instanceof List) { ........ List list = (List) propValue; ........ if (index < list.size()) { list.set(index, convertedValue); } else if (index >= list.size()) { for (int i = list.size(); i < index; i++) { try { list.add(null); } } list.add(convertedValue); ....... } //这里处理对Map的注入 else if (propValue instanceof Map) { ...... Map map = (Map) propValue; ...... map.put(convertedMapKey, convertedMapValue); } ........ } //这里是通过对一般属性进行注入的地方 else { PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName); if (pd == null || pd.getWriteMethod() == null) { PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass()); throw new NotWritablePropertyException( getRootClass(), this.nestedPath + propertyName, matches.buildErrorMessage(), matches.getPossibleMatches()); } //得到需要的set/get方法 Method readMethod = pd.getReadMethod(); Method writeMethod = pd.getWriteMethod(); Object oldValue = null; ........ try { Object convertedValue = this.typeConverterDelegate.convertIfNecessary(oldValue, newValue, pd); //千辛万苦,这里是通过set方法对bean对象的依赖属性进行注入 if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(this.object, new Object[] {convertedValue}); ........... } 这里比较重要的是propValue的取得,我们看看getPropertyValue的实现: private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException { .... //这里取得对property的读取方法,然后取得在bean对象中的属性引用 Method readMethod = pd.getReadMethod(); try { if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(this.object, (Object[]) null); if (tokens.keys != null) { ...... }//这里处理Array属性 else if (value.getClass().isArray()) { value = Array.get(value, Integer.parseInt(key)); }//这里处理List属性 else if (value instanceof List) { List list = (List) value; value = list.get(Integer.parseInt(key)); }//这里处理Set属性 else if (value instanceof Set) { // Apply index to Iterator in case of a Set. Set set = (Set) value; int index = Integer.parseInt(key); } ...... }//这里处理Map属性 else if (value instanceof Map) { Map map = (Map) value; Class mapKeyType = null; if (JdkVersion.isAtLeastJava15()) { mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType( pd.getReadMethod(), tokens.keys.length); } ..... Object convertedMapKey = this.typeConverterDelegate.convertIfNecessary( null, null, key, mapKeyType); // Pass full property name and old value in here, since we want full // conversion ability for map values. value = map.get(convertedMapKey); } ........ return value; } 这就是整个依赖注入的处理过程,在这个过程中起主要作用的是WrapperImp ,这个Wrapper不是一个简单的对bean对象的封装,因为它需要处理在beanDefinition中的信息来迭代的处理依赖注入。 从上面可以看到两个明显的迭代过程,一个是迭代的在上下文体系中查找需要的bean和创建没有被创建的bean - 根据依赖关系为线索,另一个迭代实在依赖注入的时候,如果依赖没有创建,因为是一个向容器取得bean的过程 - 其中的IOC工厂的getbean方法被迭代的调用,中间又迭代的对需要创建的bean进行了创建和依赖注入,这样根据依赖关系,一层一层的创建和注入直至顶层被要求的bean创建和依赖注入完成 - 这样最后得到一个依赖创建和注入完成的最顶层bean被用来交给客户程序使用。 |
|
返回顶楼 | |
发表时间:2007-07-01
最后来总结下吧,对于普通ioc容器启动到被客户端使用的步骤大致如下:
定义->定位->装载->注册->创建->注入 定义:类似application.xml资源文件,所有的bean定义信息都在这个文件中描述.且依赖于dtd/xsd的规则. 定位:对于资源文件信息的封装需要依赖Resource对象.当然这里的Resource仅仅存放了资源文件的简单信息,比如文件(名)和路径等等.所以这就需要ResourceLoader来用于对资源的辅助加载.其中以DefaultResourceLodaer为例,如果通过ClassPathResource方式来加载,就需要指定ClassLoader,这也就是为什么以classpath方式加载资源文件,通常将把资源文件放置于source path下而以file system方式加载则以工程相对路径.比如/WEB-INF/application.xml.这里值得说明的是ApplicationContext实现了ResourceLoader接口,因此他本身就是个资源解析器,通过构造方法就能直接加载资源文件.此外spring自带了常用的资源加载绑定类,当然也可以自己来实现这个Resource接口,比如通过网络获取2进制数据对象反序列化的方式. 装载:BeanDefinitonReader的loadBeanDefine方法用于对Resource的解析处理,另外还对资源文件定义的合法性进行校验.同大多资源文件解析相同,通过w3c开源框架对所有element做迭代处理.但是这里的DeanDefinitonReader只是对BeanDefinitonParserDelegate的套壳处理.也就是说真正对资源文件定义做每个标签和属性做读取解析的最终类,就是BeanDefinitonParserDelegate.因此从命名不难发现,对定义进行解析后最终得到的数据模型,就是BeanDefiniton对象.每个BeanDefiniton对象封装了对应bean定义的所有信息.这个有些类似于o-r(x) mapping实现思想.需要重点说明的是,这里仅仅作为bean信息个定义封装,而并没有产生任何与bean实例以及相关属性实例. 注册:这个过程比较简单,以DefaultListableBeanFactory为例,或常用的AbstractXmlApplicationContext及其实现的子类,都无非将装载时获取的所有BeanDefinition对象存放到beanDefinitionMap对象中,这个beanDefinitionMap是个标准的Map对象.当然作者处于对BeanDefinition的顺序检索处理,还特地定义了beanDefinitionNames,这个beanDefinitionNames是个List对象.这2个对象就是存放bean(定义)的场所,或许也是真正意义的容器概念.因为今后所有客户端对容器的请求发起,作为容器方都将从该Map中取得相关bean信息.而不再持久资源文件中做任何读取操作. 创建:当客户端对已经初始化的容器发起请求时,容器首先会以bean name为key从beanDefinitionMap中获取BeanDefiniton定义对象.接着对singleton bean进行预实例化的处理.随后会从BeanDefiniton定义对象中对当前定义的bean对象做实例化操作,这里调用了AbstractAutowireCapableBeanFactory的createBean方法,注意目前到这个层面仅仅对bean本身的实例化(以prototype为例也就相当于进行了new操作).这其中首先会对实例化方式进行判断,比如工厂模式或者原型模式(包括带参数构造),如遇到带参数的构造定义,则需要进步解析构造参数,这个解析过程类似与当前bean的属性注入的过程.在下文中将做详细介绍.最后当前给请求的bean通过反射来实例化对象. 注入:这个过程是所有容器工作步骤中最为复杂的部分.首先这里有个对依赖的检查过程.虽然通过调用getBean的方式处理,但仅仅为了做依赖检查而不返回被依赖的对象.这样的目的在于将校验提前进行,而不要等到对于开销较大的依赖注入时.接下来createBean开始调用populateBean方法.首先进行自动装配模式的处理,也就是说对BeanDefiniton中定义自动装配模式的属性进行调整.比如定义了AUTOWIRE_BY_NAME的属性会相应找到匹配该命名的bean.并且将该被检索到的bean实例值给property,当然这里也肯定通过getBean方法来获取这个需要自动装配的bean实例,并且在BeanWrapper中包装.其实下文要讲到的注入过程也在BeanWrapper中完成.说道这里,可以明显看出,1个普通bean(既1个bean,包含1个或多个简单的属性)的属性注入过程,也需要依赖BeanWrapper来完成.或者说当得到1个bean的所有property定义时,容器首先将根据property的不同类型,如prop,map,list,set当然还有ref(在BeanDefinitionValueResolver的resolveValueIfNecessary方法完成),进行不同方式的对象实例化,当得知了属性的类型后就可以通过该类中相关resolve方法来完成该属性需要注入的对象的实例化操作,比如1个prop类型的属性可能就通过简单的new操作外加setProperty操作来完成了.而之所以可以通过类型检查来进行判断,是因为在BeanDefiniton定义中已经对这些property value做了封装处理,这可能也是最好用于区分property类型的最佳方式.具体的封装过程可以参考BeanDefinitonParserDelegate的parsePropertyValue方法.这里最最最值得提及的是,在该类resolveReference方法中,容器依旧通过getBean来获取需要被注入的bean对象,因此这里还需要进行1次依赖检查.如果该被注入的bean对象又依赖其他bean对象,那将重复上述过程.因此说创建与注入是个递归的过程.而最后完成对bean注入,也就是通常我们所谓的setter方法的,又需要回到BeanWrapper类,这里的setPropertyValue方法比较复杂,但我们可以暂且不必关心他的实现,因为写到这里我们已经知道了属性需要被注入的对象,在setPropertyValue只是将该对象进行赋值的操作. |
|
返回顶楼 | |
发表时间:2007-07-01
bennyparlo 写道 最后来总结下吧,对于普通ioc容器启动到被客户端使用的步骤大致如下:
定义->定位->装载->注册->创建->注入 呵呵,脉络很清楚了,补充一点: 在容器启动的时候完成:定义 -〉定位 -〉装载 在客户向容器要求bean的时候完成: 创建 -〉注入 |
|
返回顶楼 | |
发表时间:2007-07-01
jiwenke 写道 bennyparlo 写道 最后来总结下吧,对于普通ioc容器启动到被客户端使用的步骤大致如下:
定义->定位->装载->注册->创建->注入 呵呵,脉络很清楚了,补充一点: 在容器启动的时候完成:定义 -〉定位 -〉装载 在客户向容器要求bean的时候完成: 创建 -〉注入 不是吧,如果没有特殊的延迟加载的指定,对于单例对象应该是在spring初始化的时候就创建并注入的吧,对于多例的对象才在用户请求的时候才创建。 |
|
返回顶楼 | |
发表时间:2007-07-01
janh 写道 不是吧,如果没有特殊的延迟加载的指定,对于单例对象应该是在spring初始化的时候就创建并注入的吧,对于多例的对象才在用户请求的时候才创建。 确实没有考虑这个属性的处理,不太清楚这个延迟属性对加载的影响,但我觉得整个加载和创建注入的过程还是比较清楚的。 janh 写道 对于单例对象应该是在spring初始化的时候就创建并注入的吧,对于多例的对象才在用户请求的时候才创建。 你说的多例是不是指protoype类型? 能不能举些代码的例子来说明一下 - 大家一起研究研究?毕竟执行的代码是最明白的。 |
|
返回顶楼 | |
发表时间:2007-07-01
没错,janh说得也正是我所忽略的部分.我想目前所欠缺的还有对依赖检查的处理和singleton bean的加载方式.当然我们会继续来探讨.因为在目前spring ioc的使用过程中,对singleton bean的使用也占据了很大的比重,所以有必要研究.
|
|
返回顶楼 | |
发表时间:2007-07-02
bennyparlo 写道 没错,janh说得也正是我所忽略的部分.我想目前所欠缺的还有对依赖检查的处理和singleton bean的加载方式.当然我们会继续来探讨.因为在目前spring ioc的使用过程中,对singleton bean的使用也占据了很大的比重,所以有必要研究.
依赖检查具体指哪一部分?我看到在getbean中主要的处理过程是这样的: 1.检查缓存中是不是已经有了需要的bean 2.检查是不是需要在父工厂去生成bean 3.对单件bean的生成处理 4.对Prototype bean的生成处理,和单件bean不同的地方在每次都要create 我觉得对singleton和prototype的bean的处理生成和依赖注入都是在getBean的时候完成的,启动的时候不做这些处理。是不是和所谓的延迟加载有关系,现在还不太清楚。 另外,对FactoryBean的处理,包含在以下的方法中。 protected Object getObjectForBeanInstance(Object beanInstance, String name, RootBeanDefinition mbd) throws BeansException { String beanName = transformedBeanName(name); // Don't let calling code try to dereference the // bean factory if the bean isn't a factory. if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } boolean shared = (mbd == null || mbd.isSingleton()); Object object = beanInstance; // 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. // 如果需要从一个FactoryBean去getBean的话,不能直接返回这个FactoryBean,需要另外生成一个bean作为产出 if (beanInstance instanceof FactoryBean) { if (!BeanFactoryUtils.isFactoryDereference(name)) { // Return bean instance from factory. // 这里会调用FactoryBean的getObject方法,当然前提是已经确认这里是一个FactoryBean FactoryBean factory = (FactoryBean) beanInstance; if (logger.isDebugEnabled()) { logger.debug("Bean with name '" + beanName + "' is a factory bean"); } // Cache object obtained from FactoryBean if it is a singleton. // 从FactoryBean中去取生成的bean,同样的需要先看看缓存中是不是已经有,注意这个缓存是FactoryBean的缓存,而FactoryBean本身可以在容器的bean缓存中 if (shared && factory.isSingleton()) { synchronized (this.factoryBeanObjectCache) { object = this.factoryBeanObjectCache.get(beanName); if (object == null) { //这个方法调用了FactoryBean的getObject去返回需要到的bean,具体factory的实例需要实现FactoryBean的getObjet接口 object = getObjectFromFactoryBean(factory, beanName, mbd); this.factoryBeanObjectCache.put(beanName, object); } } } else { //如果不是单件的bean获取,每次都需要从FactoryBean中去生成需要的bean object = getObjectFromFactoryBean(factory, beanName, mbd); } } else { // The user wants the factory itself. if (logger.isDebugEnabled()) { logger.debug("Calling code asked for FactoryBean instance for name '" + beanName + "'"); } } } return object; } |
|
返回顶楼 | |
发表时间:2007-07-02
对的,目前我只看到对于单例模式的bean,在getBean时候去进行校验是否已经创建,而并没有看到在容器启动时候进行加载,而对于lazy-init=true是指如果该bean显式或隐式定义为单例的话,那么按照这样的推断,该bean将在被第1次请求的时候创建,而并非在容器启动时或者说被客户端请求访问前来实例化.
至于依赖检查,这里我比较混淆,因为我在多处看到了容器对依赖检查的处理.比如在创建BeanDefiniton和createBean的时候. |
|
返回顶楼 | |
发表时间:2007-07-02
bennyparlo 写道 对的,目前我只看到对于单例模式的bean,在getBean时候去进行校验是否已经创建,而并没有看到在容器启动时候进行加载,而对于lazy-init=true是指如果该bean显式或隐式定义为单例的话,那么按照这样的推断,该bean将在被第1次请求的时候创建,而并非在容器启动时或者说被客户端请求访问前来实例化.
同意,同样的在Rod.Johnson的《Professional JAVA Development with Spring Framework>> 中文译本P.38中也提到这这个 - 译文如下: 向容器请求bean触发了bean的创建和初始化,包括前述的依赖注入阶段。依赖注入步骤可以触发其他BEAN(第一个bean的依赖)的创建,这样下去,创建了相关对象实例的整个图。 嘿嘿,描述得经典........ |
|
返回顶楼 | |