SpringIoc解析

一、什么是IoC

IoC,Inversion of Control,控制反转。控制反转是一种通过描述(在java中可以使XML或者注解)并通过第三方去获取特定对象的方式。简单地说就是将对象由虚拟机主动创建变为从IoC容器中获取,它是面向对象编程的一种思想,主要作用是降低开发难度、对模块解耦、有利于测试,常见实现方式是依赖注入(DI)和依赖查找(DL)。
我们举例说明,在实际生活中,我们想喝橙汁,在没有饮品店店的时候,我们一般情况下需要自己去买水果,果汁机,配料等。这里的橙汁是我们自己“主动”创造的。然而到了今天,由于饮品店的出现,我们想喝橙汁就可以直接去饮品店买,也就是橙汁是由饮品店创造的,而不是我们自己“主动”创造,这就是控制反转的思想,它最大的好处是降低对象之间的耦合。

二、Spring IoC容器的设计

Spring IoC容器的设计主要是基于BeanFactory和ApplicationContext两个接口。BeanFactory是Spring IoC所定义的最底层的接口,ApplicationContext是其高级接口之一,是最常用的Spring IoC容器。并对 BeanFactory 功能做了许多的扩展,所以在绝大部分的工作场景下,都会使用 ApplicationContext 作为 Spring IoC 容器。Spring IoC容器设计图如下:
在这里插入图片描述

从图中可以看到BeanFactory位于接口设计的最底层,它提供了Spring Ioc最底层的设计,让我们来看看它的源码:

public interface BeanFactory {

    //对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,    
    //如果需要得到工厂本身,需要转义   
    String FACTORY_BEAN_PREFIX = "&";

    //根据bean的名字,获取在IOC容器中得到bean实例 
    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
    Object getBean(String name, Object... args) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    //提供对bean的检索,看看是否在IOC容器有这个名字的bean  
    boolean containsBean(String name);

    //根据bean名字得到bean实例,并同时判断这个bean是不是单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    //得到bean实例的Class类型  
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    //得到bean的别名,如果根据别名检索,那么其原名也会被检索出来   
    String[] getAliases(String name);
}
  • getBean 用于获取配置给Spring IoC 容器的Bean

  • isSingleton 用于判断是否单例。默认返回true

  • isPrototype 判断是否为原型。默认返回false

  • getAliases 用于获取别名

三、Spring IoC容器的初始化和依赖注入

1.Bean的定义和初始化

Bean的定义和初始化在spring Ioc容器中是两大步骤,它是先定义,然后初始化和依赖的注入,Bean的定义分为3步:

  1. Resource定位,这步是Spring Ioc容器根据开发者的配置,进行资源定位,在Spring的开发中,通过XML或者注解都是十分常见的方式,定位的内容是由开发者所提供的。
  2. BeanDefinition的载入,这个时候只是将Resource定位的信息,保存到Bean定义中(BeanDefinition)中,此时并不会创建Bean的实例。
  3. BeanDefinition的注册,这个过程就是将BeanDefinition的信息发布到Spring Ioc容器中,注意,此时仍旧没有对应的Bean的实例创建。

做完这三步,Bean就在Spring IoC容器中定义了,还没有初始化,更没有完成依赖注入。对于初始化和依赖注入,Spring Bean还有一个配置项——lazy-init,其含义是是否延迟初始化Spring Bean,默认值false,也就是默认自动初始化Bean,如果设置成true,那么Bean的初始化和依赖注入将在执行getBean方法时进行。

而要知道工厂是如何产生对象的,我们需要看具体的IOC容器实现,spring提供了许多Ioc容器的实现。比如XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是针对最基本的Ioc容器的实现,这个Ioc容器可以读取XML文件定义的BeanDefinition(XML文件中对bean的描述),如果说XmlBeanFactory是容器中的屌丝,ApplicationContext应该算容器中的高帅富。

 ApplicationContext是Spring提供的一个高级的IoC容器,它除了能够提供IoC容器的基本功能外,还为用户提供了以下的附加服务。

从ApplicationContext接口的实现,我们看出其特点:

  1.  支持信息源,可以实现国际化。(实现MessageSource接口)

  2.  访问资源。(实现ResourcePatternResolver接口,这个后面要讲)

  3.  支持应用事件。(实现ApplicationEventPublisher接口)

