PropertyEditorSupport
当标签为value,我们并不想只是简单的给String类型赋值。柿子先挑软的捏,我们先给int类型赋值。我们写了一个测试用例:
public class CustomNumberEditorTest {
@Test
public void testConvertString(){
CustomNumberEditor editor = new CustomNumberEditor(Integer.class,true);
editor.setAsText("3");
Object value = editor.getValue();
Assert.assertTrue(value instanceof Integer);
Assert.assertEquals(3,((Integer) editor.getValue()).intValue());
editor.setAsText("");
Assert.assertTrue(editor.getValue() == null);
try {
editor.setAsText("3.1");
}catch (IllegalArgumentException e){
return;
}
Assert.fail();
}
}
这里使用了CustomNumberEditor
类,它继承的java.beans下面的PropertyEditorSupport
,我们通过重写它的setAsText,通过getValue便会把String类型转换成整型。下面给出CustomNumberEditor
代码:
public class CustomNumberEditor extends PropertyEditorSupport {
private final Class<? extends Number> numberClass;
private final NumberFormat numberFormat;
private final boolean allowEmpty;
/**
* 构造方法
* @param numberClass 数据类型
* @param allowEmpty 是否允许为空
* @throws IllegalArgumentException
*/
public CustomNumberEditor(Class<? extends Number> numberClass , boolean allowEmpty)throws IllegalArgumentException{
this(numberClass,null,allowEmpty);
}
/**
* 构造方法
* @param numberClass 数据类型
* @param numberFormat 数据格式
* @param allowEmpty 是否为空
* @throws IllegalArgumentException
*/
public CustomNumberEditor(Class<? extends Number> numberClass, NumberFormat numberFormat ,
boolean allowEmpty)throws IllegalArgumentException{
if (numberClass == null || !Number.class.isAssignableFrom(numberClass)){
throw new IllegalArgumentException("Property class must be a subclass of Number");
}
this.numberClass = numberClass;
this.numberFormat = numberFormat;
this.allowEmpty = allowEmpty;
}
/**
* 设置要转换的字符串
* @param text
* @throws IllegalArgumentException
*/
@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));
}
}
}
此时我们便通过了上述的测试用例。接下来我们实现自动注入boolean类型的数据,当输入on/off,注入true/false;当输入yes/no,注入true/false等。按照惯例我们先给出测试用例:
public class CustomBooleaneditorTest {
@Test
public void testConvertStringToBoolean(){
CustomBooleanEditor editor = new CustomBooleanEditor(true);
editor.setAsText("true");
Assert.assertEquals(true, ((Boolean)editor.getValue()).booleanValue());
editor.setAsText("false");
Assert.assertEquals(false, ((Boolean)editor.getValue()).booleanValue());
editor.setAsText("on");
Assert.assertEquals(true, ((Boolean)editor.getValue()).booleanValue());
editor.setAsText("off");
Assert.assertEquals(false, ((Boolean)editor.getValue()).booleanValue());
editor.setAsText("yes");
Assert.assertEquals(true, ((Boolean)editor.getValue()).booleanValue());
editor.setAsText("no");
Assert.assertEquals(false, ((Boolean)editor.getValue()).booleanValue());
try{
editor.setAsText("aabbcc");
}catch(IllegalArgumentException e){
return;
}
Assert.fail();
}
}
同样我们新建一个类,并继承PropertyEditorSupport重写setAsText方法,我们给出具体的代码示例:
public class CustomBooleanEditor extends PropertyEditorSupport{
public static final String VALUE_TRUE = "true";
public static final String VALUE_FALSE = "false";
public static final String VALUE_ON = "on";
public static final String VALUE_OFF = "off";
public static final String VALUE_YES = "yes";
public static final String VALUE_NO = "no";
public static final String VALUE_1 = "1";
public static final String VALUE_0 = "0";
private final boolean allowEmpty;
public CustomBooleanEditor(boolean allowEmpty) {
this.allowEmpty = allowEmpty;
}
/**
* 设置要转换的字符串
* @param text
* @throws IllegalArgumentException
*/
@Override
public void setAsText(String text) throws IllegalArgumentException {
String input = (text != null ? text.trim() : null);
if (this.allowEmpty && !StringUtils.hasLength(input)) {
// Treat empty String as null value.
setValue(null);
}
else if ((VALUE_TRUE.equalsIgnoreCase(input) || VALUE_ON.equalsIgnoreCase(input) ||
VALUE_YES.equalsIgnoreCase(input) || VALUE_1.equals(input))) {
setValue(Boolean.TRUE);
}
else if ((VALUE_FALSE.equalsIgnoreCase(input) || VALUE_OFF.equalsIgnoreCase(input) ||
VALUE_NO.equalsIgnoreCase(input) || VALUE_0.equals(input))) {
setValue(Boolean.FALSE);
}
else {
throw new IllegalArgumentException("Invalid boolean value [" + text + "]");
}
}
}
此时测试用例通过我们便完成了字符串转换为整型和布尔类型。但是事先我们并不知道每个属性是什么类型的,所以我们还需要一个能自动帮助我们把字符串转换成属性类型的功能。我们先写一个测试用例来描述我们的目的:
public class TypeConverterTest {
@Test
public void testConvertStringToInt(){
TypeConverter converter = new SimpleTypeConverter();
Integer i = converter.convertIfNecessary("3",Integer.class);
Assert.assertEquals(3,i.intValue());
try {
converter.convertIfNecessary("3.1",Integer.class);
}catch (TypeMismatchException e){
return;
}
fail();
}
@Test
public void testConverStringToBoolean(){
TypeConverter converter = new SimpleTypeConverter();
Boolean b = converter.convertIfNecessary("true",Boolean.class);
try {
converter.convertIfNecessary("xxxxxx",Boolean.class);
}catch (TypeMismatchException e){
return;
}
fail();
}
}
通过TypeConverter
自动自动帮助我们转换成对应的类型。TypeConverter
是一个简单的接口:
public interface TypeConverter {
<T> T convertIfNecessary(Object value,Class<T> requiredType)throws TypeMismatchException;
}
我们来写一个它的具体实现:
public class SimpleTypeConverter implements TypeConverter {
private Map<Class<?> , PropertyEditor> defaultEditors;
public SimpleTypeConverter(){
}
/**
* 把value转化成具体的实体数据
* @param value
* @param requiredType
* @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 editor = findDefaultEditor(requiredType);
try {
editor.setAsText((String)value);
}catch (IllegalArgumentException e){
throw new TypeMismatchException(value,requiredType);
}
return (T)editor.getValue();
}else {
throw new RuntimeException("Todo : can't convert value for " + value +
" class :" +requiredType);
}
}
}
/**
* 查询要使用哪个PropertyEditor
* @param requiredType
* @return
*/
private PropertyEditor findDefaultEditor(Class<?> requiredType){
PropertyEditor editor = this.getDefaultEditor(requiredType);
if (editor == null){
throw new RuntimeException("Editor for "+ requiredType + " has not been implemented" );
}
return editor;
}
/**
* 根据class返回PropertyEditor
* @param requiredType
* @return
*/
public PropertyEditor getDefaultEditor(Class<?> requiredType) {
if (this.defaultEditors == null) {
createDefaultEditors();
}
return this.defaultEditors.get(requiredType);
}
private void createDefaultEditors() {
this.defaultEditors = new HashMap<>(64);
// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
// The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
/*this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));*/
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
/*this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));*/
}
}
此时我们已经完成了字符串到类型的自动转换,我们还需要做的就是把这一部分内容与已有的代码进行整合。思考一下,应该在哪一步进行整合呢?我们想一下在set注入(上)中增加了给bean设置属性的方法。我们现在来更新一下这个方法:
protected void populateBean(BeanDefinition bd,Object bean){
//获取beanDefintion的value
List<PropertyValue> pvs = bd.getPropertyValues();
if (pvs == null || pvs.isEmpty()){
return;
}
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this);
SimpleTypeConverter converter = new SimpleTypeConverter();
try {
for (PropertyValue pv : pvs){
String propertyName = pv.getName();
Object originalValue = pv.getValue();
//获取具体的实体或值
Object resolvedValue = valueResolver.resolveValueIfNecessary(originalValue);
//利用java自带的bean方法,对bean实体属性进行赋值。
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds){
if (pd.getName().equals(propertyName)){
Object convertedValue = converter.convertIfNecessary(resolvedValue,pd.getPropertyType());
pd.getWriteMethod().invoke(bean,convertedValue);
break;
}
}
}
}catch (Exception e){
throw new BeanCreationException("Failed to obtain BeanInfo for class ["+bd.getBeanClassName()+"]");
}
}
此时我们的便完成了set注入。具体内容可查看litespring_05
生活要多点不自量力