【spring原理、源码二】IOC容器

3 篇文章 0 订阅

一、什么是IOC

IOC是一种软件的设计模式,这种模式改变了传统的控制流。传统的控制流中,某处代码需要使用某个依赖项时,需要自己进行依赖实例的创建,赋值,然后使用。而IOC所谓的依赖反转,正是反转了这种对依赖项的管理,由第三方完成对依赖项的管理(包括读取元信息、创建、销毁等全生命周期管理),而代码中不再需要对依赖项进行创建等管理。

从这里不禁会想到,传统的模式中要使用某个实例则new一个就好了,IOC运用上之后,由第三方管理这些实例,那么当代码中需要使用某个实例时,怎么获取呢?答案如下。

二、IOC解决依赖关系的方式

IOC模式运用后,第三方来管理依赖项的实例,那么当代码中需要使用时怎么获取到呢,其实由很多种实现方式,在spring framework中通过两种方式来实现:依赖查找、依赖注入。

IOC并非仅在spring framework中实现,在发展过程中,EJB等已经在实现IOC时实现了依赖查找。在谈及spring framework时经常会说DI,而往往忽视了依赖查找,按照“小马哥”的说法,就是因为依赖查找已经是早都实现的了,spring framework官方介绍自己的特性一般就不讲这个早都实现的“依赖查找”了。

依赖查找是一个代码主动向框架获取依赖项的过程,框架提供对依赖的生命周期管理,并提供查找依赖的方法,代码中需要主动调用该方法来获取依赖,是一个主动拉的方式。而依赖注入是一种推的方式,框架主动将依赖项推到需要的地方。

比如说在代码中获取IOC容器并通过getBean方法获取Bean,就是依赖查找的方式。而在配置Bean时标识auto-wiring,则IOC容器会主动将依赖注入,而不需要业务代码中通过getBean方法取获取。依赖注入通常有两种实现方式,构造器注入或者setter方法注入。

这里又引出一个问题,有哪些方式配置Bean的信息?又如下几种方式配置:xml文件中、Properties文件中、注解、Java API配置,通常使用xml文件配置或者注解配置的方式。

三、什么是IOC容器及其职责

不仅仅是spring framework中实现了IOC容器,很多技术中都有实现,其中有一些是Java原生的,以Java Beans的实现为例,其实现了依赖查找,但是并没有实现依赖注入。这种IOC容器需要熟悉大量的API,按照其规范实现某些接口来达到注册Bean,解决依赖的功能,对业务代码侵入很大。

spring中实现了IOC设计模式的组件被称为IOC容器,spring中的IOC容器有一个基本接口BeanFactory,基于此接口很多类和接口,基本可以分为两大类IOC容器,一类是基本的IOC容器,可以称为BeanFactory;还有一类是增强型的IOC容器,又被称为应用上下文ApplicationContext。BeanFactory系实现了IOC容器的基本功能,而ApplicationContext系此基础上实现了一些附加功能,ApplicaitonContext采用继承+组合的方式,在内部持有BeanFactory的实例,通过代理模式代理BeanFactory实例,来获取基本的BeanFactory的功能。

那么BeanFactory有哪些基本功能?

BeanFactory作为IOC容器的基本容器,实现了三大功能:生命周期管理、依赖处理、配置管理

生命周期管理包括对容器本身的管理,以及对其管理的Bean或其它资源的管理;依赖处理主要提供依赖查找和依赖注入的方式来实现IOC依赖处理;配置管理主要包括对容器本身的配置和对Bean的配置信息管理。

ApplicationContext又增加了哪些扩展功能?增加了国际化、注解驱动、事件等功能

四、依赖注入的方式

spring中的依赖注入一共有两种方式,setter方法注入、构造器注入。以在xml文件中进行配置为例

<bean id="user" class="domain.User">
        <property name="name" value="gnayil"/>
        <property name="age" value="18"/>
</bean>
<bean id="grade" class="domain.Grade">
        <constructor-arg name="user" ref="user"/>
</bean>
<bean id="school" class="domain.School" autowire="byType">
</bean>
public class User {
    String name;
    Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
public class Grade {
    User user;

    public Grade(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Grade{" +
                "user=" + user +
                '}';
    }
}
public class School {
    Grade grade;
    User user;

