文章目录
前言
控制反转(Inversion of Control),也称为依赖注入,许多应用都是由两个或多个类通过彼此的合作来实现业务逻辑的,这使得每个对象都需要与其依赖的对象相互引用。如果这个获取过程要靠自身实现,就会导致代码高度耦合并且难以测试。这对复杂的面向对象系统的设计是非常不利的。所以我们把这些交给框架去做是非常有价值的。
IoC的实现方式有很多种,在Spring中,IoC容器就是实现这个模式的载体,它在对象生成或初始化的时候直接将数据注入到对象中,也可以通过对象引用注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层导入。这种方案简化了对象依赖关系的管理,很大程度简化了面向对象系统的复杂性。
一、BeanFactory
public interface BeanFactory {
/*
用于解除对FactoryBean实例的引用,并将其与由FactoryBean创建的bean。 例如,如果bean被命名为
myObject是一个FactoryBean,获取代码&myObject将返回工厂,而不是由工厂返回的实例。
*/
String FACTORY_BEAN_PREFIX = "&";
// 这个方法时IoC容器API的主要方法,通过它可以取得IoC容器中管理的Bean。
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 容器中是否包含此名的Bean
boolean containsBean(String name);
// Bean是否是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// Bean是否是原型
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
// Bean是否是指定Class类
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
// Bean的Class类型
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
// Bean的所有别名
String[] getAliases(String name);
}
我们可以发现,直接的BeanFactory实现就是IoC容器的基本形式,而各种ApplicationContext的实现是IoC容器的高级表现形式。
BeanFactory接口在Spring的IoC容器中是一个最基本的接口类。
DefaultListableBeanFactory与ClassPathXmlApplicationContext都可以看成容器附加了某种功能的具体实现。
二、容器的初始化
我们可以通过如下步骤去初始化容器,
public class Test {
public static void main(String[] args) {
// 创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。
ClassPathResource classPathResource = new ClassPathResource("spring-config.xml");
// 创建一个BeanFactory,这里使用DefaultListableBeanFactory。
// 这是最基础的IoC容器,实现接口BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XMLBeanDefinition文件形式的BeanFactory。
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
// 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成整个载入和注册Bean定义之后
// 需要的容器就建立起来了,这个时候就可以直接使用IoC容器了。
reader.loadBeanDefinitions(classPathResource);
}
}
- Resource定位,它由ResourceLoader通过统一的Resource接口来完成。
- BeanDefinition的载入,它把用户定义好的Bean表示成IoC容器内部的数据结构BeanDefinition。
- 向IoC容器注册这些BeanDefinition,在IoC容器内部将BeanDefinition注入到一个HashMap中,通过HashMap来持有这些BeanDefinition数据。
对于Spring中IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,它就是容器实现依赖反转功能的核心数据结构。
Spring把这三个过程分开使用不同的模块来完成就是为了让用户能够更加灵活地对这三个过程进行扩展,定义出适合自己的IoC容器初始化过程。
也可以通过它去初试化容器
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext cp = new ClassPathXmlApplicationContext("spring-config.xml");
}
}
我们能看出来使用ClassPathXmlApplicationContext相对于直接使用DefaultListableBeanFactory的好处。
在ApplicationContext中,Spring已经为我们提供了一系列加载不同Rescoure的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IoC容器,需要为它配置特定的读取器才能完成这些功能。
三、ClassPathXmlApplicationContext的实现
下面以ClassPathXmlApplicationContext 为例,分析这个ApplicationContext的实现来看看它是怎样完成这个Resource定位过程的。
1.BeanFactory的创建
2.BeanDefinition的载入
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 这里创建了Xml的BeanDefinitionReader,并通过回调设置到BeanFactory中去
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 这里设置XMLBeanDefinitionReader,为XmlBeanDefinitionReader配ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 这里启动Bean定义信息载入的过程
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
进入loadBeanDefinitions后,找到doloadBeanDefinitions()方法可以看到载入过程。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// 省略....
try {
// 这里得到XML文件,并得到IO的inputStream 准备进行读取
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();
}
}
// 省略....
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
//这里启动的是对BeanDefinition解析的详细过程,这个解析会使用到Spring的Bean配置规则。
return registerBeanDefinitions(doc, resource);
}
// 省略....
}
进入registerBeanDefinitions后,找到doregisterBeanDefinitions()方法可以看到注册过程。
然后在这里面解析不同的xml标签
3.BeanDefinition在IoC容器中的注册
此时这些数据还不能供IoC使用,需要在IoC容器中对这些BeanDefinition数据进行注册,这个注册使得容器可以更友好的使用它。
在DefaultListableBeanFactory中,是通过一个HashMap来持有载入的BeanDefinition的。
可以看到BeanDefinitionMap中已经有我们定义好的数据结构了。
完成了BeanDefinition的注册,就算是完成了IoC容器的初始化过程,现在IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而这些BeanDefinition已经可以被容器使用,它们都在BeanDefinitionMap里被检索和使用。
容器的作用就是对这些信息进行处理和维护,这些信息就是容器建立以来反转的基础。
总结
本文主要介绍了Spring中BeanFactory接口,ApplicationContext容器的初始化流程及BeanDefinition的载入过程。
后文将分析IoC的依赖注入。