控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找。依赖注入应用比较广泛。把控制权从具体的对象手中交给平台或者是框架。
2 BeanFactory是基本的功能接口
public interface BeanFactory { //这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象, //如果需要得到工厂本身,需要转义 String FACTORY_BEAN_PREFIX = "&"; //这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就是一个大的抽象工厂。 Object getBean(String name) throws BeansException; //这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根据名字取得的bean实例的Class类型和需要的不同的话。 Object getBean(String name, Class requiredType) throws BeansException; //这里提供对bean的检索,看看是否在IOC容器有这个名字的bean boolean containsBean(String name); //这里根据bean名字得到bean实例,并同时判断这个bean是不是单件 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; //这里对得到bean实例的Class类型 Class getType(String name) throws NoSuchBeanDefinitionException; //这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来 String[] getAliases(String name); }
BeanFactory只是对IOC容器中的基本行为作了定义,但是并没有管理如何加载baen.XmlBeanFactory和ApplicationContext都实现了BeanFactory
spring提供了一个BeanFactory的基本实现----xmlBaenFactory.AbstractBeanFactory,DefaultListableBeanFactory这些抽象类为其提供模板服务.Resource接口来抽象Bean的数据
对XML定义文件的解释委托给XmlBeanDefinitionReader来完成。
XmlBeanFacotry的使用过程。
XmlBeanFactory的源代码
XmlBeanFactoy和ApplicationContext的区别,XmlBeanFactory不具备定义资源的能力,
ApplicationContext
继承Messagesource,可以支持不同的信息源
访问资源,ResourceLoader和Resource,可以从不同的地方来获取Bean的定义资源
支持应用事件,继承了ApplicationEvnetPublisher接口
ApplicationContext允许上下文嵌套 - 通过保持父上下文可以维持一个上下文体系 - 这个体系我们在以后对Web容器中的上下文环境的分析中可以清楚地看到。对于bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的bean定义环境。这个我们在分析Web容器中的上下文环境时也能看到。
ApplicationContext提供IoC容器的主要接口,在其体系中有许多抽象子类比如AbstractApplicationContext为具体的BeanFactory的实现,比如FileSystemXmlApplicationContext和 ClassPathXmlApplicationContext提供上下文的模板,使得他们只需要关心具体的资源定位问题。当应用程序代码实例化 FileSystemXmlApplicationContext的时候,得到IoC容器的一种具体表现 - ApplicationContext,从而应用程序通过ApplicationContext来管理对bean的操作。
BeanFactory 是一个接口,在实际应用中我们一般使用ApplicationContext来使用IOC容器,它们也是IOC容器展现给应用开发者的使用接口。对应用程序开发者来说,可以认为BeanFactory和ApplicationFactory在不同的使用层面上代表了SPRING提供的IOC容器服务。
FileSystemXmlApplicationContext简历IOC容器
refresh的模板在AbstractApplicationContext:refresh的模板在AbstractApplicationContext:refresh的模板在AbstractApplicationContext:
这个方法包含了整个BeanFactory初始化的过程,对于特定的FileSystemXmlBeanFactory,我们看到定位资源位置由refreshBeanFactory()来实现:
在AbstractXmlApplicationContext中定义了对资源的读取过程,默认由XmlBeanDefinitionReader来读取:
转到beanDefinitionReader中进行处理:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { //调用XmlBeanDefinitionReader来载入bean定义信息。 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
转到beanDefinitionReader中进行处理:
而在作为其抽象父类的AbstractBeanDefinitionReader中来定义载入过程:
当我们通过ResourceLoader来载入资源,别忘了了我们的GenericApplicationContext也实现了ResourceLoader接口:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { public Resource getResource(String location) { //这里调用当前的loader也就是DefaultResourceLoader来完成载入 if (this.resourceLoader != null) { return this.resourceLoader.getResource(location); } return super.getResource(location); } ....... }
而我们的FileSystemXmlApplicationContext就是一个DefaultResourceLoader - GenericApplicationContext()通过DefaultResourceLoader:
public Resource getResource(String location) { //如果是类路径的方式,那需要使用ClassPathResource来得到bean文件的资源对象 if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // 如果是URL方式,使用UrlResource作为bean文件的资源对象 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // 如果都不是,那我们只能委托给子类由子类来决定使用什么样的资源对象了 return getResourceByPath(location); } } }
我们的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的实现类,他实现了以下的接口
protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } //这里使用文件系统资源对象来定义bean文件 return new FileSystemResource(path); }
这样代码就回到了FileSystemXmlApplicationContext中来,他提供了FileSystemResource来完成从文件系统得到配置文件的资源定义。这样,就可以从文件系统路径上对IOC配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加载,在Spring中我们看到它提供的各种资源抽象,比如ClassPathResource, URLResource,FileSystemResource等来供我们使用。上面我们看到的是定位Resource的一个过程,而这只是加载过程的一部分 - 我们回到AbstractBeanDefinitionReaderz中的loadDefinitions(resource)来看看得到代表bean文件的资源定义以后的载入过程,默认的我们使用XmlBeanDefinitionReader:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ....... try { //这里通过Resource得到InputStream的IO流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //从InputStream中得到XML的解析源 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //这里是具体的解析和注册过程 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { //关闭从Resource中得到的IO流 inputStream.close(); } } ......... } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); //通过解析得到DOM,然后完成bean在IOC容器中的注册 Document doc = this.documentLoader.loadDocument( inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware); return registerBeanDefinitions(doc, resource); } ....... }