'SpringIoc之屬性注入'

文章 設計 設計模式 java那些事 2019-08-03
"
"
SpringIoc之屬性注入

上一篇文章講了SpringIoc之實例對象初始化,讓我們知道了SpringIoc容器是如何初始化對象的,這一篇文章我們講一下對象屬性如何初始化。

BeanDefinition再次重構

為了可以標示屬性,我們需要再次擴展BeanDefinition的功能,使其具有保存對象屬性的能力,因此,我們在BeanDefinition中新增一個屬性PropertyValue propertyValue。PropertyValue有兩個屬性一個name用來表示對象的屬性名稱,一個value用來表示對象的屬性值。PropertyValue代碼如下:

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;
}
}

以上的設計有一個問題,相信聰明的你一定想到了,那就是如果一個對象有多個屬性我們該怎麼表示呢?為了解決這個問題我們決定擴充BeanDefinition的能力,將BeanDefinition的PropertyValue propertyValue屬性重構為PropertyValues propertyValues。PropertyValues只有一個成員變量List<PropertyValue> propertyValueList,這樣propertyValueList就可以解決一個對象有多個屬性的問題。PropertyValues代碼如下:

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的代碼是什麼樣子了

public class BeanDefinition {
private Object bean;
private Class beanClass;
private String beanClassName;
private PropertyValues propertyValues;
...
public PropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
}

限於篇幅原因跟上一篇重複的代碼被我略去了,你可以通過文章末尾的鏈接查看上一章的內容。

屬性自動裝配

因為本篇文章加入了屬性注入的內容,因此我們的AutowireCapableBeanFactory也要有所改進,而對於上一篇講過的抽象工廠則沒有任何改動,它還是隻負責註冊bean和獲取bean。如果你看了上一篇的內容你就會知道我們使用抽象工廠設計模式的AutowireCapableBeanFactory工廠類的作用是createBean。現在,我們只需要加上在對象創建完成後使用反射給對象賦值的功能就好了,因此我們在doCreateBean()方法中調用兩個方法,一個createBeanInstance()用來創建對象實例,一個applyPropertyValues()用來給對象屬性賦值。完整AutowireCapableBeanFactory代碼如下:

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();
}
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());
}
}
}

測試

下面就進入我們熟悉的測試流程,我們這次依然使用HelloWorldService作為測試用例,只不過我們這次要修改一下HelloWorldService的代碼,我們依然會調用它的helloWorld()方法,只不過這次helloWorld()不再簡單地打印hello World,而是打印自己的屬性text。代碼如下:

public class HelloWorldService {
private String text;
public void helloWorld(){
System.out.println(text);
}
public void setText(String text) {
this.text = text;
}
}

接下來我們來寫單元測試,單元測試的第一步仍然是初始化容器對象,第二步定義bean,第三步設置屬性,第四步註冊bean,最後我們獲取bean並調用helloWorld()。

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();
}
}

上一篇:SpringIoc之實例對象初始化

下一篇:待續

"

相關推薦

推薦中...