美文网首页
spring IOC

spring IOC

作者: 黄二的NPE | 来源:发表于2018-07-23 22:29 被阅读23次
  • 依赖

class Coder {
    Computer mComputer;
    public Coder () {
        mComputer = new Computer();
    }
    public void coding(){
      mComputer.coding();
    }
}

码农需要电脑才能敲代码,在这个码农类中,码农持有电脑的引用,所以说码农依赖电脑。依赖是一种关系。

  • 依赖注入

在上面的coder代码中,Computer是coder自己创建的;假如我们把上面代码改写成如下:

class Coder {
    Computer mComputer;
    public void setMComputer(Computer mComputer){
      this.mComputer = mComputer;
    }
    public void coding(){
      mComputer.coding();
    }
}

如果改成这样,那么computer从何而来?我们可以在另外一个类中,通过setComputer注入,比如:

public class Context {
    public static void main(String[] args) {
        Computer computer = new Computer();
        Coder coder = new Coder();
        coder.setComputer(computer);
    }
}

形如Coder里面的computer不是在Coder里面(本类)直接通过 new Computer()来赋值,而是通过在第三方的类中,通过setComputer(computer)注入进去的,就叫做依赖注入。

  • 控制反转

形如上面的Coder里面的computer不是Coder自己来创建,而是把对computer的控制权交给第三方,在第三方的类中创建,然后再注入(setComputer/computer()),就叫做控制反转。

  • 依赖注入的好处

在类中我们依赖的是接口,容器注入的是实现类。假如我们在Coder里面直接写死了new Computer(),那么有一天我们要升级为Macbook,我们是不是还要去改Coder里面的代码,把computer = new Computer()改为 computer = new Macbook(),又有一天,我们突然想换个小米笔记本,岂不是又要改成 new Mibook()。这样子改来改去是比较麻烦的,而且要是有很多个Coder,比如JavaCoder,PHPCoder,他们都有computer的引用,公司统一规定要升级为MacBook,我们就要改很多个不同的类。而如果我们采用依赖注入,我们只需要在容器里面改Computer computer = new Macbook()即可。因为Coder都是接口的引用,所以就都不用改了。

  • IOC容器

上面我们说到context把coder对象和computer对象创建出来,再把computer对象注入到了coder中。在spring中,也有一个context,它把所有配置了的对象(bean)创建出来,然后再根据他们的依赖关系,把不同的bean注入到另外的bean中,形成一个关系集合,这样的context就叫做IOC容器。

  • core

context/core/beans

Core就是发现、建立和维护每 个Bean之间的关系所需要的一系列的工具,从这个角度看来,Core这个组件叫Util更能让你理解。在这些工具类中,定义了资源的访问方式的resource是其中最重要的组成部分之一。

  • beans

Bean组件在Spring的org.springframework.beans包下。这个包下的所有类主要解决了三件事:

  1. Bean的解析
  2. Bean的定义
  3. Bean 的创建
Bean的解析
bean的解析

Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这里只讲XmlBeanDefinitionReader。

  1. XmlBeanDefinitionReader通过DocumentLoader把bean的定义文件解析成一颗DOM树,然后对DOM中的各个结点就行解析。
  2. 对DOM树根节点中的各个结点进行解析。
  3. 循环各个子树,检查节点是默认的名称空间还是扩展的名称空间,比如<bean>、<property>是默认的名称空间,而<aop:xxx>、<context:xxx>这种标签是扩展的标签需要额外的标签处理器,默认标签的一级标签是<beans>。
  4. 解析bean标签,import和beans标签最后也会走到这一步。bean标签的解析逻辑被委托BeanDefinitionParserDelegate,调用parseCustomElement()方法解析出了BeanDefinition对象。
Bean 的定义
bean的定义

当 Spring 成功解析你定义的一个 <bean/> 节点后,在 Spring 的内部就被转化成 BeanDefinition 对象。以后所有的操作都是对这个对象完成的。

bean的创建
BeanFactory

Spring Bean 的创建是典型的工厂模式,它的顶级接口是 BeanFactory。BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory。从bean的解析和定义之后,我们已经得到一个BeanDefinition对象。那么BeanFactory 是怎么根据BeanDefinition创建对应的对象的呢?可以参考大致代码:

        //1、创建个bean工厂
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //2、创建一个bean的定义
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(Computer.class);
        //定义属性
        MutablePropertyValues mpvs = new MutablePropertyValues();
        PropertyValue name = new PropertyValue("name","MiBook");
        PropertyValue price = new PropertyValue("price",5399);
        mpvs.addPropertyValue(name);
        mpvs.addPropertyValue(price);
        //设置bean的类型和属性
        bd.setPropertyValues(mpvs);
        //3、将定义好bean注册到容器
        beanFactory.registerBeanDefinition("computer", bd);
        beanFactory.preInstantiateSingletons();
        //4、使用bean
        Computer computer = (Computer) beanFactory.getBean("computer");
        System.out.println(computer);
  • context

ApplicationContext

Context 位于 Spring 的 org.springframework.context 包下,它继承BeanFactory,说明spring容器运行的主体对象是bean;它还继承resourceLoader对象,说明它可以访问任何外部资源。初始化容器时,会调用AbstractApplicationContext的refresh方法。那么都做了什么事呢?

  1. 构建 BeanFactory
  2. 注册可能感兴趣的事件
  3. 创建 Bean 实例对象
  4. 触发被监听的事件
一点想法

其实分析这个spring,有点像之前分析springmvc,springmvc有一个存放url与method对应关系的map,那么spring是否存在beanId和object对应的map呢?经过我一番查找,确实找到了这个map,他就是DefaultSingletonBeanRegistry的singletonObjects,是一个ConcurrentHashMap。


    protected void addSingleton(String beanName, Object singletonObject) {
        Map var3 = this.singletonObjects;
        synchronized(this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject != null?singletonObject:NULL_OBJECT);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
bean的生命周期
生命周期

1.Spring对Bean进行实例化(相当于程序中的new Xx())
2.Spring将值和Bean的引用注入进Bean对应的属性中
3.如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法(实现BeanNameAware清主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的)
4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanDactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)
5.如果Bean实现了ApplicationContextAwaer接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把y应用上下文作为参数传入.(作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanDactory里的参数BeanFactory )
6.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法(作用是在Bean实例创建成功后对进行增强处理,如对Bean进行修改,增加某个功能)
7.如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。
8.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 )
9.经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
10.如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。

相关文章

网友评论

      本文标题:spring IOC

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