控制反转IoC(Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。其主要实现方式有两种:
- 依赖查找(Dependency Lookup):容器提供回调接口和上下文环境给组件。
- 依赖注入(Dependency Injection):组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。后者是时下最流行的IoC类型,其又有接口注入(Interface Injection),设值注入(Setter Injection)和构造子注入(Constructor Injection)三种方式。
一、接口注入
public class ClassA {
private InterfaceB clzB;
public void doSomething() {
Ojbect obj = Class.forName(Config.BImplementation).newInstance();
clzB = (InterfaceB)obj;
clzB.doIt();
}
}
ClassA依赖于InterfaceB的实现,传统的方法是在代码中创建 InterfaceB实现类的实例,ClassA在编译期即依赖于InterfaceB的实现。
我们预先在配置文件中设定的实现类的类名Config.BImplementation,动态加载实现类,并通过InterfaceB强制转型后为ClassA 所用,将调用者与实现者在编译期分离。
二、setter注入
先定义一个接口IOutputGenerator.java:
package com.mkyong.output;
public interface IOutputGenerator
{
public void generateOutput();
}
1、CsvOutputGenerator.java
package com.mkyong.output.impl;
import com.mkyong.output.IOutputGenerator;
public class CsvOutputGenerator implements IOutputGenerator {
public void generateOutput() {
System.out.println("This is Csv Output Generator");
}
}
2、OutputHelper.class
package com.mkyong.output;
import com.mkyong.output.IOutputGenerator;
public class OutputHelper {
IOutputGenerator outputGenerator;
public void generateOutput() {
outputGenerator.generateOutput();
}
//DI via setter method
public void setOutputGenerator(IOutputGenerator outputGenerator) {
this.outputGenerator = outputGenerator;
}
}
3、配置Spring,在SpringBeans.xml中
把CsvOutputGenerator
注入到OutputHelper
<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-2.5.xsd">
<bean id="OutputHelper" class="com.mkyong.output.OutputHelper">
<property name="outputGenerator" ref="CsvOutputGenerator" />
</bean>
<bean id="CsvOutputGenerator" class="com.mkyong.output.impl.CsvOutputGenerator" />
</beans>
4、运行,在App.java中
package com.mkyong.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mkyong.output.OutputHelper;
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"SpringBeans.xml");
OutputHelper output = (OutputHelper)context.getBean("OutputHelper");
output.generateOutput();
}
}
5、结果
This is Csv Output Generator
三、构造子注入
1、CsvOutputGenerator.java
package com.mkyong.output.impl;
import com.mkyong.output.IOutputGenerator;
public class JsonOutputGenerator implements IOutputGenerator {
public void generateOutput() {
System.out.println("This is Json Output Generator");
}
}
2、OutputHelper.java
package com.mkyong.output;
import com.mkyong.output.IOutputGenerator;
public class OutputHelper {
IOutputGenerator outputGenerator;
public void generateOutput() {
outputGenerator.generateOutput();
}
//DI via constructor
public OutputHelper(IOutputGenerator outputGenerator){
this.outputGenerator = outputGenerator;
}
}
3、Spring配置
<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-2.5.xsd">
<bean id="OutputHelper" class="com.mkyong.output.OutputHelper">
<constructor-arg>
<ref bean="JsonOutputGenerator" />
</constructor-arg>
</bean>
<bean id="JsonOutputGenerator" class="com.mkyong.output.impl.JsonOutputGenerator" />
</beans>
4、运行,结果
This is Json Output Generator
当有两个构造函数,且参数个数一样但参数类型不同时,则会出现构造器注入参数类型歧义的问题。下面举出另外一个例子:
Example构造子注入
1、在Customer.java中
package com.mkyong.common;
public class Customer
{
private String name;
private String address;
private int age;
public Customer(String name, String address, int age) {
this.name = name;
this.address = address;
this.age = age;
}
public Customer(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
//getter and setter methods
public String toString(){
return " name : " +name + "\n address : "
+ address + "\n age : " + age;
}
}
2、Spring配置,Spring-Customer.xml
<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-2.5.xsd">
<bean id="CustomerBean" class="com.mkyong.common.Customer">
<constructor-arg>
<value>mkyong</value>
</constructor-arg>
<constructor-arg>
<value>188</value>
</constructor-arg>
<constructor-arg>
<value>28</value>
</constructor-arg>
</bean>
</beans>
3、运行,在App.java中
package com.mkyong.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"Spring-Customer.xml"});
Customer cust = (Customer)context.getBean("CustomerBean");
System.out.println(cust);
}
}
4、结果
name : mkyong
address : 28
age : 188
在Spring,参数188
可以转换成int,所以Spring直接转换了它并用了第二个构造函数。
5、解决办法
在Spring-Customer.xml中给constructor-arg加入type。
<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-2.5.xsd">
<bean id="CustomerBean" class="com.mkyong.common.Customer">
<constructor-arg type="java.lang.String">
<value>mkyong</value>
</constructor-arg>
<constructor-arg type="java.lang.String">
<value>188</value>
</constructor-arg>
<constructor-arg type="int">
<value>28</value>
</constructor-arg>
</bean>
</beans>