Spring IOC,是控制反转的意思,也称为DI(依赖注入),它们含义相似,都指对象所依赖的对象或属性的创建本来有对象自身完成,现在权利反转给IOC容器。
IOC源码中基本的接口如下:
BeanFactory
Resource
BeanDefinition
BeanDefinitionRead
ApplicationContext
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类型和需要的不同的话。
- <T> T getBean(String name, Class requiredType) throws BeansException;
- //这里根据Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果的Class类型和需要的不同的话。
- <T> T 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的别名,如果根据别名检索,那么其原名也会被检索出来,这些别名都是在用户的BeanDefination中定义的
- String[] getAliases(String name);
- }
Resource
定义了资源访问的基本接口,实现该接口的类有ClassPathResource, FileSystemResource,InputStreamResource, ServletContextResource, UrlResource等,(UrlResource:访问网络资源的实现类。ClassPathResource:访问类加载路径里资源的实现类。FileSystemResource:访问文件系统里资源的实现类,ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类,InputStreamResource:访问输入流资源的实现类。)
Resource继承了InputStreamSource。
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
Resource接口:
public interface Resource extends InputStreamSource {
/**
*判断该资源是否存在
*/
boolean exists();
/**
* Return whether the contents of this resource can be read,
*/
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.
*/
boolean isOpen();
/**
* Return a URL handle for this resource.
*/
URL getURL() throws IOException;
/**
* Return a URI handle for this resource.
*/
URI getURI() throws IOException;
/**
* Return a File handle for this resource.
*/
File getFile() throws IOException;
/**
* Determine the content length for this resource.
*/
long contentLength() throws IOException;
/**
* Determine the last-modified timestamp for this resource.
*/
long lastModified() throws IOException;
/**
* Create a resource relative to this resource.
*/
Resource createRelative(String relativePath) throws IOException;
/**
* Determine a filename for this resource, i.e. typically the last
*/
String getFilename();
/**
* Return a description for this resource,
*/
String getDescription();
}</span>
Resource的实现类中实现了这些接口,以FileSystemResource为例:
public class FileSystemResource extends AbstractResource implements WritableResource {
private final File file;
private final String path;
public FileSystemResource(File file) {
Assert.notNull(file, "File must not be null");
this.file = file;
this.path = StringUtils.cleanPath(file.getPath());
}
public FileSystemResource(String path) {
Assert.notNull(path, "Path must not be null");
this.file = new File(path);
this.path = StringUtils.cleanPath(path);
}
/**
* Return the file path for this resource.
*/
public final String getPath() {
return this.path;
}
/**
* This implementation returns whether the underlying file exists.
*/
@Override
public boolean exists() {
return this.file.exists();
}
.....
.....
/**
* This implementation opens a FileInputStream for the underlying file.
*/
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
/**
* This implementation returns a URL for the underlying file.
*/
@Override
public URL getURL() throws IOException {
return this.file.toURI().toURL();
}
/**
* This implementation returns a URI for the underlying file.
*/
@Override
public URI getURI() throws IOException {
return this.file.toURI();
}
.......
.......
}</span>
可以看到该类实现了,Resource中的方法,并把path作为file,进行了流的输入。
BeanDefinitionRead
BeanDefinitionRead实际上是对我们从Resource中返回的InputStream操作,返回Spring中beanfactory可以读取的BeanDefinition
它主要有两个子类PropertiesBeanDefinitionReader, XmlBeanDefinitionReader(实际上这两个子类是实现BeanDefinitionReader的一个子接口AbstractBeanDefinitionReader)
对Resource的加载是通过loadBeanDefinitions方法来实现的。
BeanDefinitionRead源码
public interface BeanDefinitionReader {
/**
* Return the bean factory to register the bean definitions with.
*/
BeanDefinitionRegistry getRegistry();
/**
* Return the resource loader to use for resource locations.
*/
ResourceLoader getResourceLoader();
/**
* Return the class loader to use for bean classes.
*/
ClassLoader getBeanClassLoader();
/**
* Return the BeanNameGenerator to use for anonymous beans
*/
BeanNameGenerator getBeanNameGenerator();
/**
* Load bean definitions from the specified resource.
*/
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
/**
* Load bean definitions from the specified resources.
*/
int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
/**
* Load bean definitions from the specified resource location.
*/XmlBeanDefinitionReader
int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
/**
* Load bean definitions from the specified resource locations.
*/
int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}
</span>
XmlBeanDefinitionReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
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(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
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(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}</span>
可以看到从Resource读取到inputstream后,又调用了doLoadBeanDefinitions,对读取到的XML流进行了处理。
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);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}</span>
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
/**
* Role hint indicating that a {@code BeanDefinition} is providing an
*/
int ROLE_INFRASTRUCTURE = 2;
/**
* Return the name of the parent definition of this bean definition, if any.
*/
String getParentName();
/**
* Set the name of the parent definition of this bean definition, if any.
*/
void setParentName(String parentName);
/**
* Return the current bean class name of this bean definition.
*/
String getBeanClassName();
/**
* Override the bean class name of this bean definition.
*/
void setBeanClassName(String beanClassName);
/**
* Return the factory bean name, if any.
*/
String getFactoryBeanName();
/**
* Specify the factory bean to use, if any.
*/
void setFactoryBeanName(String factoryBeanName);
/**
* Return a factory method, if any.
*/
String getFactoryMethodName();
/**
* Specify a factory method, if any. This method will be invoked with
* constructor arguments, or with no arguments if none are specified.
* The method will be invoked on the specified factory bean, if any,
* or otherwise as a static method on the local bean class.
*/
void setFactoryMethodName(String factoryMethodName);
/**
* Return the name of the current target scope for this bean,
*/
String getScope();
/**
* Override the target scope of this bean, specifying a new scope name.
*/
void setScope(String scope);
/**
* Return whether this bean should be lazily initialized, i.e. not
* eagerly instantiated on startup. Only applicable to a singleton bean.
*/
boolean isLazyInit();
/**
* Set whether this bean should be lazily initialized.
*/
void setLazyInit(boolean lazyInit);
/**
* Return the bean names that this bean depends on.
*/
String[] getDependsOn();
/**
* Set the names of the beans that this bean depends on being initialized.
* The bean factory will guarantee that these beans get initialized first.
*/
void setDependsOn(String[] dependsOn);
/**
* Return whether this bean is a candidate for getting autowired into some other bean.
*/
boolean isAutowireCandidate();
/**
* Set whether this bean is a candidate for getting autowired into some other bean.
*/
void setAutowireCandidate(boolean autowireCandidate);
/**
* Return whether this bean is a primary autowire candidate.
*/
boolean isPrimary();
/**
* Set whether this bean is a primary autowire candidate.
*/
void setPrimary(boolean primary);
/**
* Return the constructor argument values for this bean.
*/
ConstructorArgumentValues getConstructorArgumentValues();
/**
* Return the property values to be applied to a new instance of the bean.
*/
MutablePropertyValues getPropertyValues();
/**
* Return whether this a <b>Singleton</b>, with a single, shared instance
*/
boolean isSingleton();
/**
* Return whether this a <b>Prototype</b>, with an independent instance
*/
boolean isPrototype();
/**
* Return whether this bean is "abstract", that is, not meant to be instantiated.
*/
boolean isAbstract();
/**
* Get the role hint for this {@code BeanDefinition}. The role hint
*/
int getRole();
/**
* Return a human-readable description of this bean definition.
*/
String getDescription();
/**
* Return a description of the resource that this bean definition
*/
String getResourceDescription();
/**
* Return the originating BeanDefinition, or {@code null} if none.
*/
BeanDefinition getOriginatingBeanDefinition();
}
ApplicationContext
从名字来看叫应用上下文,是和应用环境息息相关的。没错这个就是我们平时开发中经常直接使用打交道的一个类,应用上下文,或者也叫做spring容器。其实它的基本实现是会持有一个BeanFactory对象,并基于此提供一些包装和功能扩展。为什么要这么做呢?因为BeanFactory实现了一个容器基本结构和功能,但是与外部环境隔离。那么读取配置文件,并将配置文件解析成BeanDefinition,然后注册到BeanFactory的这一个过程的封装自然就需要ApplicationContext。ApplicationContext和应用环境细细相关,常见实现有ClasspathXmlApplicationContext,FileSystemXmlApplicationContext,WebApplicationContext等。Classpath、xml、FileSystem、Web等词都代表了应用和环境相关的一些意思,从字面上不难理解各自代表的含义。
当然ApplicationContext和BeanFactory的区别远不止于此,有:
1. 资源访问功能:在Resource和ResourceLoader的基础上可以灵活的访问不同的资源。
2. 支持不同的信息源。
3. 支持应用事件:继承了接口ApplicationEventPublisher,这样在上下文中为bean之间提供了事件机制。
以上5个组件基本代表了ioc容器的一个最基本组成,而组件的组合是放在ApplicationContext的实现这一层来完成。
以ClasspathXmlApplicationContext 容器实现为例
ClassPathXmlApplicationContext的refresh() 方法负责完成了整个容器的初始化
以下是Refresh的基本步骤:
1.把配置xml文件转换成resource。resource的转换是先通过ResourcePatternResolver来解析可识别格式的配置文件的路径
(如"classpath*:"等),如果没有指定格式,默认会按照类路径的资源来处理。
2.利用XmlBeanDefinitionReader完成对xml的解析,将xml Resource里定义的bean对象转换成统一的BeanDefinition。
3.将BeanDefinition注册到BeanFactory,完成对BeanFactory的初始化。BeanFactory里将会维护一个BeanDefinition的Map。
当getBean的时候就会根据调用BeanFactory,根据bean的BeanDifinition来实例化一个bean。当然根据bean的lazy-init、protetype等属性设置不同以上过程略有差别。
refresh()代码如下:
- public void refresh() throws BeansException, IllegalStateException {
- synchronized (this.startupShutdownMonitor) {
- // Prepare this context for refreshing.
- prepareRefresh();
- // Tell the subclass to refresh the internal bean factory.
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- // Prepare the bean factory for use in this context.
- prepareBeanFactory(beanFactory);
- try {
- // Allows post-processing of the bean factory in context subclasses.
- postProcessBeanFactory(beanFactory);
- // Invoke factory processors registered as beans in the context.
- invokeBeanFactoryPostProcessors(beanFactory);
- // Register bean processors that intercept bean creation.
- registerBeanPostProcessors(beanFactory);
- // Initialize message source for this context.
- initMessageSource();
- // Initialize event multicaster for this context.
- initApplicationEventMulticaster();
- // Initialize other special beans in specific context subclasses.
- onRefresh();
- // Check for listener beans and register them.
- registerListeners();
- // Instantiate all remaining (non-lazy-init) singletons.
- finishBeanFactoryInitialization(beanFactory);
- // Last step: publish corresponding event.
- finishRefresh();
- }
- catch (BeansException ex) {
- // Destroy already created singletons to avoid dangling resources.
- beanFactory.destroySingletons();
- // Reset 'active' flag.
- cancelRefresh(ex);
- // Propagate exception to caller.
- throw ex;
- }
- }
- }
以上的obtainFreshBeanFactory是很关键的一个方法,里面会调用loadBeanDefinition方法,如下:
- protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
- // Create a new XmlBeanDefinitionReader for the given BeanFactory.
- XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
- // Configure the bean definition reader with this context's
- // resource loading environment.
- beanDefinitionReader.setResourceLoader(this);
- beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
- // Allow a subclass to provide custom initialization of the reader,
- // then proceed with actually loading the bean definitions.
- initBeanDefinitionReader(beanDefinitionReader);
- loadBeanDefinitions(beanDefinitionReader);
- }
LoadBeanDifinition方法很关键,这里特定于整个IOC容器,实例化了一个XmlBeanDefinitionReader来解析Resource文件。关于Resource文件如何初始化和xml文件如何解析都在