我们先来一个简单的例子来看看怎么实例化 ApplicationContext。

首先我们先定义一个接口:

public interface MessageService {
    String getMessage();
}

定义接口实现类:

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

接下来,我们在 resources 目录新建一个配置文件,文件名随意,通常叫 application.xml 或 application-xxx.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.javadoop.example.MessageServiceImpl"/>
</beans>

这样,我们就实例化了ApplicationContext:

public class App {
    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());
    }
}

2、依赖注入
Spring实现IoC主要采用依赖注入,一般而言,依赖注入分为以下三种方式:

  • 构造器注入:依赖于构造方法实现,构造方法可以使有参的和无参的。Spring内部采用反射的方式调用构造方法实现注入。

  • setter注入:设值注入依赖setter方法,是Spring最主流的注入方式。灵活且可读性高。其原理也是通过反射实现,设值注入依赖于无参构造器,当bean类声明了带参构造器时必须同时声明无参构造器。XML方式对应p标签。

  • 接口注入:适用于来自于外界的资源,比如数据库连接资源可以在Tomcat下配置,然后通过JNDI的形式去获取它。此时可以使用借口注入。

四、SpringBean装配

Spring 中提供了 3 种方法进行配置:

  • 在XML 文件中显式配置
  • 在 Java 的接口和类中实现配置
  • 隐式 Bean 的发现机制和自动装配原则

方式选择的原则
在现实的工作中,这 3 种方式都会被用到,并且在学习和工作之中常常混合使用,所以这里给出一些关于这 3 种优先级的建议:

1.最优先:通过隐式 Bean 的发现机制和自动装配的原则。
基于约定由于配置的原则,这种方式应该是最优先的

好处:减少程序开发者的决定权,简单又不失灵活。

2.其次:Java 接口和类中配置实现配置
在没有办法使用自动装配原则的情况下应该优先考虑此类方法

好处:避免 XML 配置的泛滥,也更为容易。
典型场景:一个父类有多个子类,比如学生类有两个子类,一个男学生类和女学生类,通过 IoC 容器初始化一个学生类,容器将无法知道使用哪个子类去初始化,这个时候可以使用 Java 的注解配置去指定。

3.最后:XML 方式配置
在上述方法都无法使用的情况下,那么也只能选择 XML 配置的方式。

好处:简单易懂(当然,特别是对于初学者)
典型场景:当使用第三方类的时候,有些类并不是我们开发的,我们无法修改里面的代码,这个时候就通过 XML 的方式配置使用了。

通俗来讲,当配置类是你自身正在开发法的工程,那么应该考虑Java配置为主,而Java配置又分为自动配置和Bean名称配置,在没有歧义的基础上,优先使用自动配置,这样就可以减少大量的XML配置。如果不是自身代码开发的,那么建议使用XML配置。

具体的装配参考 《Java EE 互联网轻量级框架整合开发》 此处不再赘述。

五、Bean生命周期

之前的Ioc学习中,我们只是关心如何正确的将Bean装配到Ioc容器中,而没有关心IoC容器如何装配和销毁bean的过程。有时候我们也需要自定义初始化或者销毁Bean的过程,以满足一些Bean的特殊初始化和销毁的要求。为了解决这些问题,我们有必要了解Spring Ioc初始化和销毁Bean的过程,这边是Bean的生命周期的过程。它大致分为Bean定义,bean初始化,bean的生存期和bean的销毁4个部分。
如下图所示:
在这里插入图片描述

  • 注意这些接口都是针对生命而言的。在没有注释的情况下的流程节点都是针对单个Bean而言的,但是BeanProcessor是针对所有Bean而言的,当一个Bean实现了上述接口,我们只需要在Spring Ioc容器中定义它就可以了,SpringIoc容器会自动识别。
  • 即使你定义了ApplicationContextAware接口,但是有时候并不会调用,这要根据你的Ioc容器来决定,我们知道,Spring Ioc容器最低额要求是实现BeanFactory接口。而不是实现ApplicationContext接口。如果采用了非ApplicationContext子类创建SpringIoc容器,即使是实现了ApplicationContextAware的setApplicationContext方法,它也不会在生命周期之中被调用。

未完待续。。。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值