设置注入是指IOC容器通过使用属性的setter方法来注入被依赖的实列。还是使用“人”和“斧子”的列子。
定义一个Person接口,人都会使用“斧子”:
package com.custle.spring;
public interface Person {
//人使用斧子
public void useAxe();
}
定义一个“斧子”接口,该接口有一个“砍材”动作:
package com.custle.spring;
public interface Axe {
//斧子接口有个砍的方法
public String chop();
}
具体的“中国人”实现Person接口:
package com.custle.spring;
public class Chinese implements Person {
private Axe axe;
//注入所需要的斧子
public void setAxe(Axe axe) {
this.axe = axe;
}
@Override
public void useAxe() {
System.out.println(axe.chop());
}
}
具体的“石头斧子”实现Axe接口:
package com.custle.spring;
public class StoneAxe implements Axe {
@Override
public String chop() {
return "使用石斧砍材";
}
}
这个时候程序并不知道Chinese使用的斧子是什么类型的斧子,此时需要通过Spring来注入“斧子”至Chinese中,我们通过setAxe()来设值注入。
<?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.xs">
<!-- 将stoneAxe注入chinese中的axe属性相当于setAxe(stoneAxe)-->
<bean id="chinese" class="com.custle.spring.Chinese">
<property name="axe" ref="stoneAxe"></property>
</bean>
<!-- 将StoneAxe交给Srping管理,不然Spring怎么知道你要哪种类型的斧子呢 -->
<bean id="stoneAxe" class="com.custle.spring.StoneAxe"/>
</beans>
可以看到Spring管理Bean的灵活性,Bean与Bean之间的依赖关系放在配置文件中,而不是通过代码。通过配置文件的指定,Spring能精确的为每个Bean注入属性。通过配置文件的指定,Spring能精确的为每个Bean注入属性。因此<bean>里面的class属性不能为接口,必须为实现类。
将 Chinese类进行修改,研究Spring具体是怎么实现设值注入的过程:
package com.custle.spring;
public class Chinese implements Person {
private Axe axe;
private String axe1;
//通过property注入必须含有无参构造方法
public Chinese() {
}
//注入StoneAxe石斧子类型,注意这里为Axe的实现类
public void setAxe(StoneAxe axe) {
this.axe = axe;
System.out.println("axe="+axe.chop());
}
//注入String类型
public void setAxe1(String axe1) {
this.axe1 = axe1;
System.out.println("axe1="+axe1);
}
}
测试方法:
public static void main(String[] args) throws Exception {
//获取Chinese的Class对象
Class chinese = Class.forName("com.custle.spring.Chinese");
Class stoneAxe = Class.forName("com.custle.spring.StoneAxe");
//创建Chinese的默认实列
Object bean = chinese.newInstance();
Object stoneAxebean = stoneAxe.newInstance();
//获取axe属性对应的setAxe1()方法,参数类型为String
Method method = chinese.getMethod("setAxe1", String.class);
//获取axe属性对应的setAxe()方法,参数类型为StoneAxe
Method method1 = chinese.getMethod("setAxe", stoneAxe);
//调用bean实列的setAxe1()方法
method.invoke(bean, "hello world");
//调用bean实列的setAxe()方法
method1.invoke(bean, stoneAxebean);
}
控制台输出:
axe1=hello world
axe=使用石斧砍材
若在Chinese类中将setAxe()方法修改为StoneAxe的抽象类Axe,会发现通过反射无法将StoneAxe.Class注入。会报java.lang.NoSuchMethodException异常。
Spring会通过反射自动接管每个<bean>定义里面的<property>,Spring会调用无参构造方法来构造该bean的实列,通过调用对应的setter方法为程序注入属性值,这也是我们为bean设置带参构造器时,最好显示声明无参构造器的原因。每个bean的id唯一,程序通过bean的id访问该bean,bean与bean之间依赖关系也通过id属性关联。
通过Spring工厂去获取Spring中的bean;
package com.custle.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class GetBeanFactory {
private static ApplicationContext applicationContext;
static{
applicationContext = new ClassPathXmlApplicationContext("applicationContext-spring.xml");
}
private static Chinese getInstance(){
//通过配置文件中bean的ID获取对应的bean
Chinese chinese = (Chinese)applicationContext.getBean("chinese");
chinese.useAxe();
return chinese;
}
}
Chinese实列无须知道斧子的具体创建过程,当需要“跟换钢斧子”时,只需要钢斧子实现斧子接口,将该钢斧子注入chinese中即可。