spring源码解析(二)IOC容器
2017-04-01 15:21
1041 查看
作为面向对象编程的集大成之作,我们直接从接口入手可以帮助我们更直观的了解Ioc容器的设计原理。下图描述了Ioc容器中的主要接口设计:
这里主要是接口体系,而具体实现体系,比如DefaultListableBeanFactory就是为了实现ConfigurableBeanFactory,从而成为一个简单Ioc容器实现。与其他Ioc容器类似,XmlBeanFactory就是为了实现BeanFactory,但都是基于DefaultListableBeanFactory的基础做了扩展。同样的,ApplicationContext也一样。
从图中我们可以简要的做出以下分析:
1.从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,这是一条主要的BeanFactory设计路径。在这条接口设计路径中,BeanFactory,是一条主要的BeanFactory设计路径。在这条接口设计路径中,BeanFactory接口定义了基本的Ioc容器的规范。在这个接口定义中,包括了getBean()这样的Ioc容器的基本方法(通过这个方法可以从容器中取得Bean)。而HierarchicalBeanFactory接口在继承了BeanFactory的基本接口后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲Ioc容器的管理功能。在接下来的ConfigurableBeanFactory接口中,主要定义了一些对BeanFactory的配置功能,比如通过setParentBeanFactory()设置双亲Ioc容器,通过addBeanPostProcessor()配置Bean后置处理器,等等。通过这些接口设计的叠加,定义了BeanFactory就是最简单的Ioc容器的基本功能。
2.第二条接口设计主线是,以ApplicationContext作为核心的接口设计,这里涉及的主要接口设计有,从BeanFactory到ListableBeanFactory,再到ApplicationContext,再到我们常用的WebApplicationContext或者ConfigurableApplicationContext接口。我们常用的应用基本都是org.framework.context包里的WebApplicationContext或者ConfigurableApplicationContext实现。在这个接口体现中,ListableBeanFactory和HierarchicalBeanFactory两个接口,连接BeanFactory接口定义和ApplicationContext应用的接口定义。在ListableBeanFactory接口中,细化了许多BeanFactory的接口功能,比如定义了getBeanDefinitionNames()接口方法;对于ApplicationContext接口,它通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单Ioc容器的基础上添加了许多对高级容器的特性支持。
3.这个接口系统是以BeanFactory和ApplicationContext为核心设计的,而BeanFactory是Ioc容器中最基本的接口,在ApplicationContext的设计中,一方面,可以看到它继承了BeanFactory接口体系中的ListableBeanFactory、AutowireCapableBeanFactory、HierarchicalBeanFactory等BeanFactory的接口,具备了BeanFactoryIoc容器的基本功能;另一方面,通过继承MessageSource、ResourceLoadr、ApplicationEventPublisher这些接口,BeanFactory为ApplicationContext赋予了更高级的Ioc容器特性。对于ApplicationContext而言,为了在Web环境中使用它,还设计了WebApplicationContext接口,而这个接口通过继承ThemeSource接口来扩充功能。
容器的基础:XmlBeanFactory
xmlBeanFactory在使用过程中涉及到的类的关系图
看着非常复杂是吧,不要紧,我们以代码来做简要说明
1,创建Ioc配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。
2,创建一个BeanFactory,这里使用了DefaultListableBeanFactory。
3,创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory。
4,从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的Ioc容器就建立起来了。这个时候我们就可以直接使用Ioc容器了。
在Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源间的资源读取逻辑。而URL中却没有提供一些基本方法来实现自己的抽象结构。因而Spring对其内部资源,使用了自己的抽象结构:Resource接口来封装。而ClassPathResource实现类即是对Resource的实现。
Resource接口体系
资源的原始接口为Resource,它继承自InputStreamResource,实现了其getInstream方法,这样所有的资源就是通过该方法来获取输入流的。对于资源的加载,也实现了统一,定义了一个资源加载顶级接口ResourceLoader,它默认的加载就是DefaultResourceLoader
ClassPathResource
忽略给定接口:DefaultListableBeanFactory
这里DefaultListableBeanFactory所起到的是忽略给定接口自动装配功能。简单来说,一般bean中的功能A如果没有初始化,那么Spring会自动初始化A,这是Spring的一个特性。但当某些特殊情况时,B不会初始化,比如:B已经实现了BeanNameAware接口。可以说,就是通过其他方式来解析依赖,类似于BeanFactory的BeanFactoryAware。下面是具体实现:
这里是BeanDefinition真正被载入的地方。这个载入过程就是把用户定义好的Bean表示成Ioc容器内部的数据结构,当然这个数据结构就是BeanDefinition。而BeanDefinition实际上就是POJO对象在Ioc容器中的抽象,通过这个BeanDefinition定义的数据结构,让Ioc容器能够对POJO对象也就是Bean进行管理。
这里主要是接口体系,而具体实现体系,比如DefaultListableBeanFactory就是为了实现ConfigurableBeanFactory,从而成为一个简单Ioc容器实现。与其他Ioc容器类似,XmlBeanFactory就是为了实现BeanFactory,但都是基于DefaultListableBeanFactory的基础做了扩展。同样的,ApplicationContext也一样。
从图中我们可以简要的做出以下分析:
1.从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,这是一条主要的BeanFactory设计路径。在这条接口设计路径中,BeanFactory,是一条主要的BeanFactory设计路径。在这条接口设计路径中,BeanFactory接口定义了基本的Ioc容器的规范。在这个接口定义中,包括了getBean()这样的Ioc容器的基本方法(通过这个方法可以从容器中取得Bean)。而HierarchicalBeanFactory接口在继承了BeanFactory的基本接口后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲Ioc容器的管理功能。在接下来的ConfigurableBeanFactory接口中,主要定义了一些对BeanFactory的配置功能,比如通过setParentBeanFactory()设置双亲Ioc容器,通过addBeanPostProcessor()配置Bean后置处理器,等等。通过这些接口设计的叠加,定义了BeanFactory就是最简单的Ioc容器的基本功能。
2.第二条接口设计主线是,以ApplicationContext作为核心的接口设计,这里涉及的主要接口设计有,从BeanFactory到ListableBeanFactory,再到ApplicationContext,再到我们常用的WebApplicationContext或者ConfigurableApplicationContext接口。我们常用的应用基本都是org.framework.context包里的WebApplicationContext或者ConfigurableApplicationContext实现。在这个接口体现中,ListableBeanFactory和HierarchicalBeanFactory两个接口,连接BeanFactory接口定义和ApplicationContext应用的接口定义。在ListableBeanFactory接口中,细化了许多BeanFactory的接口功能,比如定义了getBeanDefinitionNames()接口方法;对于ApplicationContext接口,它通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单Ioc容器的基础上添加了许多对高级容器的特性支持。
3.这个接口系统是以BeanFactory和ApplicationContext为核心设计的,而BeanFactory是Ioc容器中最基本的接口,在ApplicationContext的设计中,一方面,可以看到它继承了BeanFactory接口体系中的ListableBeanFactory、AutowireCapableBeanFactory、HierarchicalBeanFactory等BeanFactory的接口,具备了BeanFactoryIoc容器的基本功能;另一方面,通过继承MessageSource、ResourceLoadr、ApplicationEventPublisher这些接口,BeanFactory为ApplicationContext赋予了更高级的Ioc容器特性。对于ApplicationContext而言,为了在Web环境中使用它,还设计了WebApplicationContext接口,而这个接口通过继承ThemeSource接口来扩充功能。
BeanFactory容器的设计
恩,我们与其写繁琐的文字,不如直接阅读代码来的直接的多,最原始的容器:BeanFactorypackage org.springframework.beans.factory; import org.springframework.beans.BeansException; import org.springframework.core.ResolvableType; /** * BeanFactory作为最原始同时也最重要的Ioc容器,它主要的功能是为依赖注入 (DI) 提供支持, BeanFactory 和相关的接口,比如,BeanFactoryAware、 * DisposableBean、InitializingBean,仍旧保留在 Spring 中,主要目的是向后兼容已经存在的和那些 Spring 整合在一起的第三方框架。在 Spring 中 * ,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一 * 个被配置化的系统或者应用。在资源宝贵的移动设备或者基于applet的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 ApplicationContext * * 这里定义的只是一系列的接口方法,通过这一系列的BeanFactory接口,可以使用不同的Bean的检索方法很方便地从Ioc容器中得到需要的Bean,从而忽略具体 * 的Ioc容器的实现,从这个角度上看,这些检索方法代表的是最为基本的容器入口。 * * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams * @since 13 April 2001 */ public interface BeanFactory { /** * 转定义符"&" 用来引用实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory * * FactoryBean和BeanFactory 是在Spring中使用最为频繁的类,它们在拼写上很相似。一个是Factory,也就是Ioc容器或对象工厂;一个 * 是Bean。在Spring中,所有的Bean都是由BeanFactory(也就是Ioc容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Be * an,而是一个能产生或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。 */ String FACTORY_BEAN_PREFIX = "&"; /** * 五个不同形式的getBean方法,获取实例 * @param name 检索所用的Bean名 * @return Object(<T> T) 实例对象 * @throws BeansException 如果Bean不能取得 */ Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; /** * 让用户判断容器是否含有指定名字的Bean. * @param name 搜索所用的Bean名 * @return boolean 是否包含其中 */ boolean containsBean(String name); /** * 查询指定名字的Bean是否是Singleton类型的Bean. * 对于Singleton属性,可以在BeanDefinition指定. * @param name 搜索所用的Bean名 * @return boolean 是否包是Singleton * @throws NoSuchBeanDefinitionException 没有找到Bean */ boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /** * 查询指定名字的Bean是否是Prototype类型的。 * 与Singleton属性一样,可以在BeanDefinition指定. * @param name 搜索所用的Bean名 * @return boolean 是否包是Prototype * @throws NoSuchBeanDefinitionException 没有找到Bean */ boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /** * 查询指定了名字的Bean的Class类型是否是特定的Class类型. * @param name 搜索所用的Bean名 * @param typeToMatch 匹配类型 * @return boolean 是否是特定类型 * @throws NoSuchBeanDefinitionException 没有找到Bean */ boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException; /** * 查询指定名字的Bean的Class类型. * @param name 搜索所用的Bean名 * @return 指定的Bean或者null(没有找到合适的Bean) * @throws NoSuchBeanDefinitionException 没有找到Bean */ Class<?> getType(String name) throws NoSuchBeanDefinitionException; /** * 查询指定了名字的Bean的所有别名,这些都是在BeanDefinition中定义的 * @param name 搜索所用的Bean名 * @return 指定名字的Bean的所有别名 或者一个空的数组 */ String[] getAliases(String name); }
容器的基础:XmlBeanFactory
package org.springframework.beans.factory.xml; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.core.io.Resource; /** * XmlBeanFactory是BeanFactory的最简单实现类 * * XmlBeanFactory的功能是建立在DefaultListableBeanFactory这个基本容器的基础上的,并在这个基本容器的基础上实行了其他诸如 * XML读取的附加功能。XmlBeanFactory使用了DefaultListableBeanFactory作为基础类,DefaultListableBeanFactory是一个很重 * 要的Ioc实现,会在下一章进行重点论述。 * * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams * @since 15 April 2001 */ public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); /** * 根据给定来源,创建一个XmlBeanFactory * @param resource Spring中对与外部资源的抽象,最常见的是对文件的抽象,特别是XML文件。而且Resource里面通常 * 是保存了Spring使用者的Bean定义,比如applicationContext.xml在被加载时,就会被抽象为Resource来处理。 * @throws BeansException 载入或者解析中发生错误 */ public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } /** * 根据给定来源和BeanFactory,创建一个XmlBeanFactory * @param resource Spring中对与外部资源的抽象,最常见的是对文件的抽象,特别是XML文件。而且Resource里面通常 * 是保存了Spring使用者的Bean定义,比如applicationContext.xml在被加载时,就会被抽象为Resource来处理。 * @param parentBeanFactory 父类的BeanFactory * @throws BeansException 载入或者解析中发生错误 */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
xmlBeanFactory在使用过程中涉及到的类的关系图
看着非常复杂是吧,不要紧,我们以代码来做简要说明
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; /** * 这是与SimpleBeanFactory等效的编程式使用Ioc容器 * * 从中我也可以看到一些Ioc的基本原理,同时也揭示了Ioc实现中的一些关键类:如Resource、DefaultListableBeanFactory * 以及BeanDefinitionReader等等 * * @author kay * @since 1.0 */ public class ProgramBeanFactory{ public static void main(String[] args) { ClassPathResource resource = new ClassPathResource("applicationContext.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(resource); Message message = factory.getBean("message", Message.class); //Message是自己写的测试类 message.printMessage(); } }以上,可以简单说明我们在使用Ioc容器时,需要如下几个步骤:
1,创建Ioc配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。
2,创建一个BeanFactory,这里使用了DefaultListableBeanFactory。
3,创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory。
4,从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的Ioc容器就建立起来了。这个时候我们就可以直接使用Ioc容器了。
在Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源间的资源读取逻辑。而URL中却没有提供一些基本方法来实现自己的抽象结构。因而Spring对其内部资源,使用了自己的抽象结构:Resource接口来封装。而ClassPathResource实现类即是对Resource的实现。
Resource接口体系
资源的原始接口为Resource,它继承自InputStreamResource,实现了其getInstream方法,这样所有的资源就是通过该方法来获取输入流的。对于资源的加载,也实现了统一,定义了一个资源加载顶级接口ResourceLoader,它默认的加载就是DefaultResourceLoader
package org.springframework.core.io; import java.io.IOException; import java.io.InputStream; /** * InputStreamSource 封装任何能返回InputStream的类,比如File、Classpath下的资源和Byte Array等 * * @author Juergen Hoeller * @since 20.01.2004 */ public interface InputStreamSource { /** * 返回InputStream的类,比如File、Classpath下的资源和Byte Array等 * @return InputStream 返回一个新的InputStream的对象 * @throws IOException 如果资源不能打开则抛出异常 */ InputStream getInputStream() throws IOException; }Resource接口
package org.springframework.core.io; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; /** * Resource接口抽象了所有Spring内部使用到的底层资源:File、URL、Classpath等。 * 同时,对于来源不同的资源文件,Resource也有不同实现:文件(FileSystemResource)、Classpath资源(ClassPathResource)、 * URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等等。 * * @author Juergen Hoeller * @since 28.12.2003 */ public interface Resource extends InputStreamSource { /** * 判断资源是否存在 * @return boolean 是否存在 */ boolean exists(); /** * 判断资源是否可读 * @return boolean 是否可读 */ boolean isReadable(); /** * 是否处于开启状态 * @return boolean 是否开启 */ boolean isOpen(); /** * 得到URL类型资源,用于资源转换 * @return URL 得到URL类型 * @throws IOException 如果资源不能打开则抛出异常 */ URL getURL() throws IOException; /** * 得到URI类型资源,用于资源转换 * @return URI 得到URI类型 * @throws IOException 如果资源不能打开则抛出异常 */ URI getURI() throws IOException; /** * 得到File类型资源,用于资源转换 * @return File 得到File类型 * @throws IOException 如果资源不能打开则抛出异常 */ File getFile() throws IOException; /** * 获取资源长度 * @return long 资源长度 * @throws IOException 如果资源不能打开则抛出异常 */ long contentLength() throws IOException; /** * 获取lastModified属性 * @return long 获取lastModified * @throws IOException 如果资源不能打开则抛出异常 */ long lastModified() throws IOException; /** * 创建一个相对的资源方法 * @param relativePath 相对路径 * @return Resource 返回一个新的资源 * @throws IOException 如果资源不能打开则抛出异常 */ Resource createRelative(String relativePath) throws IOException; /** * 获取文件名称 * @return String 文件名称或者null */ String getFilename(); /** * 得到错误处理信息,主要用于错误处理的信息打印 * @return String 错误资源信息 */ String getDescription(); }
ClassPathResource
private final String path; private ClassLoader classLoader; public ClassPathResource(String path) { this(path, (ClassLoader) null); //这里是入口,直接跳入下面的ClassPathResource(String path, ClassLoader classLoader) 中 } public ClassPathResource(String path, ClassLoader classLoader) { Assert.notNull(path, "Path must not be null"); String pathToUse = StringUtils.cleanPath(path); if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } this.path = pathToUse; this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); }
忽略给定接口:DefaultListableBeanFactory
这里DefaultListableBeanFactory所起到的是忽略给定接口自动装配功能。简单来说,一般bean中的功能A如果没有初始化,那么Spring会自动初始化A,这是Spring的一个特性。但当某些特殊情况时,B不会初始化,比如:B已经实现了BeanNameAware接口。可以说,就是通过其他方式来解析依赖,类似于BeanFactory的BeanFactoryAware。下面是具体实现:
这里是BeanDefinition真正被载入的地方。这个载入过程就是把用户定义好的Bean表示成Ioc容器内部的数据结构,当然这个数据结构就是BeanDefinition。而BeanDefinition实际上就是POJO对象在Ioc容器中的抽象,通过这个BeanDefinition定义的数据结构,让Ioc容器能够对POJO对象也就是Bean进行管理。
相关文章推荐
- Spring源码阅读-- IOC容器资源解析
- Spring源码解析之IoC容器主要接口设计
- Spring源码解析:IoC容器的基本结构设计
- Spring源码解析三:IOC容器的依赖注入
- 【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【II】
- Spring源码-IOC容器(二)-Bean的定位解析注册
- Spring源码阅读之IoC容器初始化2 -- BeanDefinition载入与解析
- 【Spring源码解析】之IOC容器
- Spring源码解析二:IOC容器初始化过程详解
- Spring源码解析-BeanDefinition在IOC容器中的注册(三)
- Spring源码解析-IOC容器的实现-ApplicationContext
- Spring源码解析之IoC容器系列的设计实现(IoC容器系列概况)
- 【Spring源码解析】之IOC容器在Web容器中的启动
- Spring源码分析--Ioc容器定位解析资源文件并注册BeanDefinition
- Spring IOC容器启动流程源码解析(一)——容器概念详解及源码初探
- Spring源码-IOC容器(十)-@Autowired解析
- Spring IoC源码解析(一)——配置文件加载和IoC容器初始化
- Spring源码解析一:IOC容器设计
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- 【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【I】