关于spring容器依赖注入接口的小问题

看了一个帖子,题主和答复非常好,特别记录下。


先看一段代码
假设你编写了两个类,一个是人(Person),一个是手机(Mobile)。
人有时候需要用手机打电话,需要用到手机的dialUp方法。
传统的写法是这样:

public class Person{
    public boolean makeCall(long number){
        Mobile mobile=new Mobile();
        return mobile.dialUp(number);
    }
}

也就是说,类Person的makeCall方法对Mobile类具有依赖,必须手动生成一个新的实例new Mobile()才可以进行之后的工作。
依赖注入的思想是这样,当一个类(Person)对另一个类(Mobile)有依赖时,不再该类(Person)内部对依赖的类(Moblile)进行实例化,而是之前配置一个beans.xml,告诉容器所依赖的类(Mobile),在实例化该类(Person)时,容器自动注入一个所依赖的类(Mobile)的实例。
接口:

public Interface MobileInterface{
    public boolean dialUp(long number);
}

Person类:

Java code

public class Person{
    private MobileInterface mobileInterface;
    public boolean makeCall(long number){
        return this.mobileInterface.dialUp(number);
    }
    public void setMobileInterface(MobileInterface mobileInterface){
        this.mobileInterface=mobileInterface;
    }
}

在xml文件中配置依赖关系

<bean id="person" class="Person">
    <property name="mobileInterface">
        <ref local="mobileInterface"/>
    </property>    
</bean>
<bean id="mobileInterface" class="Mobile"/>

这样,Person类在实现拨打电话的时候,并不知道Mobile类的存在,它只知道调用一个接口MobileInterface,而MobileInterface的具体实现是通过Mobile类完成,并在使用时由容器自动注入。
———————————以上是复制过来的代码

Person类中有这个方法—-mobileInterface.dialUp(number)。mobileInterface是个接口,怎么可以直接调用该接口实现类的方法?还有其怎么依赖注入的?
回答:
mobileInterface.dialUp(number)。mobileInterface是个接口,怎么可以直接调用该接口实现类的方法?

Java对类的定型是通过“上溯造型”这样就能确保不会丢失精度,保证类型的统一。这也是接口的一个好处。
在进行方法调用的时候,具体调用的是什么类的方法是通过动态绑定的。
也就是说虽然mobileInterface.dialUp(number)表面上看是调用的接口的方法,这样会出错。但是在运行期间mobileInterface一般会被动态绑定到一个实现类的,如果没绑定这样也确实会出错。
下面看看你的beans.xml的配置

<bean id="person" class="Person">
    <property name="mobileInterface">
        <ref local="mobileInterface"/>
    </property>    
</bean>
<bean id="mobileInterface" class="Mobile"/>

这里显然在最后个bean中为mobileInterface指定的是一个实现类Mobile。也就是说Mobile的对象调用dialUp(number)是具有意义的,而且可行的。spring在这个过程中“偷天换柱”的把接口动态绑定到了Mobile的对象上。也就是说mobileInterface.dialUp(number)在调用时实际上调用的是Mobile对象的dialUp(number)方法了。这样就可以显示正常结果了。

还有其怎么依赖注入的?
这个就简单了,用的是java的反射机制。在spring配置文件加载期间,spring会用反射机制给bean中配置的对象属性注入属性值。
他是怎么注入的呢?看配置文件:

<bean id="person" class="Person">
    <property name="mobileInterface">
        <ref local="mobileInterface"/>
    </property>    
</bean>
<bean id="mobileInterface" class="Mobile"/>

第一句 <bean id="person" class="Person">告诉spring我们这里有个bean他的类是Person,我给他的唯一id是person,此时spring会new一个Person对象,放在结果的Map中,对应的key就是你这里配置的id。所以你可以get(id)的形式来获取对象。
第二句 <property name="mobileInterface">说明我前面new的Person对象中有一个属性是mobileInterface
第三句<ref local="mobileInterface"/>是给属性赋值(调用对象的set方法,如果没有set方法会报错),他指向一个引用,看到这里spring会延迟赋值(你可以这样认为)
第四句 <bean id="mobileInterface" class="Mobile"/>创建一个id是mobileInterface的Mobile对象,这样我们再回到第三句就可以通过id进行对应的引用了。也就是说第三句的功能就实现了,把Mobile对象赋值给了Person对象的mobileInterface属性。严格说就是id为person的Person对象的mobileInterface属性指向了id为mobileInterface的Mobile对象(这里的一系列操作是spring帮我们完成的,对属性赋值需要调用属性的set方法)。有点绕~ 好好理解。
上面就相当于

Person person = new Person();
Mobile mobile = new Mobile();
person .setMobileInterface(mobile);

在person中调用mobileInterface.dialUp(number)的时候,实际上是调用了mobile的dialUp(number)方法了

还有为什么Person 类里面要调用接口的的方法呢mobileInterface.dialUp(number);
先来假设下:你有很多手机(诺基亚,Iphone,三星,华为。。。。)
他们打电话的时候拨号的方式不一样,如果你这里写成一个具体的手机的dialUp方法。比如说
诺基亚.dialUp(number),看上去很好。没有理解问题,但是如果你换手机了呢?你突然发财了换了Iphone ,那你岂不是要改变下这个类换成 Iphone.dialUp(number)?…如此下去你岂不是每换一次手机就要改下这个类。很麻烦是不是?

其实你研究会发现 这些手机不管是什么牌子的都能打电话(都具有dialUp(number)方法),只是拨号的方式不一样而已,但是功能都是为了打电话。何不把这个功能提取出来?对了,你可以这样做。我现在知道我可以用手机打电话,具体怎么打我不管他,在我打电话的时候我再决定。(也就是前面提到的在真正的运行期的时候来动态绑定,看具体调用什么对象的方法)。是不是一举N得?以后我都不需要改变这个类了。真正做到了以不变应万变。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值