首页 > 技术文章 > Sping框架的IOC特性

television 2018-04-05 10:21 原文

IOC(Inversion of Control):控制反转

以下以课程与老师的安排来介绍控制反转。

一个合理的课程编排系统应该围绕培训的内容为核心,而不应该以具体的培训老师为核心,这样才能在正常授课时可以随意选取合适的老师来上课,而非绑定到一个老师身上。

一、探索IOC

  1、最紧耦合度的编法

public class JavaTrad {
    public void JavaCourse(){
        Bill bill=new Bill();//引入讲师,使讲师直接侵入培训
         bill.teach("Spring IoC章节!");

    }
}
public class Bill {
    public void teach(String saying){
        System.out.println(saying);        
    }
}

老师和课程一一对应,完全耦合,无法做到将代码重心放到课程内容上,而且每次修改讲师后,需要修改源码。

 

  2、较紧耦合度接口式编法

public interface Teacher  {
    public void teach(String saying);
}
public class Bill implements Teacher{
    @Override
    public void teach(String saying){
        System.out.println(saying);        
    }
}
public class JavaTrad {
    public void JavaCourse(){
        Teacher teacher=new Bill();//引入教师接口
        teacher.teach("Spring IoC章节!");//通过接口进行授课
    }
}

Bill讲师实现Teacher接口,利用多态特性,在课程类中让Bill讲师实现Teacher接口。但是这样的话课程类就得和Teacher接口、Bill讲师类都关联,并没有达到我们所期望的授课内容仅依赖与课程类的目的。

所以我们仍然需要找到一个方法来松耦合。

 

  3、引入中介类chairman来松耦合

public interface Teacher  {
    public void teach(String saying);
}
public class Bill implements Teacher{
    public void teach(String saying){
        System.out.println(saying);        
    }
}
public class JavaTrad{
    private Teacher teacher;
    
    public void javaCourse(){
        teacher.teach("Spring IoC章节!");
    }
    
    public void injectTeacher(Teacher teacher) {//聚合
        // TODO Auto-generated method stub
        this.teacher=teacher;
        
    }
}
public class Chairman {
    public void managerCourse(){
        Teacher teacher=new Bill();
        JavaTrad javatrad=new JavaTrad();
        javatrad.injectTeacher(teacher);
        javatrad.javaCourse();
        
    }
    public static void main(String[] args) {
        new Chairman().managerCourse();
    }

}

课程类中只需要引入一个实现老师接口的对象即可,具体是哪个对象实现了老师接口,课程类不用管,这个工作交给了chairman类来分配,解决了具体讲师类和课程类之间的紧耦合关系,但是每次换讲师之前还得修改chairman代码,所以还可以有所改进。

 

二、IoC概念

  在实际应用开发中,需要尽量避免和降低对象之间的依赖关系,即降低耦合,一般的业务对象之间,业务对象与持久层之间都存在这样或那样的依赖关系。那么,如何降低对象之间的依赖关系,IoC正式解决这个问题。

  在传统的实现中,由程序内部的代码来控制对象之间的关系。当一个对象需要依赖另一个对象时,我们用new来创建它的依赖对象,实现两个组件的组合关系。这种实现方式会造成组件之间的耦合。控制反转是指应用程序中对象的创建、销毁等不再由程序本身编码实现,而是由外部的Spring容器来控制,所以称为控制反转。这种控制权的转移带来的好处是降低了对象简单间的依赖关系,即实现了解耦。

  IoC的字面意思是控制反转,它包括两个内容:其一就是控制,其二就是反转。控制就选择被调用者类的控制权,反转指这种控制权从具体的调用者类中移除,转而交给一个装配者手中了。

  在软件模式中具体表现为:某一个接口具体实现类的选择控制权从调用者类中移除,转交给第三方仲裁。第三方为配置文件,由IOC容器装载配置文件之后,自动映射生成对象及其属性。

  IoC的具体方法是依赖注入DI(Dependency ):组件之间的依赖关系由容器在运行期间决定,形象的说,即由容器动态的将某种依赖关系注入到主键之中。

 

三、依赖注入DI的三种方式

  1、构造函数注入

    在构造器注入中,我们通过在调用者类的构造器中将被调用者对象变量赋值即可完成注入

public class Chairman {
    public void managerCourse(){
        Teacher teacher=new Bill();
        JavaTrad javatrad=new JavaTrad(teacher);    //构造器注入    
        javatrad.javaCourse();        
    }
    public static void main(String[] args) {
        new Chairman().managerCourse();
    }

}
public class JavaTrad  {
    private Teacher teacher;

    public  JavaTrad(Teacher teacher){//构造函数注入方法
        this.teacher=teacher;
    }
    public void javaCourse(){
        this.teacher.teach("Spring IoC章节!");
    }
}

 

  2、set属性注入方式

     在set属性注入中,我们通过使用调用者的set方法将teacher的具体对象传入teacher中,即可完成注入。 

