上篇文章介紹了Spring的相關基礎概念,我們瞭解到Spring Framework提供的兩個基礎功能就是IOC和AOP。關於IOC容器我們分爲兩篇文章來介紹,本篇文章會介紹IOC容器的基礎概念,並自定義實現一個基礎的IOC容器,幫助我們後續更好的解讀IOC源碼。AOP會在之後的文章中介紹。
1. IOC容器基本概念
IOC(Inversion Of Control)也叫控制反轉,這個概念經常會伴隨另一個概念DI(Dependence Injection)依賴注入出現。直白的講就是,Java中一個對象依賴另一個對象,當我們使用這個對象時,需要主動去創建或查找這個依賴的對象,而IOC就是藉助容器在對象初始化時主動將依賴傳遞給它。通過容器初始化對象的過程,就是發生了控制反轉,因爲對象初始化的控制的控制由程序員反轉到容器。同時容器初始化對象的過程就是根據對象的依賴,主動將對象依賴注入的,也就是DI的概念。
這時,我們就比較好了解IOC容器的概念了:IOC容器首先是個容器,內部持有很多對象,並管理着這些對象的生命週期,支持對象的查找和依賴決策,以及配置管理等其他功能。
舉個例子,在做項目時,需要其他多個部門的人提供API,一般都是我分別主動找相關部門的人溝通讓他們提供。假如有個項目經理,我給他一份API需求文檔,等我需要使用API時,發現已經提供好了可以直接使用了。從這個例子可以發現,因爲項目經理的存在,我不用直接跟各個部門去對接了,只需要一個API需求文檔給到項目經理就可以了,當我的需求變更時,只需要更新一下API需求文檔,項目經理就會爲我提供新的API。可以看到,項目經理的存在,最直接的好處就是,我跟對方部門解耦了,簡化了我的工作。而這個項目經理的角色就相當於IOC容器,我給項目經理的API需求文檔,就是IOC容器依賴注入的根據配置文件。
2. 自定義實現IOC容器
通過上面的介紹,我們應該可以瞭解到IOC容器的需要完成以下兩件工作:
- 實例化對象
- 根據配置,解讀對象的依賴,並填充對象的相關依賴
現在我們以xml文件爲配置信息載體,構建一個公司的Demo。公司(Company)開業有名字(name),也要有員工(Employee)。
2.1 業務類定義
- Company
package com.zhuoli.service.min.spring.ioc.bean;
public class Company {
private String name;
private Employee employee;
public void open(){
System.out.println("Company " + name + " is open.");
employee.work();
}
}
- Employee
package com.zhuoli.service.min.spring.ioc.bean;
public class Employee {
private String name;
public void work(){
System.out.println("Employee " + name + " is working");
}
}
- 配置文件min-ioc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="company" class="com.zhuoli.service.min.spring.ioc.bean.Company">
<property name="name" value="Apple"></property>
<property name="employee" ref="employee"></property>
</bean>
<bean id="employee" class="com.zhuoli.service.min.spring.ioc.bean.Employee">
<property name="name" value="zhuoli"></property>
</bean>
</beans>
2.2 Spring IOC實現
2.2.1 配置文件讀取
因爲我們是通過xml配置文件通知自定義IOC容器所需的Java對象以及其依賴(本文中之後叫做bean),所以自定義IOC容器首先要支持xml文件的讀取,將文件讀取爲字節流。這裏我們定義一個Resource接口:
package com.zhuoli.service.min.spring.ioc.rsource;
import java.io.IOException;
import java.io.InputStream;
public interface Resource {
InputStream getInputStream() throws IOException;
}
通常資源文件都會放在classpath路徑下,實現一個ClassPathResource類可以讀取classpath的文件:
package com.zhuoli.service.min.spring.ioc.rsource;
import java.io.InputStream;
public class ClassPathResource implements Resource {
private String path;
private ClassLoader classLoader;
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}
public ClassPathResource(String path, ClassLoader classLoader) {
this.path = path;
this.classLoader = (classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader());
}
@Override
public InputStream getInputStream() {
return classLoader.getResourceAsStream(path);
}
}
資源也可以通過其他方式定義,比如絕對路徑或者URL的方式,因而需要一個Resource的處理器ResourceLoader。當前我們就只處理classpath方式:
package com.zhuoli.service.min.spring.ioc.rsource;
public class ResourceLoader {
final String CLASSPATH_URL_PREFIX = "classpath:";
public Resource getResource(String location) {
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
} else {
return new ClassPathResource(location);
}
}
}
這樣就可以通過ResourceLoader來獲得min-ioc.xml文件的字節流:
InputStream inputStream = new ResourceLoader().getResource("classpath:min-ioc.xml").getInputStream;
2.2.2 解析xml配置的bean依賴
有了min-ioc.xml文件的輸入流,就可以進行bean及bean關係的解析,在解析之前,我們需要一個數據結構存儲解析後的數據。爲什麼不直接存儲對象?因爲spring中可以設置lazy-init=true,從而在真正需要bean對象時才創建;另一個是spring中可以設置bean的scope爲prototype,也就是bean不是單例的,每次獲取的都是一個新的bean對象。這樣就要求我們只能存儲bean的數據結構,而不能直接存儲bean對象。所以xml配置文件解析的存儲對象至少需要包含以下幾部分:
- bean名稱
- bean的Class包路徑或Class對象
- 依賴屬性
xml配置文件bean解析的數據結構BeanDefinition:
package com.zhuoli.service.min.spring.ioc.beans;
public class BeanDefinition {
// bean名稱
private String beanName;
// bean的class對象
private Class beanClass;
// bean的class的包路徑
private String beanClassName;
// bean依賴屬性
private PropertyValues propertyValues = new PropertyValues();
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
public String getBeanClassName() {
return beanClassName;
}
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
}
public PropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
}
其中PropertyValues用來存儲依賴屬性:
package com.zhuoli.service.min.spring.ioc.beans;
import java.util.LinkedList;
import java.util.List;
public class PropertyValues {
private List<PropertyValue> propertyValues = new LinkedList<>();
public void addPropertyValue(PropertyValue propertyValue) {
propertyValues.add(propertyValue);
}
public List<PropertyValue> getPropertyValues() {
return this.propertyValues;
}
}
package com.zhuoli.service.min.spring.ioc.beans;
public class PropertyValue {
private final String name;
private final Object value;
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
BeanDefinition讀取:
定義一個接口,該接口功能就是將指定位置的配置文件轉化爲BeanDefinition:
package com.zhuoli.service.min.spring.ioc.xml;
public interface BeanDefinitionReader {
void loadBeanDefinitions(String location) throws Exception;
}
實現xml文件的BeanDefinition讀取類XmlBeanDefinitionReader:
package com.zhuoli.service.min.spring.ioc.xml;
import com.zhuoli.service.min.spring.ioc.beans.BeanDefinition;
import com.zhuoli.service.min.spring.ioc.beans.BeanDefinitionRegistry;
import com.zhuoli.service.min.spring.ioc.beans.BeanReference;
import com.zhuoli.service.min.spring.ioc.beans.PropertyValue;
import com.zhuoli.service.min.spring.ioc.rsource.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
// BeanDefinition註冊到BeanFactory接口
private BeanDefinitionRegistry registry;
// 資源載入類
private ResourceLoader resourceLoader;
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
this.registry = registry;
this.resourceLoader = resourceLoader;
}
@Override
public void loadBeanDefinitions(String location) throws Exception {
InputStream is = getResourceLoader().getResource(location).getInputStream();
doLoadBeanDefinitions(is);
}
public BeanDefinitionRegistry getRegistry() {
return registry;
}
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
protected void doLoadBeanDefinitions(InputStream is) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(is);
registerBeanDefinitions(document);
is.close();
}
protected void registerBeanDefinitions(Document document) {
Element root = document.getDocumentElement();
parseBeanDefinitions(root);
}
protected void parseBeanDefinitions(Element root) {
NodeList nodeList = root.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node item = nodeList.item(i);
if (item instanceof Element) {
Element ele = (Element) item;
processBeanDefinition(ele);
}
}
}
protected void processBeanDefinition(Element ele) {
String name = ele.getAttribute("id");
String className = ele.getAttribute("class");
if (className == null || className.length() == 0) {
throw new IllegalArgumentException("Configuration exception: <bean> element must has class attribute.");
}
if (name == null || name.length() == 0) {
name = className;
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName(className);
processBeanProperty(ele, beanDefinition);
getRegistry().registerBeanDefinition(name, beanDefinition);
}
protected void processBeanProperty(Element ele, BeanDefinition beanDefinition) {
NodeList children = ele.getElementsByTagName("property");
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node instanceof Element) {
Element property = (Element) node;
String name = property.getAttribute("name");
String value = property.getAttribute("value");
if (value != null && value.length() > 0) {
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
} else {
String ref = property.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("Configuration problem: <property> element for " +
name + " must specify a value or ref.");
}
BeanReference reference = new BeanReference(ref);
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, reference));
}
}
}
}
}
XmlBeanDefinitionReader的構造參數傳入資源文件的載入類和BeanDefinition註冊接口實現類,用來定位xml文件和註冊BeanDefinition,所以在XmlBeanDefinitionReader中實現了Bean的定位,解析和之後的註冊。
在loadBeanDefinitions方法中,通過ResourceLoader獲取到xml文件的InputStream,完成了定位操作,解析操作轉到doLoadBeanDefinitions方法中。
protected void doLoadBeanDefinitions(InputStream is) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(is);
registerBeanDefinitions(document);
is.close();
}
protected void registerBeanDefinitions(Document document) {
Element root = document.getDocumentElement();
parseBeanDefinitions(root);
}
DOM方式解析xml,拿到Document對象,再拿到根節點,轉到parseBeanDefinitions解析具體的 <beans> 信息。
protected void parseBeanDefinitions(Element root) {
NodeList nodeList = root.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node item = nodeList.item(i);
if (item instanceof Element) {
Element ele = (Element) item;
processBeanDefinition(ele);
}
}
}
protected void processBeanDefinition(Element ele) {
String name = ele.getAttribute("id");
String className = ele.getAttribute("class");
if (className == null || className.length() == 0) {
throw new IllegalArgumentException("Configuration exception: <bean> element must has class attribute.");
}
if (name == null || name.length() == 0) {
name = className;
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName(className);
processBeanProperty(ele, beanDefinition);
getRegistry().registerBeanDefinition(name, beanDefinition);
}
遍歷<beans>下的每一個<bean>節點,獲取屬性id和class的值,分別對應BeanDefinition的beanName和BeanClassName。然後解析依賴屬性<property>標籤:
protected void processBeanProperty(Element ele, BeanDefinition beanDefinition) {
NodeList children = ele.getElementsByTagName("property");
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node instanceof Element) {
Element property = (Element) node;
String name = property.getAttribute("name");
String value = property.getAttribute("value");
if (value != null && value.length() > 0) {
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
} else {
String ref = property.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("Configuration problem: <property> element for " +
name + " must specify a value or ref.");
}
BeanReference reference = new BeanReference(ref);
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, reference));
}
}
}
}
如果<property>中存在value屬性,則作爲String類型存儲,如果是ref屬性,則創建BeanReference對象存儲。
package com.zhuoli.service.min.spring.ioc.beans;
public class BeanReference {
private String ref;
public BeanReference(String ref) {
this.ref = ref;
}
public String getRef() {
return ref;
}
}
最後註冊BeanDefinition到存儲BeanDefinition的地方,我們可以猜測肯定有一個Map<String, BeanDefinition>。
getRegistry().registerBeanDefinition(name, beanDefinition);
2.2.3 註冊BeanDefinition
上面的getRegistry()獲得的就是XmlBeanDefinitionReader構造時傳入的BeanDefinitionRegistry實現類。首先BeanDefinitionRegistry是個接口:
package com.zhuoli.service.min.spring.ioc.beans;
public interface BeanDefinitionRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
}
它只定義了一個方法,就是註冊BeanDefinition。它的實現類就是要實現這個方法並將BeanDefinition註冊到Map<String,BeanDefinition>中。在這裏這個實現類是DefaultListableBeanFactory:
package com.zhuoli.service.min.spring.ioc.factory;
import com.zhuoli.service.min.spring.ioc.beans.BeanDefinition;
import com.zhuoli.service.min.spring.ioc.beans.BeanDefinitionRegistry;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class DefaultListableBeanFactory extends AbstractBeanFactory implements BeanDefinitionRegistry {
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
private List<String> beanDefinitionNames = new LinkedList<>();
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
beanDefinitionNames.add(beanName);
}
@Override
protected BeanDefinition getBeanDefinitionByName(String beanName) {
return beanDefinitionMap.get(beanName);
}
@Override
public void preInstantiateSingletons() throws Exception {
for (String beanName : beanDefinitionNames) {
getBean(beanName);
}
}
}
可以看出它包含一個beanDefinitionMap 和 beanDefinitionNames,一個記錄beanName和BeanDefinition的關係,一個記錄所有BeanName。而registerBeanDefinition方法就是將BeanDefinition添加到beanDefinitionMap和beanDefinitionNames。
2.2.4 獲取bean
DefaultListableBeanFactory實現了ConfigurableListableBeanFactory接口,而ConfigurableListableBeanFactory繼承了BeanFactory接口。BeanFactory是一個頂級接口:
package com.zhuoli.service.min.spring.ioc.factory;
public interface BeanFactory {
Object getBean(String beanName);
}
它定義了獲取bean對象的接口方法,實現這個方法的是AbstractBeanFactory抽象類:
package com.zhuoli.service.min.spring.ioc.factory;
import com.zhuoli.service.min.spring.ioc.beans.BeanDefinition;
import com.zhuoli.service.min.spring.ioc.beans.BeanReference;
import com.zhuoli.service.min.spring.ioc.beans.PropertyValue;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class AbstractBeanFactory implements ConfigurableListableBeanFactory {
private Map<String, Object> singleObjects = new ConcurrentHashMap<>();
@Override
public Object getBean(String beanName) {
Object singleBean = this.singleObjects.get(beanName);
if (singleBean != null) {
return singleBean;
}
BeanDefinition beanDefinition = getBeanDefinitionByName(beanName);
if (beanDefinition == null) {
throw new RuntimeException("bean for name '" + beanName + "' not register.");
}
singleBean = doCreateBean(beanDefinition);
this.singleObjects.put(beanName, singleBean);
return singleBean;
}
protected abstract BeanDefinition getBeanDefinitionByName(String beanName);
protected Object doCreateBean(BeanDefinition beanDefinition) {
Object bean = createInstance(beanDefinition);
applyPropertyValues(bean, beanDefinition);
return bean;
}
protected Object createInstance(BeanDefinition beanDefinition) {
try {
if (beanDefinition.getBeanClass() != null) {
return beanDefinition.getBeanClass().newInstance();
} else if (beanDefinition.getBeanClassName() != null) {
try {
Class clazz = Class.forName(beanDefinition.getBeanClassName());
beanDefinition.setBeanClass(clazz);
return clazz.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("bean Class " + beanDefinition.getBeanClassName() + " not found");
}
}
} catch (Exception e) {
throw new RuntimeException("create bean " + beanDefinition.getBeanName() + " failed");
}
throw new RuntimeException("bean name for " + beanDefinition.getBeanName() + " not define bean class");
}
protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) {
for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
String name = propertyValue.getName();
Object value = propertyValue.getValue();
if (value instanceof BeanReference) {
BeanReference reference = (BeanReference) value;
value = getBean(reference.getRef());
}
try {
Method method = bean.getClass().getDeclaredMethod("set" + name.substring(0, 1).toUpperCase() +
name.substring(1), value.getClass());
method.setAccessible(true);
method.invoke(bean, value);
} catch (Exception e) {
try {
Field field = bean.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(bean, value);
} catch (Exception e1) {
throw new RuntimeException("inject bean property " + name + " failed");
}
}
}
}
}
它定義了獲取bean對象的接口方法getBean,這個方法的實現是在AbstractBeanFactory抽象類:
@Override
public Object getBean(String beanName) {
Object singleBean = this.singleObjects.get(beanName);
if (singleBean != null) {
return singleBean;
}
BeanDefinition beanDefinition = getBeanDefinitionByName(beanName);
if (beanDefinition == null) {
throw new RuntimeException("bean for name '" + beanName + "' not register.");
}
singleBean = doCreateBean(beanDefinition);
this.singleObjects.put(beanName, singleBean);
return singleBean;
}
AbstractBeanFactory中定義了單實例(Demo默認都是單實例的bean)的beanName和bean對象關係的singleObjects的Map。getBean方法傳入beanName,先查詢singleObjects有沒有beanName的緩存,如果存在,直接返回;如果不存在則調用getBeanDefinitionByName獲取BeanDefinition(本質上是從Bean註冊中心獲取,而DefaultListableBeanFactory類實現了BeanDefinitionRegistry接口),DefaultListableBeanFactory類中實現了getBeanDefinitionByName方法:
@Override
protected BeanDefinition getBeanDefinitionByName(String beanName) {
return beanDefinitionMap.get(beanName);
}
拿到BeanDefinition後,進入真正創建bean的方法doCreateBean:
protected Object doCreateBean(BeanDefinition beanDefinition) {
Object bean = createInstance(beanDefinition);
applyPropertyValues(bean, beanDefinition);
return bean;
}
它做了兩個操作,一個是創建bean對象,另一個是注入依賴屬性:
protected Object createInstance(BeanDefinition beanDefinition) {
try {
if (beanDefinition.getBeanClass() != null) {
return beanDefinition.getBeanClass().newInstance();
} else if (beanDefinition.getBeanClassName() != null) {
try {
Class clazz = Class.forName(beanDefinition.getBeanClassName());
beanDefinition.setBeanClass(clazz);
return clazz.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("bean Class " + beanDefinition.getBeanClassName() + " not found");
}
}
} catch (Exception e) {
throw new RuntimeException("create bean " + beanDefinition.getBeanName() + " failed");
}
throw new RuntimeException("bean name for " + beanDefinition.getBeanName() + " not define bean class");
}
通過Class.forName加載bean的Class對象,再通過Class的newInstance方法反射生成bean對象。
protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) {
for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
String name = propertyValue.getName();
Object value = propertyValue.getValue();
if (value instanceof BeanReference) {
BeanReference reference = (BeanReference) value;
value = getBean(reference.getRef());
}
try {
Method method = bean.getClass().getDeclaredMethod("set" + name.substring(0, 1).toUpperCase() +
name.substring(1), value.getClass());
method.setAccessible(true);
method.invoke(bean, value);
} catch (Exception e) {
try {
Field field = bean.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(bean, value);
} catch (Exception e1) {
throw new RuntimeException("inject bean property " + name + " failed");
}
}
}
}
如果依賴屬性是對象的話,通過getBean獲取或生成依賴對象,並通過反射注入到bean對象中,這也就是依賴注入。對於以來的屬性是對象的情況,如果對象間存在循環依賴,我們實現的IOC容器是無法解決的,但是Spring的IOC容器可以完美地解決這個問題,我們會在後續的文章中介紹。至此,Bean的創建和獲取就完成了。
2.2.5 ApplicationContext
在我們日常使用spring時,一般不會直接用BeanFactory,而是通過一個更加高級的接口ApplicationContext來初始化容器以及提供其他企業級的功能。在我們的簡易IOC容器中,通過ApplicationContext來實例化所有的bean。
package com.zhuoli.service.min.spring.ioc.context;
import com.zhuoli.service.min.spring.ioc.factory.BeanFactory;
public interface ApplicationContext extends BeanFactory {
}
ApplicationContext沒有提供其他功能,只是繼承了BeanFactory。ApplicationContext的一個最終實現類ClasspathXmlApplicationContext:
package com.zhuoli.service.min.spring.ioc.context;
import com.zhuoli.service.min.spring.ioc.beans.BeanDefinitionRegistry;
import com.zhuoli.service.min.spring.ioc.rsource.ResourceLoader;
import com.zhuoli.service.min.spring.ioc.xml.XmlBeanDefinitionReader;
public class ClasspathXmlApplicationContext extends AbstractApplicationContext {
private String location;
public ClasspathXmlApplicationContext(String location) {
this.location = location;
try {
refresh();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void loadBeanDefinitions(BeanDefinitionRegistry registry) throws Exception {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry, new ResourceLoader());
reader.loadBeanDefinitions(location);
}
}
ClasspathXmlApplicationContext的構造函數裏接收傳入xml資源文件的路徑location,再調用refresh方法。refresh方法在AbstractApplicationContext抽象類中實現:
package com.zhuoli.service.min.spring.ioc.context;
import com.zhuoli.service.min.spring.ioc.beans.BeanDefinitionRegistry;
import com.zhuoli.service.min.spring.ioc.factory.ConfigurableListableBeanFactory;
import com.zhuoli.service.min.spring.ioc.factory.DefaultListableBeanFactory;
public abstract class AbstractApplicationContext implements ApplicationContext {
private ConfigurableListableBeanFactory beanFactory;
@Override
public Object getBean(String beanName) {
return beanFactory.getBean(beanName);
}
public void refresh() throws Exception{
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
onRefresh();
}
protected void onRefresh() throws Exception {
beanFactory.preInstantiateSingletons();
}
protected abstract void loadBeanDefinitions(BeanDefinitionRegistry registry) throws Exception;
}
refresh方法先創建一個DefaultListableBeanFactory,然後通過loadBeanDefinitions解析並註冊bean,真正的實現在ClasspathXmlApplicationContext中:
@Override
protected void loadBeanDefinitions(BeanDefinitionRegistry registry) throws Exception {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry, new ResourceLoader());
reader.loadBeanDefinitions(location);
}
就是通過XmlBeanDefinitionReader的loadBeanDefinitions方法來處理(加載xml配置解析爲BeanDefinition,註冊到Bean註冊中心BeanDefinitionRegistry),這在上面已經詳細的講過了,就不再贅述了。
refresh方法中最後調用onRefresh方法,初始化所有bean實例:
protected void onRefresh() throws Exception {
beanFactory.preInstantiateSingletons();
}
實際調用的是DefaultListableBeanFactory類,方法定義在ConfigurableListableBeanFactory接口中:
package com.zhuoli.service.min.spring.ioc.factory;
public interface ConfigurableListableBeanFactory extends BeanFactory{
void preInstantiateSingletons() throws Exception;
}
DefaultListableBeanFactory的實現:
@Override
public void preInstantiateSingletons() {
for (String beanName : beanDefinitionNames) {
getBean(beanName);
}
}
便利析出的所有beanDefinitionName,通過getBean方法獲取Bean,getBean方法上面已經介紹過,不多贅述了。
3. 測試
package com.zhuoli.service.min.spring.ioc.test;
import com.zhuoli.service.min.spring.ioc.bean.Company;
import com.zhuoli.service.min.spring.ioc.context.ApplicationContext;
import com.zhuoli.service.min.spring.ioc.context.ClasspathXmlApplicationContext;
public class CompanyApplicationContext {
public static void main(String[] args) {
ApplicationContext context = new ClasspathXmlApplicationContext("classpath:min-ioc.xml");
Company company = (Company) context.getBean("company");
company.open();
}
}
運行結果:
Company Apple is open.
Employee zhuoli is working
通過自定義實現的IOC容器,我們成功獲取了xml文件中配置的對象,說明自定義IOC容器是生效的。
4. 總結
上面介紹了很多類的概念,也許會有些亂,最後我們整理一下各個類的作用。
分別來梳理一下自定義IOC容器涉及的幾個類的作用:
Resource:
- Resource:接口,定義了獲取字節流的能力,getInputStream
- ClassPathResource:實現了Resource接口,可以獲取path指定文件的字節輸入流
- ResourceLoader:獲取Resource對象,是XmlBeanDefinitionReader類的成員變量,用於XmlBeanDefinitionReader類解析xml配置文件的基礎工作——將xml文件爲字節輸入流
BeanFactory:
- BeanFactory:接口,定義了通過beanName獲取bean對象的方法
- ConfigurableListableBeanFactory:繼承了BeanFactory接口,並拓展了初始化所有單例bean的方法preInstantiateSingletons
- AbstractBeanFactory:抽象類,繼承了ConfigurableListableBeanFactory,實現了BeanFactory接口中的getBean方法,該抽象類的作用是方便我們實現自定義的BeanFactory,通過繼承AbstractBeanFactory抽象類,我們只用實現ConfigurableListableBeanFactory接口定義的preInstantiateSingletons方法即可
- BeanDefinitionRegistry:接口,定義了註冊BeanDefinition的能力
- DefaultListableBeanFactory:繼承了AbstractBeanFactory類,實現了BeanDefinitionRegistry接口,所以可以說DefaultListableBeanFactory既是Bean工廠也是Ban註冊中心
- ApplicationContext:接口,繼承了BeanFactory接口,ApplicationContext也是bean工廠,沒有拓展BeanFactory的能力
- AbstractApplicationContext:抽象類,繼承了ApplicationContext接口,並持有一個ConfigurableListableBeanFactory成員變量,實現了getBean方法,具有初始化Bean工廠(初始化所有的bean)和獲取bean對象的能力
- ClasspathXmlApplicationContext:繼承了AbstractApplicationContext,具備AbstractApplicationContext的一切能力。所以可以通過ClasspathXmlApplicationContext初始化Bean工廠,獲取bean對象。
BeanDefinitionReader:
- BeanDefinitionReader:接口,定義了從指定位置加載BeanDefinition的方法
- XmlBeanDefinitionReader:實現了BeanDefinitionReader接口,持有ResourceLoader和BeanDefinitionRegistry兩個成員變量,分別用於在加載BeanDefinition時從指定位置加載字節輸入流、將BeanDefinition註冊到註冊中心。XmlBeanDefinitionReader的使用時機是上述ClasspathXmlApplicationContext類中實現AbstractApplicationContext抽象類定義的loadBeanDefinitions方法
@Override
protected void loadBeanDefinitions(BeanDefinitionRegistry registry) throws Exception {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry, new ResourceLoader());
reader.loadBeanDefinitions(location);
}
上述就是我們自定義實現的一個簡單地IOC容器,實現了IOC和DI的功能。其實Spring Framework也提供了IOC容器,基本思路與我們自定義實現的IOC容器一致,並且相關類的類名也是一致的(因爲我們自定義實現的IOC容器其實是對Spring IOC容器的精簡==)。只不過Spring實現的IOC容器提供了更多的功能呢,比如實現Bean的作用週期、延遲加載等功能。我們會在接下來的文章來介紹Spring IOC的實現。在理解了本文的基礎上,我們再研究Spring IOC的實現就會比較簡單。
參考鏈接:
1. Spring 源碼
2. 構建簡單IOC容器
3. 實現一個基本的IoC容器