从零开始造Spring02---实现setter注入

举报
码农飞哥 发表于 2021/05/29 13:11:19 2021/05/29
【摘要】 前言 本节我们将要学习如何实现setter 注入。此博文是学习刘欣老师《从零开始造Spring》的学习笔记。 为啥要实现setter 注入 在上一篇博客中我们实现了Bean实例的生成,但是 Bean与Bean之间的依赖关系我们还没有实现,例如:当A类(Bean) 依赖于B类(Bean)时,我们就需要将B类的实例注入到A类中。常见的注入方式有三种: - sette...

前言

本节我们将要学习如何实现setter 注入。此博文是学习刘欣老师《从零开始造Spring》的学习笔记。

为啥要实现setter 注入

在上一篇博客中我们实现了Bean实例的生成,但是 Bean与Bean之间的依赖关系我们还没有实现,例如:当A类(Bean) 依赖于B类(Bean)时,我们就需要将B类的实例注入到A类中。常见的注入方式有三种:
- setter 注入
- 构造器注入
- 接口注入
接口注入不常用,此处我们主要实现setter注入和构造器注入。首先,我们这篇博客主要讲的就是如何实现setter注入。

数据结构的表达

xml中的配置

  <bean id="petStoreService" class="com.jay.spring.service.v2.PetStoreService"> <property name="accountDao" ref="accountDao"/> <property name="itemDao" ref="itemDao"/> <property name="owner" value="xiangwei"/> <property name="version" value="2"/> </bean> <bean id="accountDao" class="com.jay.spring.dao.v2.AccountDao"></bean> <bean id="itemDao" class="com.jay.spring.dao.v2.ItemDao"/>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

思考:我们使用BeanDefinition表达了<bean>标签中的id,class。 那么对于property 怎么表达?ref 怎么表达? value 怎么表达?
我们新建一个PropertyValue类用于存放<property> 标签中的name和 ref 或者value。
类图如下:
setter注入数据结构表达
如果是ref 类型的我们还需要将其实例名保存到RuntimeBeanReference中,如果是value 类型的我们需要将其值保存到TypedStringValue
数据结构类图

关键代码实例

  1. GenericBeanDefinition 类,存放property的数据结构
public class GenericBeanDefinition implements BeanDefinition{ private String beanClassName; private List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>(); public String getBeanClassName() { return this.beanClassName; } @Override public List<PropertyValue> getPropertyValues() { return this.propertyValueList; }
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. XmlBeanDefinitionReader 类,主要用于解析xml,读取其中的配置
public class XmlBeanDefinitionReader { public static final String PROPERTY_ELEMENT = "property"; public static final String REF_ATTRIBUTE = "ref"; public static final String VALUE_ATTRIBUTE = "value"; public static final String NAME_ATTRIBUTE = "name"; public void parsePropertyElement(Element beanElem, BeanDefinition bd) { Iterator iterator = beanElem.elementIterator(PROPERTY_ELEMENT); while (iterator.hasNext()) { Element propElem = (Element) iterator.next(); // 取出name 元素 String propertyName = propElem.attributeValue(NAME_ATTRIBUTE); // 元素如果为空直接返回 if (!StringUtils.hasLength(propertyName)) { logger.fatal("Tag 'property' must have a 'name' attribute"); return; } Object val = parsePropertyValue(propElem, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); bd.getPropertyValues().add(pv); } } public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; //分别取出ref属性和value属性。 boolean hasRefAttribute = (ele.attribute(REF_ATTRIBUTE) != null); boolean hasValueAttribute = (ele.attribute(VALUE_ATTRIBUTE) != null); if (hasRefAttribute) { String refName = ele.attributeValue(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { logger.error(elementName + " contains empty 'ref' attribute"); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); return ref; } else if (hasValueAttribute) { TypedStringValue typedStringValue = new TypedStringValue(ele.attributeValue(VALUE_ATTRIBUTE)); return typedStringValue; } else { throw new RuntimeException(elementName + " must specify a ref or value"); } }

}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

读取出来ref 或者value之后,我们如何将其实例化呢?对此我们新建了一个类BeanDefinitionValueResolve来处理value 的值。
代码如下:

public class BeanDefinitionValueResolve { private final BeanFactory beanFactory; public BeanDefinitionValueResolve(BeanFactory beanFactory) { this.beanFactory = beanFactory; } public Object resolveValueIfNecessary(Object value) { if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; String refName = ref.getBeanName(); Object bean = this.beanFactory.getBean(refName); return bean; } else if (value instanceof TypedStringValue) { return ((TypedStringValue) value).getValue(); } else { throw new RuntimeException("the value " + value + " has not implemented"); } }
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

那么如果获取bean,并且设置属性呢:
关键代码如下:(DefaultBeanFactory类中)

