您的位置:首页 > 编程语言 > Java开发

Spring源码阅读一——IOC

2015-11-26 10:28 351 查看

什么是IOC?

Inversion of Control (IoC) (DI).

It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.

知道了IOC,我们自然会想到 (注入的)对象,以及容器。两个概念

对象



这个接口定了一个对象的基本功能,我们通过把对象抽象化方便注入容器,以便管理。

容器(ApplicationContext)

BeanFactroy.java



这个接口定义了容器的基本规范。

getBean(..) 获取容器中的对象;

contiansBean(..)判断容器中是否已经存在该对象;

isSingleton(..)判断这个对象是否为单例..该属性可以在BeanDefination中指定.



ApplicationContext.java 在BeanFactory基础上扩展了更多的高级功能,官网推荐使用ApplicationContext.




XMLBeanFactory.java分析





[code]/**
  * @deprecated as of Spring 3.1 in favor of {@link DefaultListableBeanFactory} and
 *             {@link XmlBeanDefinitionReader}
 * 
 *            这里 为什么推荐使用 DefaultListableBeanFactory 和   XmlBeanDefinitionReader ?
 *            因为完全可以使用两个对象组合的方式实现这个类的功能,
 *            那又何必继承呢。而且灵活、解耦
 * 
 */
@Deprecated
@SuppressWarnings({ "serial", "all" })
public class XmlBeanFactory extends DefaultListableBeanFactory {

    /**
     * 用来读取XML形式的配置文件  I/O reader
     */
    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

    /**
     * 是XML形式存在的, 我们可以使用Resource resouce=new
     * ClassPathResource (beans.xml),定位到元信息
     * (metadata),spring中将元信息抽象为Resource,从中读取
     * BeanDefination并注册到容器里。
     * 然后获取BeanDefination信息来创建对象,完成初始化的
     * 依赖注入。
     * 
     * @param resource XML resource to load bean 
     * definitions from
     * parsing errors
     */
public XmlBeanFactory(Resource resource) throws BeansException {

        this(resource, null);
    }

    /**
     * Create a new XmlBeanFactory with the given input 
     * stream, which must be parsable
     * using DOM.
     * definitions from
     * @param parentBeanFactory parent bean factory
     * @throws BeansException in case of loading or 
     * parsing errors
     */
public XmlBeanFactory(Resource resource, BeanFactory  parentBeanFactory)
            throws BeansException {
        super(parentBeanFactory);
        /**
         * 加载初始化,实现依赖注入
         */
        this.reader.loadBeanDefinitions(resource);
    }

}


FileSystemXmlApplicationContext.java分析




它的父类AbstractApplicationContext.java中实现了ApplicationContext的主要功能;

[code]    /**
     * Create a new FileSystemXmlApplicationContext with 
     * the given parent, loading the
     * definitions from the given XML files.
     * 
     * @param configLocations array of file paths
     * context, loading all bean
     * definitions and creating all singletons. 
     * Alternatively, call refresh
     * manually after further configuring the context.
     * @param parent the parent context
     * @throws BeansException if context creation failed
     * @see #refresh()
     */
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh,
            ApplicationContext parent) throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            /*
             * 1.BeanDefinition对应的Resource 定位:
             * 如文件系统中通过"FileSystemResource"对bean进行
             * Resource抽象、如类路径中通过
             * "ClassPathResource"对 bean进行抽象。定位过程可
             * 以理解为寻找Bean的元数据。
             * 
             * 2.载入 把定位到的(xml形式、annotation形式)元数据
             * 表示为容器内部的数据结构 (BeanDefinition,实际上就
             * 是POJO在Spring中容器中的抽象),并载入容器
             * 
             * 3.注册   把载入的BeanDefinition向容器注册(通过
             * HashMap持有)
             * 这里容器的初始化过程中,并没有依赖注入的过程。依赖注
             * 入与上边所说的BeanDefination的载入是两个独立的过程
             * 当调用getBean(..)时,触发依赖注入。
             * 
             * 如果设置了Bean的lazyInit属性,在容器初始化过程中,
             * 也会完成该对象的依赖注入!!! 
             */
            refresh();
        }
    }


[code]public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

..........

    /**
     * Create a new FileSystemXmlApplicationContext with 
     * the given parent,
     * loading the definitions from the given XML files.
     * @param configLocations array of file paths context,
     * loading all bean definitions and creating all 
     * singletons.configuring the context.
     * @param parent the parent context
     * @throws BeansException if context creation failed
     * @see #refresh()
     */
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
        // 初始化容器
        refresh();
        }
    }

    /**
     * Resolve resource paths as file system paths.
     * <p>Note: Even if a given path starts with a slash, 
     * it will get interpreted as relative to the current 
     * VM working directory.container.
     * @param path path to the resource
     * @return Resource handle
     * @see 
     * 
     *模板方法的实现,通过Reader定位到 metaData数据,并以
     * BeanDefination形式,注册到容器中
     */
    @Override
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }

}

