美文网首页Spring IOC
spring 知识整理(二):spring ioc 的一个简单例

spring 知识整理(二):spring ioc 的一个简单例

作者: beldon_wu | 来源:发表于2018-07-09 10:22 被阅读0次

    为了使用ioc,先撇开我们常用的ApplicationContext,来个简单版的BeanFactory

    例子

    先建一个maven项目

    • 添加SimpleBean

    beldon.learn.ioc.sample.SimpleBean

    package beldon.learn.ioc.sample;
    public class SimpleBean {
        // something
    }
    
    • 在resource 下添加个 sample.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="simpleBean" class="beldon.learn.ioc.sample.SimpleBean">
        </bean>
    </beans>
    
    • 添加个SampleApplication

    beldon.learn.ioc.sample.SampleApplication

    package beldon.learn.ioc.sample;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    public class SampleApplication {
        public static void main(String[] args) {
            Resource resource = new ClassPathResource("sample.xml");
            XmlBeanFactory factory = new XmlBeanFactory(resource);
            System.out.println(factory.containsBean("simpleBean"));
            SimpleBean bean = factory.getBean(SimpleBean.class);
            System.out.println(bean);
        }
    }
    

    运行结果

    true
    beldon.learn.ioc.sample.SimpleBean@d7b1517
    

    上面代码简单地使用简单的spring ioc容器,先用ClassPathResource定位了sample.xml、然后通过XmlBeanFactory的构造方法把resouce传递进去,最后就可以通过XmlBeanFactory 来获取定义的bean了。

    源码

    先来看下XmlBeanFactory源码。

    package beldon.learn.ioc.sample;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    public class XmlBeanFactory extends DefaultListableBeanFactory {
        private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
        public XmlBeanFactory(Resource resource) throws BeansException {
            this(resource, null);
        }
    
        public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
            super(parentBeanFactory);
            //被内部的reader去加载。
            this.reader.loadBeanDefinitions(resource);
        }
    }
    

    XmlBeanFactory是继承DefaultListableBeanFactory,实现就简单的几行代码,其通过构造方法参数传输进去的Resource是被内部的XmlBeanDefinitionReader去加载。

    也就是说,如果我们不用XmlBeanFactory的话,自己也可以手动写

    package beldon.learn.ioc.sample;
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionReader;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    
    public class SampleApplication {
        public static void main(String[] args) {
            //资源定位
            Resource resource = new ClassPathResource("sample.xml");
            BeanDefinitionRegistry registry = new DefaultListableBeanFactory();
            BeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
            //加载BeanDefinitions
            reader.loadBeanDefinitions(resource);
            BeanFactory beanFactory = (BeanFactory) registry;
            System.out.println(beanFactory.containsBean("simpleBean"));
            SimpleBean bean = beanFactory.getBean(SimpleBean.class);
            System.out.println(bean);
        }
    }
    
    

    从上面代码可以看出,简单的可以看出实现一个spring ioc,主要是Resource资源定位和BeanDefinitionReader加载beanDefinition的一个过程,而BeanDefinitionReader需要一个BeanDefinitionRegistry来完成beanDefinition的注册。

    注:至于ResourceBeanDefinitionRegistryBeanDefinitionReader的说明,可以看上一篇文章

    代码研究

    从名字及注释上看,XmlBeanDefinitionReader的作用就是把xml解析为BeanDefinition

    跟踪XmlBeanDefinitionReader可以看到

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    

    XmlBeanDefinitionReader最终的让一个DefaultBeanDefinitionDocumentReader去读取bean信息并注册BeanDefinition。继续追踪下去。

    org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
    
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                    "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
    
        preProcessXml(root);
        //真正解析BeanDefinitions
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);
    
        this.delegate = parent;
    }
    

    从上面代码可以看出,真正解析的是parseBeanDefinitions(root, this.delegate);。继续跟下去会发现.

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                                         bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
    

    DefaultBeanDefinitionDocumentReader最终是委派了一个BeanDefinitionParserDelegate来解析xml元素,把解析处来的BeanDefinitionHolder

    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    

    来注册。而getReaderContext().getRegistry()拿到的BeanDefinitionRegistry就是我们一开始传进去的DefaultListableBeanFactory

    再看下 BeanDefinitionReaderUtils.registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException;

    public static void registerBeanDefinition(
                BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
                throws BeanDefinitionStoreException {
        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }
    

    可以从上面代码可以看出,最终是调用BeanDefinitionRegistryvoid registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;来进行BeanDefinition注册。

    总结

    上面只是简单跟踪了下XmlBeanDefinitionReader,目的是为了清楚spring ioc的加载的一个过程。这个过程主要分为三个步骤,分别是定位、加载,注册。

    • 定位

    定位主要是org.springframework.core.io.Resource的资源定位。ClassPathResource就是一个例子,除此之外还有FileSystemResourcePathResourceUrlResourceInputStreamResource等等。

    • 加载

    加载其实就是把用户定义bean的外部资源转换成spring ioc可以读的数据结构,也就是转换成BeanDefinition,上面sample的XmlBeanFactory就是通过xml中的bean标签来解析成BeanDefinition

    • 注册

    注册就是上一步转成的BeanDefinition通过BeanDefinitionRegistry来注册。

    相关文章

      网友评论

        本文标题:spring 知识整理(二):spring ioc 的一个简单例

        本文链接:https://www.haomeiwen.com/subject/gtgouftx.html