<?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:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="    
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
    <bean name="teacher" class="com.weikun.IoC6.Bill"></bean>
    <bean id="trad" class="com.weikun.IoC6.JavaTrad">
        <property name="teacher" ref="teacher"></property>
    </bean>
</beans>

 

public class JavaTrad{
    private Teacher teacher;

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public void JavaCourse(){
        this.teacher.teach("Spring IoC章节!");
    }
}
public class Test {
    public static void main(String[] args) {
        Resource resource=new ClassPathResource("applicationContext1.xml");//容器
        BeanFactory beanFactory=new XmlBeanFactory(resource);
        JavaTrad javaTrad=(JavaTrad)beanFactory.getBean("trad");
     Teacher teacher=(Teacher)beanFactory.getBean("teacher");
javaTrad.getTeacher().teach("asd"); } }

 

  3、接口注入方式

    让调用者也实现一个接口,接口中定义一个接口注入方法,在具体调用者类中实现它,从而完成属性的注入(方法类似于自定义一个setter方法)

public class JavaTrad  implements Trad{
    private Teacher teacher;
    
    public void JavaCourse(){
        this.teacher.teach("Spring IoC章节!");        
    }
    public void injectTeacher(Teacher teacher) {//接口属性注入方式
        // TODO Auto-generated method stub
        this.teacher=teacher;
    }
}

public interface Trad {
void injectTeacher(Teacher teacher);
}

   

  4、四种依赖注入方式的对比

    1)、接口注入

      从注入方式的使用上来说,接口注入是不提倡的一种方式,极少被用户使用。因为它强制被注入对象实现容器的接口,带有“侵入性”,而构造方法注入和setter方法注入则不需要如此。

    2)、setter注入

      这种注入方式与传统的JavaBean写法很相似,程序员更容易理解和接受,通过setter方式设定依赖关系显得更加直观自然。

      缺点是组件使用者或许会忘记组件注入需要的依赖关系,同时依赖可能会因为setter方法的调用而被修改。

    3)、构造注入

      这种方法的优点是:构造注入可以在构造器中决定依赖关系的注入顺序。依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系。对组件的调用者而言,组件内部的依赖关系完全透明。

      缺点:对于复杂的依赖关系如果采用构造注入,会导致构造器过于臃肿,难以阅读。

  

  5、IoC的装载机制

    1、IoC容器

      Spring提供了强大的IoC容器来管理组成应用程序中的Bean,要利用容器提供的服务,就必须配置Bean,配置文件描述了Bean的定义和他们之间的依赖关系。Spring通过大量引入了Java的反射机制动态生成Bean对象并注入到程序中避免硬编码,实现了该功能的核心组件是BeanFactory,而ApplicationContext继承了BeanFactory接口,提供了更多的高级特性,建议优先使用后者。

      ApplicationContext的实现类如下:

      1)、ClassPathXmlApplicationContext:从CLASSPATH下加载文件

      2)、FileSystemXmlApplicationContext:从文件系统中加载文件  

    首先,可以通过任一实现类来将配置文件中定义的Bean加载到容器中,如:(这里的ctx就是容器)

      1)、ApplicationContext ctx=new ClassPathXmlApplicationContext("bean.xml");

       2)、ApplicationContext ctx=new FileSystemXmlApplicationContext("classpath:bean.xml");

    然后,通过下列语句获取Bean的实例

      Moveable animal=(Moveable) ctx.getBean("animal");

  

  6、IoC的零配置注解写法

    如果Bean不是自己编写的类(如SessionFactory),注解配置将无法实施,此时XML配置是唯一可以使用的方式

    1)、@Autowired

        它对类的成员变量、方法以及构造方法进行标注,完成自动装配的工作。

        

import org.springframework.beans.factory.annotation.Autowired;
public class Animal{
  Private MessagePriter printer; @Autowired
public void setPrinter(MessagePriter printer){ this.priter=printer; } }
<?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:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"

       xsi:schemaLocation="
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
       ">
    <bean id="movePrinter" class="com.zxc.A.movePriter"></bean>
    <bean id="animal" class="com.zxc.A.JavaTrade">
        <property name="animalType" value="Bird"/>
        <property name="moveMode" value="fly"/>
    </bean>
</beans>

 

    2)、@Qualifier  

        如果一个bean的属性可能来自多个其他的候选bean,导致Spring无法确定使用那一个bean,当Spring容器在启动时就会抛出BeanCreationException异常。Spring允许我们通过@Qualifie注解指定注入bean的名称。

import org.springframework.beans.factory.annotation.Autowired;
public class Animal{
     @Autowired
  Private @Qualifier(value="screen")MessagePriter printer;
}    

        通常@Qualifer和@Autowired结合使用,且置于成员变量类型的前面。

    3)、@Resource

        @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入的,而@Resource默认按byName自动注入罢了。

    4)、@Component

        虽然可以通过@Autowired或@Resource在Bean类中使用自动注入功能,但是还是需要在XML中定义<bean>。而通过注解的@Component就可以通过注解就定义Bean,在类前加上@Component

推荐阅读