1. 程式人生 > >深入研究Spring-IoC:原始碼分析容器建立

深入研究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》