Spring IoC容器详解

IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

Spring IoC容器详解

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

Spring IoC体系结构

BeanFactory

Spring Bean的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多的IOC容器的实现供用户选择和使用,其相互关系如下:

Spring IoC体系结构

其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。

BeanFactory源码如下:

public interface BeanFactory {    
    //对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,    
    //如果需要得到工厂本身,需要转义         
    String FACTORY_BEAN_PREFIX = "&"; 
       
    //根据bean的名字,获取在IOC容器中得到bean实例    
    Object getBean(String name) throws BeansException;    
   
    //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。    
    Object getBean(String name, Class requiredType) throws BeansException;    
    
    //提供对bean的检索,看看是否在IOC容器有这个名字的bean    
    boolean containsBean(String name);    
    
    //根据bean名字得到bean实例,并同时判断这个bean是不是单例    
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    
    
    //得到bean实例的Class类型    
    Class getType(String name) throws NoSuchBeanDefinitionException;    
    
    //得到bean的别名,如果根据别名检索,那么其原名也会被检索出来    
   String[] getAliases(String name);    
}

BeanDefinition

Spring IOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的,其继承体系如下:

BeanDefinition继承体系

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

Bean解析类

IOC容器获取Bean

调用ApplicationContext的getBean()方法
//2.从IO从容器中获取Bean实例
//利用id定位到IOC容器中的bean
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");
//利用类型返回IOC容器中的Bean,但要求IOC容器中必须只能有一个该类型的Bean
//HelloWorld helloWorld = ctx.getBean(HelloWorld.class);

IoC注入

构造函数注入

1.通过构造方法注入Bean的属性值或依赖对象,它保证了Bean实例在实例化后就可以使用
2.构造器注入在<constructor-arg>元素里声明属性,<constructor-arg>中没有name属性
例:
   1. 创建一个Car类
        public class Car {
            private String brand;
            private String corp;
            private int price;
            private int maxSpeed;

            public Car(String brand, String corp, int price) {
                this.brand = brand;
                this.corp = corp;
                this.price = price;
            }     
            @Override
            public String toString() {
                return "Car{" +
                        "brand='" + brand + '\'' +
                        ", corp='" + corp + '\'' +
                        ", price=" + price +
                        ", maxSpeed=" + maxSpeed +
                        '}';
            }
        }
2.    <!--通过构造方法来配置bean的属性-->
    <bean id="car" class="com.hongkun.spring.beans.Car">
        <constructor-arg value="Audi"></constructor-arg>
        <constructor-arg value="Shanghai"></constructor-arg>
        <constructor-arg value="300000"></constructor-arg>
    </bean>
3. 在类中使用
Car car = (Car) ctx.getBean("car");
System.out.println(car);

属性注入

属性注入即通过setter方法注入bean的属性值或依赖的对象
属性注入使用<property>元素,使用name属性指定Bean的属性名称,value属性或<value>子节点指定属性值
属性注入是实际应用中最常用的注入方式
例:
<bean id="helloWorld"class="com.hongkun.spring.beans.HelloWorld">
        <property name="name2" value="Spring"></property>
</bean>
向HelloWorld类的name2属性注入Spring值

使用IOC容器注意点

使用IOC框架产品能够给我们的开发过程带来很大的好处,但是也要充分认识引入IOC框架的缺点,做到心中有数,杜绝滥用框架。

第一、软件系统中由于引入了第三方IOC容器,生成对象的步骤变得有些复杂,本来是两者之间的事情,又凭空多出一道手续,所以,我们在刚开始使用IOC框架的时候,会感觉系统变得不太直观。所以,引入了一个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运行维护中,还得让新加入者具备同样的知识体系。
第二、由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。如果你要追求运行效率的话,就必须对此进行权衡。

第三、具体到IOC框架产品(比如:Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。

第四、IOC框架产品本身的成熟度需要进行评估,如果引入一个不成熟的IOC框架产品,那么会影响到整个项目,所以这也是一个隐性的风险。

版权声明:本文为JAVASCHOOL原创文章,未经本站允许不得转载。