Learn Spring - 依赖注入(Dependency Injection)

什么是依赖注入

DependencyInjection

面向对象中,系统功能都是通过一系列对象之间的协作完成的。为了降低系统的复杂度,增加代码的复用性,方便测试。应该尽量降低类与类之间的关联度,降低耦合度。类与类之间的关联很多时候是不可避免的。如上图,如果A依赖于B,那么A中的b对象应该怎么实例化呢?两种方法:

Spring中的依赖注入有两种实现方法。即:

通过构造方法实现

Bean配置格式

<bean id="a" class = "me.coolcodes.A">
    <constructor-arg index="0" ref = "b"></constructor-arg>  <!-- "value"用于常量值,"ref"用于其他Bean     还可以用type表示参数数据类型 -->
</bean>

<bean id="b" class = "me.coolcodes.B"></bean>

通过constructor-arg表示构造函数参数,有几个参数就写几个constructor-arg。顺序必须和对应类的构造方法参数顺序一致。为了避免顺序不一致,可以使用index属性标识顺序。

还可以把第二个bean放到constructor-arg中,变成内部Bean(inner Bean):

<bean id="a" class = "me.coolcodes.A">
    <constructor-arg index="0">
        <bean id="b" class = "me.coolcodes.B"></bean>
    </constructor-arg>
</bean>

Example

类图

classDiagram

目录结构

constructor_project_tree

Code
Beans.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"
       xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id = "classA" class = "me.coolcodes.ClassA">
        <constructor-arg index="0" ref="classB"></constructor-arg>
    </bean>

    <bean id="classB" class="me.coolcodes.ClassB"></bean>
</beans>

ClassA依赖于ClassB

ClassA.java
package me.coolcodes;

public class ClassA {
    private ClassB classB;

    public ClassA(ClassB classB){
        this.classB = classB;
    }

    public void print(){
        System.out.println("I am ClassA");
        classB.print();
    }
}

ClassA在构造方法中通过传入的ClassB对象设置classB

ClassB.java
package me.coolcodes;

public class ClassB {

    public ClassB(){
        // to do
    }

    public void print(){
        System.out.println("I am ClassB");
    }
}
Main.java
package me.coolcodes;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        ClassA classA = (ClassA) context.getBean("classA");

        classA.print();
    }
}

实例化一个ClassA对象的时候,Spring框架会实例化一个ClassB对象,并传给ClassA的构造方法。

结果

result

通过set方法实现

Bean配置格式

<bean id="a" class = "me.coolcodes.A">
    <property name="b" ref="b"></property> <!-- "value"指常量值,"ref"指其他Bean -->
</bean>

<bean id="b" class = "me.coolcodes.B"></bean>

Spring框架通过name调用相应的set方法,并将refvalue作为参数。

还可以把第二个Bean放到property中,变成内部bean(inner bean):

<bean id="a" class = "me.coolcodes.A">
    <property name="b">
        <bean id="b" class = "me.coolcodes.B"></bean>
    </property>
</bean>

Example

类图

classDiagram

目录结构

project_tree

Code
Beans.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"
       xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id = "classA" class = "me.coolcodes.ClassA">
        <property name="classB" ref="classB"></property>
    </bean>

    <bean id="classB" class="me.coolcodes.ClassB"></bean>
</beans>

当实例化ClassA时,Spring框架会创建一个ClassB对象,并调用ClassAsetClassB()方法,以新创建的ClassB对象为参数。

ClassA.java
package me.coolcodes;

public class ClassA {
    private ClassB classB;

    public ClassA(){
        // to do
    }

    public void setClassB(ClassB classB){
        this.classB = classB;
    }

    public void print(){
        System.out.println("I am ClassA");
        classB.print();
    }
}

setClassB()方法负责设置classB的值。

ClassB.java
package me.coolcodes;

public class ClassB {

    public ClassB(){
        // to do
    }

    public void print(){
        System.out.println("I am ClassB");
    }
}
Main.java
package me.coolcodes;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        ClassA classA = (ClassA) context.getBean("classA");

        classA.print();
    }
}
结果

result

总结

两种方法的本质区别在于一个使用构造方法传参数,一个通过set方法传参数。

comments powered by Disqus