深入研究Spring-IoC:原始碼分析容器建立
1.前言
從之前的分析中可以知道IOC容器的建立大致分為3步:Resource定位、BeanDefinition載入解析、向容器註冊BeanDefinition。
Tiny-spring手動實現了Spring框架,通過對這個原始碼的解讀可以更好更有效的理解Spring。
2.容器建立的硬編碼
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean ("helloWorldService");
helloWorldService.helloWorld();
這是我手動建立ioc容器最常見的方式了,但是這段程式碼底層是如何實現的呢?下面通過tiny-spirng原始碼分析一下。
回到最初:
BeanDefinition的Resource定位:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
Resource的介面實現類ClassPathXmlApplicationContext並不能直接被BeanFatory的簡單實現類呼叫,因為ClassPathXmlApplicationContext讀取了xml後是由BeanDefnintionReader來處理的。除非是實現了ResourceLoader介面,也就是ApplicationContext及其子類。
BeanDefinition的載入和BeanDefinition註冊往下看:
BeanFactory基本容器介面:
public interface BeanFactory {
Object getBean(String name) throws Exception;
}
ApplicationContext繼承BeanFatory:
public interface ApplicationContext extends BeanFactory {
}
AbstractApplicationContext實現了ApplicationContext介面:
public abstract class AbstractApplicationContext implements ApplicationContext {
protected AbstractBeanFactory beanFactory;
public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void refresh() throws Exception {
loadBeanDefinitions(beanFactory);
registerBeanPostProcessors(beanFactory);
onRefresh();
}
protected abstract void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception;
protected void registerBeanPostProcessors(AbstractBeanFactory beanFactory) throws Exception {
List beanPostProcessors = beanFactory.getBeansForType(BeanPostProcessor.class);
for (Object beanPostProcessor : beanPostProcessors) {
beanFactory.addBeanPostProcessor((BeanPostProcessor) beanPostProcessor);
}
}
protected void onRefresh() throws Exception{
beanFactory.preInstantiateSingletons();
}
@Override
public Object getBean(String name) throws Exception {
return beanFactory.getBean(name);
}
}
ClassPathXmlApplicationContext繼承AbstractApplicationContext:
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
private String configLocation;
public ClassPathXmlApplicationContext(String configLocation) throws Exception {
this(configLocation, new AutowireCapableBeanFactory());
}
public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
super(beanFactory);
this.configLocation = configLocation;
refresh();
}
@Override
protected void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
}
}
XmlBeanDefinitionReader處理類(核心):
package us.codecraft.tinyioc.beans.xml;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import us.codecraft.tinyioc.BeanReference;
import us.codecraft.tinyioc.beans.AbstractBeanDefinitionReader;
import us.codecraft.tinyioc.beans.BeanDefinition;
import us.codecraft.tinyioc.beans.PropertyValue;
import us.codecraft.tinyioc.beans.io.ResourceLoader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
/**
* @author [email protected]
*/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
super(resourceLoader);
}
@Override
public void loadBeanDefinitions(String location) throws Exception {
InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
doLoadBeanDefinitions(inputStream);
}
protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
// 解析bean
registerBeanDefinitions(doc);
inputStream.close();
}
public void registerBeanDefinitions(Document doc) {
Element root = doc.getDocumentElement();
parseBeanDefinitions(root);
}
protected void parseBeanDefinitions(Element root) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
processBeanDefinition(ele);
}
}
}
protected void processBeanDefinition(Element ele) {
String name = ele.getAttribute("id");
String className = ele.getAttribute("class");
BeanDefinition beanDefinition = new BeanDefinition();
processProperty(ele, beanDefinition);
beanDefinition.setBeanClassName(className);
getRegistry().put(name, beanDefinition);
}
private void processProperty(Element ele, BeanDefinition beanDefinition) {
NodeList propertyNode = ele.getElementsByTagName("property");
for (int i = 0; i < propertyNode.getLength(); i++) {
Node node = propertyNode.item(i);
if (node instanceof Element) {
Element propertyEle = (Element) node;
String name = propertyEle.getAttribute("name");
String value = propertyEle.getAttribute("value");
if (value != null && value.length() > 0) {
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
} else {
String ref = propertyEle.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("Configuration problem: <property> element for property '"
+ name + "' must specify a ref or value");
}
BeanReference beanReference = new BeanReference(ref);
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference));
}
}
}
}
}
3.分析
當執行:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
ClassPathXmlApplicationContext的構造方法為:
public ClassPathXmlApplicationContext(String configLocation) throws Exception {
this(configLocation, new AutowireCapableBeanFactory());
}
public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
super(beanFactory);
this.configLocation = configLocation;
refresh();
}
其中configLocation為傳入xml定位,BeanFatory指定了容器的可自動配置。最後調refresh()方法。
public void refresh() throws Exception {
loadBeanDefinitions(beanFactory);
registerBeanPostProcessors(beanFactory);
onRefresh();
}
在refresh()方法中呼叫了loadBeanDefinitions(beanFactory)和 registerBeanPostProcessors(beanFactory)方法。先看loadBeanDefinitions(beanFactory)方法的實現:
@Override
protected void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
}
底層中是呼叫了XmlBeanDefinitionReader來讀取xml內的bean.loadBeanDefinitions方法是用io流inputStream 讀取資源。
@Override
public void loadBeanDefinitions(String location) throws Exception {
InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
doLoadBeanDefinitions(inputStream);
}
doLoadBeanDefinitions(inputStream)方法將讀取的資源轉換成Document物件,通過registerBeanDefinitions(Document),解析其中的每個Elements。然後得到BeanDefinition然後註冊。
protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
// 解析bean
registerBeanDefinitions(doc);
inputStream.close();
}
public void registerBeanDefinitions(Document doc) {
Element root = doc.getDocumentElement();
parseBeanDefinitions(root);
}
protected void parseBeanDefinitions(Element root) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
processBeanDefinition(ele);
}
}
}
protected void processBeanDefinition(Element ele) {
String name = ele.getAttribute("id");
String className = ele.getAttribute("class");
BeanDefinition beanDefinition = new BeanDefinition();
processProperty(ele, beanDefinition);
beanDefinition.setBeanClassName(className);
getRegistry().put(name, beanDefinition);
}
4.總結
短短的一行程式碼,卻包含了太多的東西:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
ClassPathXmlApplicationContext構造方法中指定了url和實現BeanFatory介面的容器–AutowireCapableBeanFactory。呼叫refresh()方法時加載出來了XmlBeanDefinitionReader和registerBeanPostProcessors方法。
通過XmlBeanDefinitionReader將xml中的bean轉換成了BeanDefinition並註冊;
通過registerBeanPostProcessors介面幫助我們,當需要在Spring容器完成Bean的例項化、配置和其他的初始化前後新增一些自己的邏輯處理,我們就可以定義一個或者多個BeanPostProcessor介面的實現,然後註冊到容器中。
讀原始碼總是能看到更多的東西。
學習資料git《tiny-Spring》