  public Object createBean(BeanDefinition bd) {
// 创建实例 Object bean = instantiateBean(bd); //设置属性 populateBean(bd, bean);
// populateBeanUseCommonBeanUtils(bd,bean); return bean; } private Object instantiateBean(BeanDefinition bd) { ClassLoader beanClassLoader = this.getBeanClassLoader(); String beanClassName = bd.getBeanClassName(); try { Class<?> clz = beanClassLoader.loadClass(beanClassName); return clz.newInstance(); } catch (Exception e) { throw new BeanCreationException("create bean for "+ beanClassName +" failed",e); } } /** * 调用set方法进行setter注入 * @param bd * @param bean */ protected void populateBean(BeanDefinition bd, Object bean) { // 获取一个bean下所有的PropertyValue List<PropertyValue> pvs = bd.getPropertyValues(); if (pvs == null || pvs.isEmpty()) { return; } // 实例化BeanDefinitionValueResolve,并传入当前的DefaultBeanFactory BeanDefinitionValueResolve valueResolve = new BeanDefinitionValueResolve(this); SimpleTypeCoverter coverter = new SimpleTypeCoverter(); try { for (PropertyValue pv : pvs) { String propertyName = pv.getName(); //对于ref来说就是beanName,对于value 来说就是value Object originalValue = pv.getValue(); Object resolvedValue = valueResolve.resolveValueIfNecessary(originalValue);
// 假设现在originalValue表示的是ref=accountDao,已经通过resolve得到了accountDao对象,接下来
// 如何调用petStoreService的setAccountDao方法?
// 注释:使用到了java.beans 中的Introspector类拿到bean的相关信息,包括其属性,方法 BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { if (pd.getName().equals(propertyName)) { Object convertedValue = coverter.convertIfNecessary(resolvedValue, pd.getPropertyType()); //通过反射的方式调用set方法 pd.getWriteMethod().invoke(bean, convertedValue); break; } } } } catch (Exception e) { throw new BeanCreationException("Failed to obtain BeanInfo for class["+bd.getBeanClassName()+"]",e); } }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

调试如下:
BeanInfo调试

类型转化

类图如下:
类型转化类图
解析说明:
TypeConverter 类型转化接口,将传入的值转化为其需要的类型。
SimpleTypeCoverter 是TypeConverter接口的一个实现。其依赖于java.beans中的PropertyEditor,其类似于java GUI中的编程,例如:拖拽一个button, 然后,设置其颜色,长度,宽度,这些都属于button的属性,在java.beans中将这些抽象成了一个PropertyEditor 接口。 setAsText(), 例如button 的高度,值是什么跟属性的类型密切相关。

代码示例

public class CustomNumberEditor extends PropertyEditorSupport { private final Class<? extends Number> numberClass; private final boolean allowEmpty; private final NumberFormat numberFormat; public CustomNumberEditor(Class<? extends Number> numberClass, boolean allowEmpty) { this(numberClass, allowEmpty,null); } public CustomNumberEditor(Class<? extends Number> numberClass, boolean allowEmpty, NumberFormat numberFormat) { if (numberClass == null || !Number.class.isAssignableFrom(numberClass)) { throw new IllegalArgumentException("Property class must be a subclass of Number"); } this.numberClass = numberClass; this.allowEmpty = allowEmpty; this.numberFormat = numberFormat; } @Override public void setAsText(String text) throws IllegalArgumentException { if (this.allowEmpty && !StringUtils.hasText(text)) { setValue(null); } else if (this.numberFormat != null) { setValue(NumberUtils.parseNumber(text, this.numberClass, this.numberFormat)); } else { setValue(NumberUtils.parseNumber(text, this.numberClass)); } } public void setValue(Object value) { if (value instanceof Number) { super.setValue(NumberUtils.convertNumberToTargetClass((Number) value, this.numberClass)); } else { super.setValue(value); } } public String getAsText() { Object value = getValue(); if (value == null) { return ""; } else if (this.numberFormat != null) { return this.numberFormat.format(value); } else { return value.toString(); } }
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

SimpleTypeConverter 类

 /** * 值的类型转化 * @param value  3 * @param requiredType  Integer.class * @param <T> * @return * @throws TypeMismatchException */ @Override public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException { // 值能不能直接赋值呢,能,直接返回 if (ClassUtils.isAssignableValue(requiredType, value)) { return (T) value; } else {
// 只支持字符串 if (value instanceof String) { PropertyEditor defaultEditor = findDefaultEditor(requiredType); try { defaultEditor.setAsText((String) value); } catch (IllegalArgumentException e) { //非法参数异常 throw new TypeMismatchException(value, requiredType); } return (T) defaultEditor.getValue(); } else { throw new RuntimeException("Todo : can't convert value for "+value +" class:"+requiredType); } } } private PropertyEditor findDefaultEditor(Class<?> requiredType) { // 查找DefaultEditor PropertyEditor defaultEditor = this.getDefaultEditor(requiredType); if (defaultEditor == null) { throw new RuntimeException("Editor for" + requiredType + "has not been implemented"); } return defaultEditor; } public PropertyEditor getDefaultEditor(Class<?> requiredType) { if (defaultEditors == null) { createDefaultEditors(); } return defaultEditors.get(requiredType); } private void createDefaultEditors() { this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64); this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false)); this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true)); this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false)); this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true)); }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

———————————-分割线——————————————————–

使用common-beanutil设置bean的属性

说明: Commons BeanUtils 类的
BeanUtils.setProperty(bean, propertyName,propertyValue)
bean : java对象,例如petStoreService
propertyName,例如: “version”,”accountDao”
propertyValue,例如:3, accountDao对象

代码示例

  private void populateBeanUseCommonBeanUtils(BeanDefinition bd, Object bean) { List<PropertyValue> pvs = bd.getPropertyValues(); if (pvs == null || pvs.isEmpty()) { return; } BeanDefinitionValueResolve valueResolve = new BeanDefinitionValueResolve(this); try { for (PropertyValue pv : pvs) { String propertyName = pv.getName(); Object originalValue = pv.getValue(); Object resolve = valueResolve.resolveValueIfNecessary(originalValue); BeanUtils.copyProperty(bean, propertyName, resolve); } } catch (Exception e) { throw new BeanDefinitionException("Populate bean property failed for["+bd.getBeanClassName()+""); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

———————-答疑总结—————————-

  1. 职责分离的问题:解析的时候只做解析的事,不要做额外的工作。
    这里写图片描述
  2. Open Close 对修改封闭,对扩展开放,例如:AbstractApplicationContext。流程定下来了。通过
 public abstract Resource getResourceByPath(String configFile);

  
 
  • 1
  • 2

进行扩展。
3. 单一职责
4. 对接口编程,不对实现编程
5. 优先使用组合。
6. 现在PropertyValue中value有两种类型,是否可以封装成两种PropertyValue
RuntimeBeanReferencePropertyValue和TypedStringValuePropertyValue呢?
答:
1. PropertyValue类中新增一个resolve的抽象方法

 public abstract Object resolve(BeanFactory beanFactory);
  
 
  • 1
  1. RuntimeBeanReferencePropertyValue类
public class RuntimeBeanReferencePropertyValue extends PropertyValue { public RuntimeBeanReferencePropertyValue(String name,String value) { this.name = name; this.value = value; } @Override public Object resolve(BeanFactory factory) { return factory.getBean(name); }

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  1. TypedStringValuePropertyValue类
public class TypedStringValuePropertyValue extends PropertyValue { public TypedStringValuePropertyValue(String name,String value) { this.name = name; this.value = value; } @Override public Object resolve(BeanFactory factory) { return value; }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  1. XmlBeanDefinitionReader中做的修改
 public PropertyValue parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; boolean hasRefAttribute = (ele.attribute(REF_ATTRIBUTE) != null); boolean hasValueAttribute = (ele.attribute(VALUE_ATTRIBUTE) != null); if (hasRefAttribute) { String refName = ele.attributeValue(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { logger.error(elementName + " contains empty 'ref' attribute"); } PropertyValue ref = new RuntimeBeanReferencePropertyValue(ele.attributeValue(NAME_ATTRIBUTE),refName); return ref; } else if (hasValueAttribute) { PropertyValue typedStringValue = new TypedStringValuePropertyValue(ele.attributeValue(NAME_ATTRIBUTE),ele.attributeValue(VALUE_ATTRIBUTE)); return typedStringValue; } else { throw new RuntimeException(elementName + " must specify a ref or value"); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

参考源代码

https://github.com/XWxiaowei/spring-learn/releases/tag/testcase-3

文章来源: feige.blog.csdn.net,作者:码农飞哥,版权归原作者所有,如需转载,请联系作者。

原文链接:feige.blog.csdn.net/article/details/80959453

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。