Spring IOC

Posted hangtutu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring IOC相关的知识,希望对你有一定的参考价值。

前言

Spring框架为什么如此流行?

原来Spring框架解决了一个很关键的问题,它可以把对象之间的依赖关系转为用配置文件来管理,也就是它的依赖注入机制。IOC容器用来管理这些Bean,管理Bean的关系以及生命周期,然而这与之前将应用程序主动new对象不同,Spring实现使用IOC容器创建对象,对象的获取方式反转了,所以IOC容器也称为控制反转。面对繁琐的依赖关系,我们不用一个一个去new对象,直接使用IOC创建好的对象,这也正是IOC的方便之处。

Spring 核心组件:Bean Context,Core

Bean

Bean组件在org.springframework.beans包下,这个包下所有的类解决了Bean的定义,创建以及解析。

1.Bean的定义:主要有BeanDefinition描述,也可以说Spring中的Bean就是BeanDefinition的实例。Spring成功解析一个<bean/>节点后,在Spring的内部它就被转化为BeanDefinition对象。

技术分享图片

 

2.Bean的创建:.Spring Bean的创建是典型的工厂设计模式,顶级接口是BeanFactory

技术分享图片

3.Bean的解析:Bean的解析主要是对配置文件的解析

技术分享图片

Context

context组件在org.framework.context包下,主要就是Bean关系的集合。给Spring提供了一个运行时环境用于存储对象的状态。ApplicationContext是顶级的父类

技术分享图片

ApplicationContext继承了BeanFactory说明了Spring容器运行的主要对象是Bean

ApplicationContext实现了ResourceLoader接口说明ApplicationContext可以访问到外部资源。

Core

core组件就是发现,建立和维护每个Bean之间关系所需要的一系列工具。core组件其中一个重要的组成部分就是定义了资源的访问方式。

技术分享图片

 

 

IOC的工作原理

下面我们看看IOC是怎样工作的呢?

先来一个小demo

MessageService.java

public interface MessageService {
    String getMessage();
}

MessageServiceImpl.java

public class MessageServiceImpl implements MessageService {
    public String getMessage() {
        return "hello world";
    }
}  

application.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">
    <bean id="messageService" class="com.kristin.spring.ioc.MessageServiceImpl"/>
</beans>  

TestMessage.java

public class TestMessage {
    public static void main(String[] args) {
        // 用我们的配置文件来启动一个 ApplicationContext
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");

        System.out.println("context 启动成功");

        // 从 context 中取出我们的 Bean,而不是用 new MessageServiceImpl() 这种方式
        MessageService messageService = context.getBean(MessageService.class);
        // 这句将输出: hello world
        System.out.println(messageService.getMessage());
    }
}  

运行结果:

技术分享图片

IOC运行分析  

从上面代码可以发现,IOC的入口就是ClassPathXmlApplicationContext的构造方法,下面我们看看源码是怎样写的

ClassPathXmlApplicationContext.java

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[] {configLocation}, true, null);
}

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
        throws BeansException {

    super(parent);
    setConfigLocations(configLocations);    //根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
    if (refresh) {
        refresh();  //这里是核心代码
    }
}  

下面我们看一下refresh()

ClassPathXmlApplicationContext.java

@Override
public void refresh() throws BeansException, IllegalStateException {
    //这里加锁,防止多个线程同时refresh()时出现问题
    synchronized (this.startupShutdownMonitor) {
        // 为刷新准备新的context,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
        prepareRefresh();

        // 刷新所有BeanFactory的子容器
        // 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,
        // 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
        // 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 准备BeanFactory,设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
        prepareBeanFactory(beanFactory);

        try {
            // 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
            // 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事
            postProcessBeanFactory(beanFactory);

            // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
            invokeBeanFactoryPostProcessors(beanFactory);

            // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
            // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
            // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
            registerBeanPostProcessors(beanFactory);

            // 初始化message source
            initMessageSource();

            // 初始化 event multicaster
            initApplicationEventMulticaster();

            // 刷新由子类实现的方法
            onRefresh();

            // 注册事件监听器,监听器需要实现 ApplicationListener 接口。
            registerListeners();

            // 初始化所有的 singleton beans
            finishBeanFactoryInitialization(beanFactory);

            // 最后,广播事件,ApplicationContext 初始化完成
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

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

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring‘s core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}  

refresh函数主要包含了以下几个步骤:

1.构建BeanFactory,以便于产生所需的"演员"

2.注册可能感兴趣的事件

3.创建Bean实例对象

4.触发被监听的事件

 

创建BeanFactory的时序图:

技术分享图片

 继续看一看refresh()函数中调用的函数吧

AbstractApplicationContext.java

protected void prepareRefresh() {
    // 记录启动时间,将 active 属性设置为 true,closed 属性设置为 false,它们都是 AtomicBoolean 类型
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    if (logger.isInfoEnabled()) {
        logger.info("Refreshing " + this);
    }

    // Initialize any placeholder property sources in the context environment
    initPropertySources();

    // 校验 xml 配置文件
    getEnvironment().validateRequiredProperties();

    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

AbstractApplicationContext.java

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 关闭旧的 BeanFactory (如果有),创建新的 BeanFactory,加载 Bean 定义、注册 Bean 等等
    refreshBeanFactory();
    // 返回刚刚创建的 BeanFactory
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

AbstractRefreshableApplicationContext.java

@Override
protected final void refreshBeanFactory() throws BeansException {
    // 如果 ApplicationContext 中已经加载过 BeanFactory 了,销毁所有 Bean,关闭 BeanFactory
    // 注意,应用中BeanFactory本来就是可以多个的,这里可不是说应用全局是否有 BeanFactory,而是当前ApplicationContext是否有BeanFactory
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 初始化一个 DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        // 用于 BeanFactory 的序列化
        beanFactory.setSerializationId(getId());
        // 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
        customizeBeanFactory(beanFactory);
        // 加载 Bean 到 BeanFactory 中,这个方法很重要
        loadBeanDefinitions(beanFactory);   
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

此时已经获得了BeanFactory

后面会陆续更新,暂时先整理到这里...

 

 

对了,以上文章整理自

https://javadoop.com/post/spring-ioc

《深入分析Java Web技术内幕》

感谢两位大神的文章及书籍 

 

以上是关于Spring IOC的主要内容,如果未能解决你的问题,请参考以下文章

Spring之IOC原理及代码详解

Spring IOC源代码具体解释之整体结构

Spring 框架学习——IOC思想原型及实质

Spring IOC源代码具体解释之容器依赖注入

[Spring 源解系列] 重温 IOC 设计理念

Spring IoC