如何在 Exchange 中获取嵌套组的所有成员
2013-04-05 19:58
162 查看
IOC初始化
最近再看Spring技术内幕。发现里面写的挺不错,就是个人觉得有点乱。这里按照程序执行顺序重新整理一遍,方便理解。
再次声明:仅是个人意见!!!
Figure1
Figure2
Figure3
Figure4
Figure5
以FileSystemXmlApplicationContext为例,初始化的三个部分:
1. 配置文件资源(Resource interface)的定位
2. 配置文件资源的载入(载入Document对象并且解析成BeanDefinition的格式)
3. BeanDefinition在IOC容器中的注册(这一部分包括将BeanDefinition注册入容器)
代码1:
上面代码是一个简单的初始化一个IOC容器。其中DefaultListableBeanFactory是一个BeanFactory的实现类。其只是个IOC容器,而ApplicationContext是对上述进行了一个封装。
首先BeanDefinition是Xml中Bean标签的抽象,FileSystemXmlApplicationContext底层还是使用的DefaultListableBeanFactory。由Figure3可以看出AbstractApplicationContext实现了ResourceLoader,所以资源的定位这一部分是由AbstractApplicationContext完成的,而FileSystemXmlApplicationContext重写了getResourceByPath(String path)方法,所以具体定位是由FileSystemXmlApplicationContext完成的
第二部分这里使用了两个类DefaultBeanDefinitionDocumentReader和BeanDefinitionParserDelegate,前者是读取BeanDefinition的信息,后者是解析BeanDefinition的信息完成对Xml中Bean标签的抽象
由figure5可以看出DefaultListableBeanFactory继承了BeanDefinitionRegistry。所以第三部分BeanDefinition是由DefaultListableBeanFactory完成的。
1.启动初始化
在FileSystemXmlApplicationContext的构造方法FileSystemXmlApplicationContext(String[] configLocationions,boolean refresh,Application parent)中调用了方法refresh(),该方法定义了整个初始化IOC的流程,也是IOC初始化的入口.具体实现在其父类AbstractApplicationContext中实现。改方法中有句代码如ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这句主要就是刷新(刷新的过程其实就是销毁所有的bean再关闭BeanFactory然后再新建)当前的bean facotry也就是IOC容器并且返回,当然如当前没有IOC那就新建一个返回。在obtainFreshBeanFactory()方法中调用refreshBeanFactory()方法,该方法是由其子类AbstractRefreshableApplicatioContext实现的。改方法中就会判断是否已经存在了IOC容器。判断完之后紧接着就是创建一个beanFactory代码如下:
代码2
从这里可以看出其底层依旧是使用DefaultListableBeanFactory.从这里主要开始看loadBeanDefinitions这个方法了。这个loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法是个抽象方法具体实现需要在其子类AbstractXmlApplicationContext中查找代码如下:
代码3:
这段代码看起来和之前的代码一有相似之处了。但还有不同之处,由于代码1中我们直接定义了一个Resource换而言之就是我们手动的定位了Resource。而这里我们能获取Resource的唯一途径就在我们构造FileSystemXmlApplication的时候传入的configLocatio-ns这个文件路径数组。所以我们需要使用setResourceLoader(ResourceLoader resourceLoader)这个方法将一个Resource加载器给当前的BeanDefinition的读取器XmlBeanDefinitionReader,让它内部能够自动完成Resource的定位和创建。然后就是将这个XmlBeanDefinitionReader作为参数传递给loadBeanDefinitions的一个重载方法loadBeanDefinitions(XmlBeanDefinitionReader reader)中。在这个loadBeanDefinitions的方法中。从这里开始将进入配置文件资源Resource定位的部分了。
2.配置文件资源Resource定位
上面讲到进入重载方法loadBeanDefinitions(XmlBeanDefinitionReader reader)。在这个方法中代码如下:
代码4:
从代码能够发现,它在这里不仅会检测配置文件路径configLocations还会检测已经定位好的Resource通过getConfigResources()方法。但是在该文件的源代码中的getConfigResources()方法中只有个return null的语句。所以if (configResources != null)始终不通过,换言之。这里只检测配置文件路径,这里可能是为了后续的拓展。至于getConfigLocations(),改方法继承自其父类AbstractRefreshableConfigApplicationContext中的。具体实现语句就是判断configLocations是否为null,如果不是null返回当前值,是null返回默认值。由于之前构造FileSystemXmlApplicationContext的时候已经将configLoactions传入进去了。所以这里直接进入if语句。调用loadBeanDef-initions(String[] locations)方法,该是继承自XmlBeanDefinitionReader父类AbstractBeanDefinitionReader中的。具体代码如下:
代码5:
代码6:
这里首先会判断ResourceLoader是否为空。由于之前我们在AbstractXmlApplicationContext里填入了该值beanDefinitionReader.setResourceLoader(this)。所以继续执行下面的if。由于我们填入的ResourceLoader并不是ResourcePatternResolver类型所以进入else中。else中执行了Resource resource = resourceLoader.getRes-ource(location).由于之前在AbstractXmlApplicationContext里调用了beanDefinitionReader.setResourceLoader(this)。而AbstractXmlApplicationContext是DefaultResourceLoader的子类。DefaultResourceLoader又是ResouceLoader的实现类,所以这里应该调用的是DefaultResourceLoader的getResouce(String location)方法。具体代码如下:
代码7:
由于这里并不是URL方式表示的配置文件路径。所以直接跳入cathc语句块。执行getResourceByPath(location)。由于这个getResource方法是其子类FileSystemXmlApplicationContext调用的,所以调用的是FileSystemXmlApplicationContext重写的getResourceByPath(location)并且返回一个Resource方法代码如下:
代码8:
由于至此Resource的定位部分完成了。
3.BeanDefinition的加载和解析
之前已经介绍如何定位Resource.也就是已经将配置文件的IO封装成Resource并且返回了。让我们继续回到代码6中AbstractBeanDefinitionReader这个类。在获取了Resource之后继续执行了loadBeanDefinitions (Resource resource).这里的resource正是刚才获取的。执行的loadBeanDefinitions是BeanDefinitionReader的接口方法。具体的实现实在其实现类AbstractBeanDefinitionReader的子类中。当然loadBeanDefinitions(Resource resource)这个方法也不是直接使用resource去解析。而是将resource进一步封装成EncodeResource并将其作为参数传递给loadBeanDefinitions(EncodeResource encodeResource)这个重载的方法进行处理.主要代码如下:
代码9:
如代码所示在这里会对Resource处理,将IO流从Resource中读取出来,然后封装成InputSource并将其传入doLoadBeanDefinitions。doLoadBeanDefinitions方法依旧是XmlBeanDefinitionReader中的方法.然后doLoadBeanDefinitions将根据inputSource产生出Document对象这个对象就是Xml文件的一个抽象。并且将这个Document传入XmlBeanDefinitionReader中的registerBeanDefinition方法中去代码如下
代码10:
配置文件加载完成,现在开始解析部分,将Xml形式解析成BeanDefinition这种形式。首先进入XmlBeanDefinitionReader这个的registerBeandDefinitions这个方法。这个方法内部会新建立一个BeanDefinitionDocumentReader这个对象。该对象是专门用于读取文档的一个API。然后再将上面所说的Document对象作为参数传递给BeanDefinitionDocumentReader对象的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法中并且调用,该方法中将完成解析的过程代码如下:
代码11:
这里调用的registerBeanDefinitions这个方法是个接口方法,具体实现在其子类DefaultBeanDefinitionDocumentReader中实现。该子类实现的是仅仅是对Document的读取功能,具体解析的功能还需要一个类registerBeanDefinitions.所以在registerBeanDefinitions方法中会产生个registerBeanDefinitions类帮助解析。具体如何解析这里不做说明。自己也能看懂
至此,BeanDefinition的加载和解析也完成了。接下来就是如何注册了。
4.BeanDefinition在IOC容器中的注册
经过之前的分析已经能够了解到注册这部分的功能的实现是通过DefaultListableBeanFactory这个Class完成的。而实际上BeanDefinition的注册这一功能的实现是通过一个名为beanDefinitionMap的HashMap这样一个数据结构来维护的。那么具体是从说明地方开始调用和触发注册部分的功能的呢。通过Eclipse发现,如图所示
Figure6
当在DefaultBeanDefinitionDocumentReader调用processBeanDefinition的时候。当我们解析完BeanDefinition之后通过BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())。这么一条语句调用的。最后会获取我们bean的Name和BeanDefinition对象然后作为参数传入registerBeanDefinition这个方法完成注册。注册代码如下:
代码12:
最近再看Spring技术内幕。发现里面写的挺不错,就是个人觉得有点乱。这里按照程序执行顺序重新整理一遍,方便理解。
再次声明:仅是个人意见!!!
Figure1
Figure2
Figure3
Figure4
Figure5
以FileSystemXmlApplicationContext为例,初始化的三个部分:
1. 配置文件资源(Resource interface)的定位
2. 配置文件资源的载入(载入Document对象并且解析成BeanDefinition的格式)
3. BeanDefinition在IOC容器中的注册(这一部分包括将BeanDefinition注册入容器)
代码1:
ClassPathResource res = new ClassPathResource(“beans.xml”); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); Reader.loadBeanDefinitions(res);
上面代码是一个简单的初始化一个IOC容器。其中DefaultListableBeanFactory是一个BeanFactory的实现类。其只是个IOC容器,而ApplicationContext是对上述进行了一个封装。
首先BeanDefinition是Xml中Bean标签的抽象,FileSystemXmlApplicationContext底层还是使用的DefaultListableBeanFactory。由Figure3可以看出AbstractApplicationContext实现了ResourceLoader,所以资源的定位这一部分是由AbstractApplicationContext完成的,而FileSystemXmlApplicationContext重写了getResourceByPath(String path)方法,所以具体定位是由FileSystemXmlApplicationContext完成的
第二部分这里使用了两个类DefaultBeanDefinitionDocumentReader和BeanDefinitionParserDelegate,前者是读取BeanDefinition的信息,后者是解析BeanDefinition的信息完成对Xml中Bean标签的抽象
由figure5可以看出DefaultListableBeanFactory继承了BeanDefinitionRegistry。所以第三部分BeanDefinition是由DefaultListableBeanFactory完成的。
1.启动初始化
在FileSystemXmlApplicationContext的构造方法FileSystemXmlApplicationContext(String[] configLocationions,boolean refresh,Application parent)中调用了方法refresh(),该方法定义了整个初始化IOC的流程,也是IOC初始化的入口.具体实现在其父类AbstractApplicationContext中实现。改方法中有句代码如ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这句主要就是刷新(刷新的过程其实就是销毁所有的bean再关闭BeanFactory然后再新建)当前的bean facotry也就是IOC容器并且返回,当然如当前没有IOC那就新建一个返回。在obtainFreshBeanFactory()方法中调用refreshBeanFactory()方法,该方法是由其子类AbstractRefreshableApplicatioContext实现的。改方法中就会判断是否已经存在了IOC容器。判断完之后紧接着就是创建一个beanFactory代码如下:
代码2
DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; }
从这里可以看出其底层依旧是使用DefaultListableBeanFactory.从这里主要开始看loadBeanDefinitions这个方法了。这个loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法是个抽象方法具体实现需要在其子类AbstractXmlApplicationContext中查找代码如下:
代码3:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
这段代码看起来和之前的代码一有相似之处了。但还有不同之处,由于代码1中我们直接定义了一个Resource换而言之就是我们手动的定位了Resource。而这里我们能获取Resource的唯一途径就在我们构造FileSystemXmlApplication的时候传入的configLocatio-ns这个文件路径数组。所以我们需要使用setResourceLoader(ResourceLoader resourceLoader)这个方法将一个Resource加载器给当前的BeanDefinition的读取器XmlBeanDefinitionReader,让它内部能够自动完成Resource的定位和创建。然后就是将这个XmlBeanDefinitionReader作为参数传递给loadBeanDefinitions的一个重载方法loadBeanDefinitions(XmlBeanDefinitionReader reader)中。在这个loadBeanDefinitions的方法中。从这里开始将进入配置文件资源Resource定位的部分了。
2.配置文件资源Resource定位
上面讲到进入重载方法loadBeanDefinitions(XmlBeanDefinitionReader reader)。在这个方法中代码如下:
代码4:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
从代码能够发现,它在这里不仅会检测配置文件路径configLocations还会检测已经定位好的Resource通过getConfigResources()方法。但是在该文件的源代码中的getConfigResources()方法中只有个return null的语句。所以if (configResources != null)始终不通过,换言之。这里只检测配置文件路径,这里可能是为了后续的拓展。至于getConfigLocations(),改方法继承自其父类AbstractRefreshableConfigApplicationContext中的。具体实现语句就是判断configLocations是否为null,如果不是null返回当前值,是null返回默认值。由于之前构造FileSystemXmlApplicationContext的时候已经将configLoactions传入进去了。所以这里直接进入if语句。调用loadBeanDef-initions(String[] locations)方法,该是继承自XmlBeanDefinitionReader父类AbstractBeanDefinitionReader中的。具体代码如下:
代码5:
public int loadBeanDefinitions(String[] locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int counter = 0; for (String location : locations) { counter += loadBeanDefinitions(location); } return counter; }
代码6:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException(…); } if (resourceLoader instanceof ResourcePatternResolver) { … } else { Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } … return loadCount; } }
这里首先会判断ResourceLoader是否为空。由于之前我们在AbstractXmlApplicationContext里填入了该值beanDefinitionReader.setResourceLoader(this)。所以继续执行下面的if。由于我们填入的ResourceLoader并不是ResourcePatternResolver类型所以进入else中。else中执行了Resource resource = resourceLoader.getRes-ource(location).由于之前在AbstractXmlApplicationContext里调用了beanDefinitionReader.setResourceLoader(this)。而AbstractXmlApplicationContext是DefaultResourceLoader的子类。DefaultResourceLoader又是ResouceLoader的实现类,所以这里应该调用的是DefaultResourceLoader的getResouce(String location)方法。具体代码如下:
代码7:
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { return getResourceByPath(location); } } }
由于这里并不是URL方式表示的配置文件路径。所以直接跳入cathc语句块。执行getResourceByPath(location)。由于这个getResource方法是其子类FileSystemXmlApplicationContext调用的,所以调用的是FileSystemXmlApplicationContext重写的getResourceByPath(location)并且返回一个Resource方法代码如下:
代码8:
protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }
由于至此Resource的定位部分完成了。
3.BeanDefinition的加载和解析
之前已经介绍如何定位Resource.也就是已经将配置文件的IO封装成Resource并且返回了。让我们继续回到代码6中AbstractBeanDefinitionReader这个类。在获取了Resource之后继续执行了loadBeanDefinitions (Resource resource).这里的resource正是刚才获取的。执行的loadBeanDefinitions是BeanDefinitionReader的接口方法。具体的实现实在其实现类AbstractBeanDefinitionReader的子类中。当然loadBeanDefinitions(Resource resource)这个方法也不是直接使用resource去解析。而是将resource进一步封装成EncodeResource并将其作为参数传递给loadBeanDefinitions(EncodeResource encodeResource)这个重载的方法进行处理.主要代码如下:
代码9:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(…); if (logger.isInfoEnabled()) { … } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException(…); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding( encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); }finally { inputStream.close(); } }catch (IOException ex) { throw new BeanDefinitionStoreException(…); }finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.set(null); } } }
如代码所示在这里会对Resource处理,将IO流从Resource中读取出来,然后封装成InputSource并将其传入doLoadBeanDefinitions。doLoadBeanDefinitions方法依旧是XmlBeanDefinitionReader中的方法.然后doLoadBeanDefinitions将根据inputSource产生出Document对象这个对象就是Xml文件的一个抽象。并且将这个Document传入XmlBeanDefinitionReader中的registerBeanDefinition方法中去代码如下
代码10:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); } … }
配置文件加载完成,现在开始解析部分,将Xml形式解析成BeanDefinition这种形式。首先进入XmlBeanDefinitionReader这个的registerBeandDefinitions这个方法。这个方法内部会新建立一个BeanDefinitionDocumentReader这个对象。该对象是专门用于读取文档的一个API。然后再将上面所说的Document对象作为参数传递给BeanDefinitionDocumentReader对象的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法中并且调用,该方法中将完成解析的过程代码如下:
代码11:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { SPI. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
这里调用的registerBeanDefinitions这个方法是个接口方法,具体实现在其子类DefaultBeanDefinitionDocumentReader中实现。该子类实现的是仅仅是对Document的读取功能,具体解析的功能还需要一个类registerBeanDefinitions.所以在registerBeanDefinitions方法中会产生个registerBeanDefinitions类帮助解析。具体如何解析这里不做说明。自己也能看懂
至此,BeanDefinition的加载和解析也完成了。接下来就是如何注册了。
4.BeanDefinition在IOC容器中的注册
经过之前的分析已经能够了解到注册这部分的功能的实现是通过DefaultListableBeanFactory这个Class完成的。而实际上BeanDefinition的注册这一功能的实现是通过一个名为beanDefinitionMap的HashMap这样一个数据结构来维护的。那么具体是从说明地方开始调用和触发注册部分的功能的呢。通过Eclipse发现,如图所示
Figure6
当在DefaultBeanDefinitionDocumentReader调用processBeanDefinition的时候。当我们解析完BeanDefinition之后通过BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())。这么一条语句调用的。最后会获取我们bean的Name和BeanDefinition对象然后作为参数传入registerBeanDefinition这个方法完成注册。注册代码如下:
代码12:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "'beanName' must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); }catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(…); } } synchronized (this.beanDefinitionMap) { Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(…); }else { if (this.logger.isInfoEnabled()) { this.logger.info(…); } } }else { this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } this.beanDefinitionMap.put(beanName, beanDefinition); resetBeanDefinition(beanName); } }
相关文章推荐
- 如何最快的获取QQ群所有成员的qq号信息
- CE MAPI实例讲解 --- 如何获取Inbox里的所有消息(一)(zhuan)
- c#如何获取当前系统所有的字体和颜色?
- 如何利用API获取WINDOWS系统所有用户名和密码(做过的朋友进来看看)
- gridview中嵌套checkbox,如何在选中事件中获取当前行的索引值
- 如何获取一个基因家族的所有小麦基因
- Python中子类如何获取父类的类成员
- 如何用WaitForMultipleObjects获取所有被同时触发的内核对象
- 如何采集所有QQ群成员?
- 算法积累:解决如何获取指定文件夹路径或者文件路径下所有子文件后缀为.h .m .c的文本的行数
- 方法:如何获取远程PC所有共享资源
- js如何通过正则表达式获取所有的img的src
- 如何获取元素的所有属性??
- 如何获取本机所有MAC地址
- 如何获取结构体某成员的偏移地址
- 如何获取exchange邮箱限量
- CE MAPI实例讲解 --- 如何获取Inbox里的所有消息(二)(zhuan)
- c#如何获取当前系统所有的字体和颜色?
- MyBatis: 获取一个用户的所有blog的基础上讲一下如何获取一篇文章机器作者的信息。
- 如何在angularJS深层嵌套ngRepeat中获取不同层级的$index