    @Override
    public String toString() {
        return "School{" +
                "grade=" + grade +
                ", user=" + user +
                '}';
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

网上有的博客中说autowire也是一种注入方式,其实这时不准确的,通过上面的代码,如果将School中的setter方法删掉,那么xml中对School配置了autowire,但是测试发现并没有注入成功,那么可以说明autowire本质上就是基于setter方式注入的。

这里还有一个问题,通常使用@Autowire注解来实现依赖注入,那么注解的方式本质上是基于构造器还是setter的方式注入呢?还是说其本质上是另外一种注入方式?

答:@Autowire注解有三种用法,可以加载属性、setter方法、构造器。加载setter方法和构造器上本质上就是setter注入和构造器注入。而加载属性上是通过反射来对属性进行注入,不需要setter方法,也不是构造器注入。

构造器注入和setter注入各有什么优缺点,使用哪一种?

对于选择构造器注入还是setter注入,并没有一个统一的答案,只能分析一下各自的优缺点,各取所需吧

构造器注入,通常将属性定义为final这样一但通过构造器注入便不可修改,保证不会被随意更改,并且构造器的方式能够保证注入的对象是成熟态的,不会出现未构造完成的中间状态;

setter方法注入,更加灵活,可以对依赖项在不同的时间注入,而不必一次性都注入。

问题:spring的IOC容器有什么优势?

相较于其它的实现方式,比如Java Beans等,其不仅可以对依赖进行查找,还可以通过依赖注入的方式简化依赖的管理。还有一些我现在不能理解的优势

五、依赖查找

依赖查找的方式有很多不同的方式

Bean的名称指的是xml中Bean节点的id,延迟查找是指通过ObjectFactory获取bean,这种方式在获取到ObjectFactory时,相应的Bean并未获取到,而是调用其getObject方式时才会获取到,因此叫延迟查找。根据bean的类型查找是指指定bean的类例如User.class,而集合Bean对象不是指List这种集合,而是按照类型查找时可能有多个符合条件的类,因此查找到多个Bean对象,比如Sutdent继承自People按照People.class查找时Student也会被找出来;根据java注解是指按照注解的方式@Bean定义bean

六、依赖注入

依赖查找的方式只能查找IOC容器管理的Bean,而对于非Bean对象,不能查找到。但是依赖注入的方式,不仅可以注入IOC管理的Bean,而且可以注入非Bean对象。

不管时依赖查找还是依赖注入,IOC依赖的来源都有三个:自定义Bean、容器自建Bean、容器内部依赖的非Bean。

Bean、容器的配置及定义有以下几种方式

七、spring中的IOC容器实现

spring中IOC容器的基础是BeanFactory接口,其基本的继承关系如下

重点可以关注一下这两个DefaultListableBeanFactory和ClassPathXmlApplicationContext这两个IOC容器,这两个描述了spring中IOC容器的两条线。BeanFactory到DefaultListableBeanFactory可以认为是基本IOC容器,其实现了IOC容器的依赖查找、依赖注入等对Bean进行管理的功能;而BeanFactory到ClassPathXmlApplicationContext这条线除了完全涵盖基本IOC容器的功能,还提供了面向切面、事件、注解等功能。

ApplicationContext这条线能够有基本IOC容器的实现方式,是通过继承+组合代理的方式,内部持有DefaultListableBeanFactory的实例,基本的IOC功能都通过代理调用该实例实现。

因此,总的来讲,BeanFactory是基本IOC容器,而ApplicationContext是具备扩展应用功能的IOC容器

八、IOC容器生命周期概览

启动、运行、终止

ApplicationContext通过refresh方法启动,通过close方法关闭,可以通过这两个方法了解启动和终止的过程,总的来说启动过程中需要读取容器的配置元信息、读取Bean的配置信息、修改容器的一些状态变量等等,还会调用postProcessBeanFactory,从而实现用户自定义的容器启动逻辑;而终止过程需要销毁Bean、修改容器状态变量等等来关闭。

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }
    public void close() {
        synchronized(this.startupShutdownMonitor) {
            this.doClose();
            if (this.shutdownHook != null) {
                try {
                    Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                } catch (IllegalStateException var4) {
                }
            }

        }
    }

    protected void doClose() {
        if (this.active.get() && this.closed.compareAndSet(false, true)) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Closing " + this);
            }

            LiveBeansView.unregisterApplicationContext(this);

            try {
                this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
            } catch (Throwable var3) {
                this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
            }

            if (this.lifecycleProcessor != null) {
                try {
                    this.lifecycleProcessor.onClose();
                } catch (Throwable var2) {
                    this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
                }
            }

            this.destroyBeans();
            this.closeBeanFactory();
            this.onClose();
            if (this.earlyApplicationListeners != null) {
                this.applicationListeners.clear();
                this.applicationListeners.addAll(this.earlyApplicationListeners);
            }

            this.active.set(false);
        }

    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值