这是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持,这个容器接口在 org.springframework.beans.factory.BeanFactor 中被定义。BeanFactory 和相关的接口,比如BeanFactoryAware、DisposableBean、InitializingBean,仍旧保留在 Spring 中,主要目的是向后兼容已经存在的和那些 Spring 整合在一起的第三方框架。
在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
在资源宝贵的移动设备或者基于 applet 的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 ApplicationContext,除非你有更好的理由选择 BeanFactory。
XmlBeanFactory设计的类继承关系图:
XmlBeanFactory继承自DefaultListableBeanFactory这个类,后者是我们经常要用到的IOC容器的实现,比如在设计应用上下文ApplicationContext时就会用到它。在Spring中,实际上是把DefaultListableBeanFactory作为一个默认功能完整的IOC容器来使用的。XmlBeanFactory继承DefaultListableBeanFactory容器功能的同时,增加了新的功能。从名字我们可以看出,它是一个与XML相关的BeanFactory,也就是说它是一个可以读取以XML文件方式定义的BeanDefinition的IOC容器。(关于BeanDefinition,我们可以理解为spring ioc的bean配置文件)在XmlBeanFactory中,初始化了一个XmlBeanDefinitionReader对象,有了这个Reader对象,那些以XML方式定义的BeanDefinition就有了处理的地方。我们可以看到,对这些XML形式的信息的处理实际上是由这个XmlBeanDefinitionReader来完成的。构造XmlBeanFactory这个IOC容器时,需要指定BeanDefinition的信息来源,而这个信息来源需要封装成Spring中的Resource类,Resource源码:
public interface Resource extends InputStreamSource {
/**
* Return whether this resource actually exists in physical form.
* <p>This method performs a definitive existence check, whereas the
* existence of a {@code Resource} handle only guarantees a
* valid descriptor handle.
*/
boolean exists();
/**
* Return whether the contents of this resource can be read,
* e.g. via {@link #getInputStream()} or {@link #getFile()}.
* <p>Will be {@code true} for typical resource descriptors;
* note that actual content reading may still fail when attempted.
* However, a value of {@code false} is a definitive indication
* that the resource content cannot be read.
* @see #getInputStream()
*/
boolean isReadable();
/**
* Return whether this resource represents a handle with an open
* stream. If true, the InputStream cannot be read multiple times,
* and must be read and closed to avoid resource leaks.
* <p>Will be {@code false} for typical resource descriptors.
*/
boolean isOpen();
/**
* Return a URL handle for this resource.
* @throws IOException if the resource cannot be resolved as URL,
* i.e. if the resource is not available as descriptor
*/
URL getURL() throws IOException;
/**
* Return a URI handle for this resource.
* @throws IOException if the resource cannot be resolved as URI,
* i.e. if the resource is not available as descriptor
*/
URI getURI() throws IOException;
/**
* Return a File handle for this resource.
* @throws IOException if the resource cannot be resolved as absolute
* file path, i.e. if the resource is not available in a file system
*/
File getFile() throws IOException;
/**
* Determine the content length for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long contentLength() throws IOException;
/**
* Determine the last-modified timestamp for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long lastModified() throws IOException;
/**
* Create a resource relative to this resource.
* @param relativePath the relative path (relative to this resource)
* @return the resource handle for the relative resource
* @throws IOException if the relative resource cannot be determined
*/
Resource createRelative(String relativePath) throws IOException;
/**
* Determine a filename for this resource, i.e. typically the last
* part of the path: for example, "myfile.txt".
* <p>Returns {@code null} if this type of resource does not
* have a filename.
*/
String getFilename();
/**
* Return a description for this resource,
* to be used for error output when working with the resource.
* <p>Implementations are also encouraged to return this value
* from their {@code toString} method.
* @see Object#toString()
*/
String getDescription();
}
public interface InputStreamSource {
/**
* Return an {@link InputStream}.
* <p>It is expected that each call creates a <i>fresh</i> stream.
* <p>This requirement is particularly important when you consider an API such
* as JavaMail, which needs to be able to read the stream multiple times when
* creating mail attachments. For such a use case, it is <i>required</i>
* that each {@code getInputStream()} call returns a fresh stream.
* @return the input stream for the underlying resource (must not be {@code null})
* @throws IOException if the stream could not be opened
* @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
*/
InputStream getInputStream() throws IOException;
}
Resource是Spring用来封装I/O操作的类。比如我们的BeanDefinition信息的以XML文件的形式存在的,那么可以使用像ClassPathResource res = new ClassPathResource("beans.xml")这样具体的ClassPathResource来构造需要的Resource,然后将Resource作为构造参数传递给XmlBeanFactory构造函数。这样,IOC容器就可以方便地定位到需要的BeanDefinition信息来对Bean完成容器的初始化和依赖注入过程。
XmlBeanFactory的功能是建立在DefaultListableBeanFactory这个基本容器基础上的,并在这个基本容器的基本上实现了诸如XML读取的附加功能。如代码清单所示,在
XmlBeanFactory构造方法中需要得到Resource对象。对XmlBeanDefinitionReader初始化,以及使用这个对象来完成loadBeanDefinitions的调用,就是这个调用启动从Resource中载人BeanDefinition的过程,loadBeanDefinitions同时也是IOC初始化的重要组成部分。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public EncodedResource(Resource resource) {
Assert.notNull(resource, "Resource must not be null");
this.resource = resource;
}