zoukankan      html  css  js  c++  java
  • Spring IOC

    前言

    Spring框架为什么如此流行?

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

    Spring 核心组件

    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技术内幕》

  • 相关阅读:
    hdu 4521 小明系列问题——小明序列(线段树 or DP)
    hdu 1115 Lifting the Stone
    hdu 5476 Explore Track of Point(2015上海网络赛)
    Codeforces 527C Glass Carving
    hdu 4414 Finding crosses
    LA 5135 Mining Your Own Business
    uva 11324 The Largest Clique
    hdu 4288 Coder
    PowerShell随笔3 ---别名
    PowerShell随笔2---初始命令
  • 原文地址:https://www.cnblogs.com/Hangtutu/p/9391330.html
Copyright © 2011-2022 走看看