前面两篇是最简单的通过反射创建实例和简单赋值功能的实现,这篇的任务是能装配引用类型的属性。
<?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.2.xsd">
<bean id="gordon" class="gordon.study.spring.sample.Employee">
<property name="name" value="Gordon" />
<property name="age" value="32" />
<property name="male" value="true" />
<property name="birthday" value="1982-07-05" />
<property name="salary" value="817.52" />
<property name="dept" ref="lab" />
</bean>
<bean id="ghost" class="gordon.study.spring.sample.Employee">
<property name="name" value="Ghost" />
<property name="dept" ref="warehouse" />
</bean>
<bean id="elf" class="gordon.study.spring.sample.Employee">
<property name="name" value="Elf" />
<property name="dept" ref="lab" />
</bean>
<bean id="robot" class="gordon.study.spring.sample.Employee"
scope="prototype">
<property name="name" value="Robot" />
<property name="dept" ref="warehouse" />
</bean>
<bean id="lab" class="gordon.study.spring.sample.Department">
<property name="name" value="Research Lab" />
</bean>
<bean id="warehouse" class="gordon.study.spring.sample.Department">
<property name="name" value="Warehouse" />
</bean>
</beans>
测试改为junit4
public class Test001 extends TestCase {
public void testSample1() {
try {
ApplicationContext context = new ClassPathXmlApplicationContext("sample1.xml");
Employee gordon = (Employee) context.getBean("gordon");
Employee elf = (Employee) context.getBean("elf");
Employee ghost = (Employee) context.getBean("ghost");
Department lab = (Department) context.getBean("lab");
Department warehouse = (Department) context.getBean("warehouse");
Employee robot1 = (Employee) context.getBean("robot");
robot1.setName("Robot1");
Employee robot2 = (Employee) context.getBean("robot");
robot2.setName("Robot2");
assertEquals("Gordon", gordon.getName());
assertEquals(32, gordon.getAge());
assertEquals(817.52f, gordon.getSalary());
assertEquals(true, gordon.isMale());
assertTrue(gordon != elf);
assertTrue(gordon.getDept() == elf.getDept());
assertTrue(gordon.getDept() == lab);
assertTrue(gordon.getDept() != ghost.getDept());
assertTrue(robot1 != robot2);
assertTrue(robot1.getDept() == robot2.getDept());
assertTrue(robot1.getDept() == warehouse);
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先,BeanProperty类要加上propertyRef字段,xml reader做相应修改。
接着考虑装配引用属性的问题。当mySpring使用者调用getBean时,我们是先初始化一个实例,然后再装配其属性。当装配引用类型的属性时,可能会发现beans这个map里面还没有可用的实例(可能是singleton的尚未初始化,也可能是prototype类型的所以不放入beans),这时容器就得对该引用类型调用getBean,以期望它返回一个可供自己装配的实例。一个典型的递归。
@Override
public Object getBean(String beanName) throws Exception {
if (beans.containsKey(beanName)) {
return beans.get(beanName);
}
Object bean = createBean(beanName);
if (beanDefinitions.get(beanName).getScope() == 0) {
beans.put(beanName, bean);
}
assembleBean(beanName, bean);
return bean;
}
private void assembleBean(String beanName, Object targetBean) throws Exception {
BeanDefinition beanDef = beanDefinitions.get(beanName);
Method[] methods = targetBean.getClass().getMethods();
for (BeanProperty prop : beanDef.getProperties()) {
String beanRef = prop.getPropertyRef();
if (beanRef != null && beanRef.length() > 0) {
Object refBean = getBean(beanRef);
Method setterMethod = findSetterMethod(methods, targetBean.getClass(), prop.getPropertyName());
setterMethod.invoke(targetBean, refBean);
}
}
}