读懂Spring核心系列3(自动装配属性)
上一篇中,我们的代码已经可以使用工厂实例化一个bean并放到容器中。但是还剩下一个问题,我们实例化的bean还没有属性,或者定义了属性,但没有为属性赋值。在这一篇的代码中,我们将重构上一篇中出现的几个类,并添加两个新类,最终完成bean属性的初始化。
首先是一个保存bean属性字段和值的配置类。
/** * 用于bean的属性注入 * @author yihua.huang@dianping.com */ public class PropertyValue { private final String name; private final Object value; public PropertyValue(String name, Object value) { this.name = name; this.value = value; } public String getName() { return name; } public Object getValue() { return value; } }
然而一个bean可能有多个属性,所以还需要一个类来维护一个属性列表。
/** * 包装一个对象所有的PropertyValue。<br/> * 为什么封装而不是直接用List?因为可以封装一些操作。 * @author yihua.huang@dianping.com */ public class PropertyValues { private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>(); public PropertyValues() { } public void addPropertyValue(PropertyValue pv) { //TODO:这里可以对于重复propertyName进行判断,直接用list没法做到 this.propertyValueList.add(pv); } public List<PropertyValue> getPropertyValues() { return this.propertyValueList; } }
在上一篇中出现的BeanDefinition类中需要加入Property这个类作为关联的对象,用来保存Bean的属性值,为初始化做准备。
/** * bean的内容及元数据,保存在BeanFactory中,包装bean的实体 * @author yihua.huang@dianping.com */ public class BeanDefinition { private Object bean; private Class beanClass; private String beanClassName; private PropertyValues propertyValues; //注意新加入的关联对象 public BeanDefinition() { } public void setBean(Object bean) { this.bean = bean; } public Class getBeanClass() { return beanClass; } public void setBeanClass(Class beanClass) { this.beanClass = beanClass; } public String getBeanClassName() { return beanClassName; } public void setBeanClassName(String beanClassName) { this.beanClassName = beanClassName; try { this.beanClass = Class.forName(beanClassName); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public Object getBean() { return bean; } public PropertyValues getPropertyValues() { return propertyValues; } public void setPropertyValues(PropertyValues propertyValues) { this.propertyValues = propertyValues; } }
上一篇中bean的实例化在AutowireCapableBeanFactory这个类里面完成,所以我们重构这个类,在初始化的时候为属性赋值。
/** * 可自动装配内容的BeanFactory * * @author yihua.huang@dianping.com */ public class AutowireCapableBeanFactory extends AbstractBeanFactory { @Override protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception { Object bean = createBeanInstance(beanDefinition); applyPropertyValues(bean, beanDefinition); return bean; } protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception { return beanDefinition.getBeanClass().newInstance(); } /** * 在这里实现了bean属性的赋值操作 * @param bean * @param mbd * @throws Exception */ protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception { for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); declaredField.setAccessible(true); declaredField.set(bean, propertyValue.getValue()); } } }
于是,结合上一篇的代码,我们这一小个系列所要完成的目标就此实现啦。。。
当然,测试代码中需要给出bean属性的值,下面就是测试代码。
/** * @author yihua.huang@dianping.com */ public class BeanFactoryTest { @Test public void test() throws Exception { // 1.初始化beanfactory BeanFactory beanFactory = new AutowireCapableBeanFactory(); // 2.bean定义 BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService"); // 3.设置属性 PropertyValues propertyValues = new PropertyValues(); propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!")); beanDefinition.setPropertyValues(propertyValues); // 4.生成bean beanFactory.registerBeanDefinition("helloWorldService", beanDefinition); // 5.获取bean HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); helloWorldService.helloWorld(); } }
当然,最后要附上代码的UML啦,这个图就包括上一篇提及的代码加上这一篇的
可以看到,HelloWorldService和框架类没有关联,仅仅通过配置文件进行装配,符合松散的耦合关系。
当然,整个一次的配置只是一个类,没有写到XML里面。在下一篇中我们将实现XML的配置。