public abstract class AbstractApplicationContext extends   DefaultResourceLoader implements   ConfigurableApplicationContext, DisposableBean {
......

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //创建 一个 DefaultListableBeanFactory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }

    /**
     * Return the internal bean factory of the parent 
     * context if it implements parent context itself.
     * 
     */
protected BeanFactory getInternalParentBeanFactory() {
        return (getParent() instanceof ConfigurableApplicationContext) ?
                ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
    }
    }

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {    
......

    /**
     * Create an internal bean factory for this context.
     * Called for each {@link #refresh()} attempt.
     * <p>The default implementation creates a
     * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
     * with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
     * context's parent as parent bean factory. Can be overridden in subclasses,
     * for example to customize DefaultListableBeanFactory's settings.
     * @return the bean factory for this context
     * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
     * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
     * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
     * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
     */
    protected DefaultListableBeanFactory createBeanFactory() {
        //调用父类方法
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }

    /**
     * This implementation performs an actual refresh of this context's underlying
     * bean factory, shutting down the previous bean factory (if any) and
     * initializing a fresh bean factory for the next phase of the context's lifecycle.
     */
    @Override
    protected final void refreshBeanFactory() throws BeansException {
        //1.关闭之前的容器(如果存在)
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //2.获取ApplicationContext 实现(容器)
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            //3.设置beanFactory属性,如同名的beanDefination允许注册后,覆盖之前的bean Defination
            customizeBeanFactory(beanFactory);
            //4.加载BeanDefiantion信息
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }
}

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {

    /**
     * Loads the bean definitions via an XmlBeanDefinitionReader.
     * 
     * 通过Reader 加载BeanDefiantion信息
     *
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     * @see #initBeanDefinitionReader
     * @see #loadBeanDefinitions
     */
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        //实际加载BeanDefination的动作
        loadBeanDefinitions(beanDefinitionReader);
    }

    /**
     * Load the bean definitions with the given XmlBeanDefinitionReader.
     * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
     * method; hence this method is just supposed to load and/or register bean definitions.
     * @param reader the XmlBeanDefinitionReader to use
     * @throws BeansException in case of bean registration errors
     * @throws IOException if the required XML document isn't found
     * @see #refreshBeanFactory
     * @see #getConfigLocations
     * @see #getResources
     * @see #getResourcePatternResolver
     */
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }

    /**
     * Return an array of Resource objects, referring to the XML bean definition
     * files that this context should be built with.
     * <p>The default implementation returns {@code null}. Subclasses can override
     * this to provide pre-built Resource objects rather than location Strings.
     * @return an array of Resource objects, or {@code null} if none
     * @see #getConfigLocations()
     * 
     * 模板方法--查看--FileSystemXmlApplicationContext.getResourceByPath()
     * 最终返回 "return new FileSystemResource(path);" 对Resource的实现;
     * 如果是其他类型的 ***ApplicationContext.java 当然会生成对应的 ***Resource对象
     * Resource 可以理解为对元信息的定位以及抽象(这个元信息可以用xml实现;或者annotation实现)
     */
    protected Resource[] getConfigResources() {
        return null;
    }

}


最终的注册方法DefaultListableBeanFactory.registerBeanDefinition(…):

[code] public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(
                    beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition oldBeanDefinition;

    synchronized (this.beanDefinitionMap) {
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!this.allowBeanDefinitionOverriding) {
                throw new BeanDefinitionStoreException(
                        beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition
                                + "] for bean '" + beanName + "': There is already ["
                                + oldBeanDefinition + "] bound.");
            }
            else {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '"
                            + beanName + "': replacing [" + oldBeanDefinition
                            + "] with [" + beanDefinition + "]");
                }
            }
        }
        else {
            this.beanDefinitionNames.add(beanName);
            this.frozenBeanDefinitionNames = null;
        }
        // HashMap中持有beanName以及对应的beanDefinition
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }

    if (oldBeanDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}


完成了BeanDefiniton的注册,就完成了整个容器的初始化过程。此时,在使用的IOC容器DefaultListableBeanFactory中,已经建立了整个Bean的配置信息,可以在beanDefinitionMap中被检索使用。依赖注入的基础已经建立,接下我们详细说一些依赖注入的原理:

1.依赖注入的过程是用户首次向容器索要bean时触发的(getBean(…)调用),当然如果Bean的熟悉设置为lazy-init时,容器完成初始化后就完成了这个Bean的依赖注入。

待续…………………………….
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: