现在的位置: 首页 > 综合 > 正文

读懂Spring核心系列3(自动装配属性)

2018年05月28日 ⁄ 综合 ⁄ 共 3539字 ⁄ 字号 评论关闭

上一篇中,我们的代码已经可以使用工厂实例化一个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的配置。

抱歉!评论已关闭.