Spring的控制反转(IOC)

原创 2016年06月02日 00:08:35

        IoC 全称 (Inverse Of Control) ,中文译为控制反转, Spring框架的核心基于控制反转原理。

        控制反转是一种将组件依赖关系的创建和管理置于程序外部的技术。由容器控制程序之间的关系,而不是由代码直接控制。由于控制权由代码转向了容器,所以称为反转。

        对象与对象之间的关系可以简单的理解为对象之间的依赖关系:在 A 类需要类 B 的一个实例来进行某些操作,比如在类 A 的方法中需要调用类 B 的方法来完成功能,叫做 A 类依赖于 B 类。

        一个需要特定的依赖的组件一般会涉及一个依赖对象,在 IOC 的概念中叫做目标 (target) 。换句话说, IOC 提供了这样的服务,使一个组件能够在它的整个生命周期中访问它的依赖和服务,用这种方法与它的依赖进行交互。总的来说, IOC 能够被分解为两种子类型:依赖查找和依赖注入。

        一、依赖查找

        比如使用 JNDI 注册一个数据库连接池时,代码中从注册处获得依赖关系的 JNDI 查找 (JNDI lookups) :

initContext = new InitialContext();
// 获取数据源
DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/mysql");

        二、依赖注入

        Dependency Injection简称DI:两个对象之间的依赖关系在程序运行时由外部容器动态的注入依赖行为方式称为依赖注入。在理解依赖注入之前,看如下这个问题在各种社会形态里如何解决:

        一个人(Java实例,调用者)需要一把斧子(Java实例,被调用者):

        (1)原始社会里,几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。对应的情形为:Java程序里的调用者自己创建被调用者。必然要求被调用的Java类出现在调用者的代码里。无法实现二者之间的松耦合。

        (2)进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。对应Java程序的简单工厂的设计模式。调用者无须关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例即可使用。此时调用的代码面向接口编程,可以让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。

        (3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就自然出现在他面前。对应Spring的依赖注入。调用者无须自己定位工厂,程序运行到需要被调用者时,系统自动提供被调用者实例。事实上,调用者和被调用者都处于Spring的管理下,二者之间的依赖关系由Spring提供。

        所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。有四种方式,分别是:

1、Setter注入方式

        这是最常用的注入方式,假设有一个SpringService类需要调用SpringDao类进行数据库操作,通常需要实例化(new一个)SpringDao对象。而Setter注入方式则可以定义一个private的SpringDao类的成员变量,然后创建该SpringDao的set方法。例如:

public class SpringService{   
    private SpringDao springDao;   
    public void setSpringDao(SpringDao springDao){  
        this.springDao = springDao;  
    }  
    public void operate(){  
        springDao.update();  
    }  
}  

        随后需要配置spring的xml文件,<bean>中的name属性是bean的一个别名,class属性是类的全名,因为在SpringService中有一个成员变量SpringDao,所以要在<bean>标签中创建一个<property>标签指定SpringDao的实例。<property>标签中的name就是SpringService类中的SpringDao的实例名,ref映射相对应定义的SpringDao对象:<bean name="springDao" class="...">,这样其实是将SpringDao对象实例化并且调用SpringService的setSpringDao方法将SpringDao实例注入

<bean name="springService" class="com.mytest.service.SpringService">   
    <property name="springDao" ref="springDao"></property>  
</bean>  
<bean name="springDao" class="com.mytest.dao.SpringDao"></bean>  

2、Constructor注入方式

        这种注入方式是指带有参数的构造方法注入,假如有类型分别是SpringDao和User的成员变量,但是并未设置对象的set方法,所以就不能用第一种注入方式。但可以在SpringService的构造方法中注入,也就是说在创建SpringService对象时将SpringDao和User类型的两个实列参数值传进来:

public class SpringService{  
    private SpringDao springDao;  
    private User user;  

    public SpringService(SpringDao springDao,User user){  
        this.springDao = springDao;  
        this.user = user;  
    }  

    public void operate(){  
        user.setName("xxx");  
        springDao.update(user);
    }  
}  

        在xml文件中不用<property>的形式,而是使用<constructor-arg>标签,ref属性同样指向对应定义的<bean>标签的name属性:

<bean name="springService" class="com.mytest.service.SpringService">  
    <constructor-arg ref="springDao"></constructor-arg>  
    <constructor-arg ref="user"></constructor-arg>  
</bean>  
<bean name="springDao" class="com.mytest.dao.SpringDao"></bean>  
<bean name="user" class="com.mytest.service.User"></bean> 

        解决构造方法参数的不确定性,你可能会遇到构造方法传入的两参数是同类型的,为了分清哪个是第一个参数哪个是第二个参数,则需要进行一些小处理。下面设置index标识参数位置:

<bean name="springService" class="com.mytest.service.SpringService">  
    <constructor-arg index="0" ref="springDao"></constructor-arg>  
    <constructor-arg index="1" ref="user"></constructor-arg>  
</bean> 

        另外还可以有其他属性,比如:

            type是指该属性所对应的类型;

            value是当注入的不是依赖自定义类,而是基本数据类型时,就用value;

3、注解注入方式

        就是在Java代码中使用注解的方式(主要是@Resource或@Autowired)进行装配。前提是需要在xml文件中配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:tx="http://www.springframework.org/schema/tx"
     xmlns:context=http://www.springframework.org/schema/context
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.2.xsd
     http://www.springframework.org/schema/tx 
     http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
     <context:annotation-config></context:annotation-config>或者<context:component-scan base-package="com.xxx,com.yyy"></context:component-scan>
</beans>

           只有配置了xmlns:context=http://www.springframework.org/schema/context以及<context:annotation-config></context:annotation-config>或者<context:component-scan base-package="com.xxx,com.yyy"></context:component-scan>才可以引入注解的命名空间,否则报错。以上的配置隐式地注册了多个对注释进行解析的处理器:AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 等。

        <context:annotation-config> 和 <context:component-scan>的区别在于:<context:annotation-config>是用于激活那些已经在spring容器里注册过的bean上面的注解,而<context:component-scan>除了具有<context:annotation-config>的功能外,还可以在指定package下扫描并注册bean。当<context:annotation-config>和<context:component-scan>同时存在时前者被忽略。并且注解只会被注入一次,哪怕是手动注册了多个处理器,spring仍然只会处理一次。

        1><context:annotation-config> 方式:

        java:

public class SpringService{   
    private SpringDao springDao; 
    @Autowired或@Resource(name="springDao")
    public void setSpringDao(SpringDao springDao){  
        this.springDao = springDao;  
    }  
    public void operate(){  
        springDao.update();  
    }  
}  

        xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:tx="http://www.springframework.org/schema/tx"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.2.xsd
     http://www.springframework.org/schema/tx 
     http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <context:annotation-config></context:annotation-config>
    <bean name="springDao" class="com.mytest.dao.SpringDao"></bean>  
</beans>

        2><context:component-scan>方式:

        java:

@Component
public class SpringService{   
    private SpringDao springDao; 
    @Autowired或@Resource(name="springDao")
    public void setSpringDao(SpringDao springDao){  
        this.springDao = springDao;  
    }  
    public void operate(){  
        springDao.update();  
    }  
} 

        xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:tx="http://www.springframework.org/schema/tx"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.2.xsd
     http://www.springframework.org/schema/tx 
     http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <context:component-scan base-package="com.xxx"/>
</beans>

        虽然@Resource和@Autowired都可以来完成注入依赖,但它们之间是有区别和联系的:

        a.@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;

        b.@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;

        c.@Resource注解是又J2EE提供,而@Autowired是由spring提供,故减少系统对spring的依赖建议使用前者;  

        d.@Resource和@Autowired都可以标注字段或者该字段的setter方法之上。

4、自动装配注入方式

        Spring中提供了自动装配依赖对象的机制,但是在实际应用中并不推荐使用自动装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果。自动装配是在配置文件中实现的,配置如下:

<bean id="xxx" class="xxx" autowire="byType">

        只需要配置一个autowire属性即可完成自动装配,不用再配置文件中写<property>,但是在类中还是要生成依赖对象的setter方法。

        Autowire的属性值有如下几种:

        1>byType:按属性类型装配,在容器中寻找该类型匹配的bean,如有多个,则会抛出异常,如果没有找到,则属性值为null;

        2>byName:按属性名称装配,在容器中查询与该属性名称相同的bean,如果没有找到,则属性值为null;

        3>constructor:与byType方式相似,不同之处在与它应用于构造器参数,如果在容器中没有找到与构造器参数类型一致的bean,那么将抛出异常;

        4>autodetect:通过bean类的自省机制(introspection)来决定是使用constructor还是byType的方式进行自动装配。如果没发现匹配的构造器,那么将使用byType的方式。

              


参考:http://blog.sina.com.cn/s/blog_833273af01018xkd.html

           http://blog.csdn.net/qh_java/article/details/44044441

           http://www.cnblogs.com/leiOOlei/p/3713989.html

           http://blog.csdn.net/baple/article/details/17891755

           http://blog.sina.com.cn/s/blog_99201d8901012dzu.html


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

Spring框架中IoC(控制反转)的原理

一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。即软件系统中对象之间的耦合,对...
  • u012561176
  • u012561176
  • 2015年05月26日 12:18
  • 12098

什么叫控制反转(IoC )

IOC(Inversion of Control)控制反转模式,意味着去除所有有该类产生但不由该类直接负责的对象实例,而改由外界传入。由简单的对象开发模式到IOC(控制翻转)1. 依赖注入(Depe...
  • cuker919
  • cuker919
  • 2010年12月07日 12:05
  • 5377

Spring之ioc控制反转(依赖注入)

个人感觉依赖注入比控制反转更好理解,所以下面就只说依赖注入: spring的整体结构示意图: 一、spring 中的概念:  beanFactory容器: 1、容器是spring框架的核心,容器...
  • QH_JAVA
  • QH_JAVA
  • 2015年02月08日 17:16
  • 4295

spring的IOC(控制反转)与AOP(面向切面编程)

一直对spring的两大特性IOC与AOP了解的模模糊糊,不知道怎么区分,怎么理解。 今天看了一篇讲spring常用注解的文章和spring AOP的文章,引用这两个作者的观点,感觉对这两个特性的了...
  • u013538390
  • u013538390
  • 2015年07月02日 18:24
  • 1544

如何理解Spring的控制反转IOC和依赖注入DI思想

首先我们要知道Java应用程序的每个逻辑业务的完成需要多个类的相互合作。例如有一个Student对象需要使用Course对象,通常需要在Student对象中new一个Course对象,然后再调用Cou...
  • FFFLLLLLL
  • FFFLLLLLL
  • 2016年07月04日 15:56
  • 641

Spring的控制反转(IoC)和面向切面编程(AOP)的概念浅析。

转自:http://baike.baidu.com/link?url=BhIdxXwp9Xs7PjzebLL8sisJrgh0MiLUB2clkgpf-rpyexxlKTOXDDCxmX1MYe4qe...
  • suyu_yuan
  • suyu_yuan
  • 2016年09月18日 21:57
  • 4287

spring——控制反转简单例子

http://www.cnblogs.com/liangjq/p/3994141.html
  • Baple
  • Baple
  • 2014年10月08日 16:24
  • 6500

【Spring】Spring的IOC(控制反转)/DI(依赖注入)原理(一):用到“反射”编程

1. IoC理论的背景 我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。 图1:软件系统中耦合的对象 ...
  • cangchen
  • cangchen
  • 2015年04月08日 22:25
  • 1871

Spring 用注解实现IOC控制反转

1、建包cn.google.annotation.scan    用于类扫描     2、导入 Student.java,Person.java,applicationContext-scan-ann...
  • hlbt0112
  • hlbt0112
  • 2015年10月03日 21:29
  • 221

Spring入门--控制反转(IOC)与依赖注入(DI)

1.控制反转(Inversion of Control)与依赖注入(Dependency Injection) 控制反转即IoC (Inversion of Control),它把传统上由程序代码直接...
  • hy6688_
  • hy6688_
  • 2014年10月31日 18:46
  • 9281
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Spring的控制反转(IOC)
举报原因:
原因补充:

(最多只允许输入30个字)