时间:2020-11-26 22:54:40 | 栏目:JAVA代码 | 点击:次
引言
最近在重读《精通Spring+4.x++企业应用开发实战》这本书,看到了有关JavaBean编辑器的部分,了解到PropertyEditor和BeanInfo的使用。不得不说,BeanInfo是一个很强大的东西,Java中的内省也与之有一点点小关联。
JavaBean、PropertyEditor与BeanInfo
JavaBean简单介绍
JavaBean是一种Java写成的可重用组件,本质上还是一个Java类,但是与一般的Java类不同,JavaBean必须有一个无参的构造函数,其字段必须私有化,并提供set、get方法供外界使用。根据书中所介绍,Sun所制定的JavaBean规范很大程度山是为了IDE准备的--它让IDE能够以可视化的方式设置JavaBean的属性。
PropertyEditor接口
PropertyEditor是属性编辑器的接口,其作用是将一个String类型的值转换为JavaBean的属性。Java为PropertyEditor提供了一个默认的实现类PropertyEditorSupport。
BeanInfo接口
BeanInfo用于描述JavaBean哪些属性可以编辑及对应属性编辑器。Java为BeanInfo也提供了一个默认实现--SimpleBeanInfo。
其他
更多有关JavaBean以及这两个接口的知识,可以购买这本书《精通Spring+4.x++企业应用开发实战》,或者看我的Copy
一个小例子
在《精通Spring+4.x++企业应用开发实战》中使用的例子是根据《Core Java II》的一个例子改变而成,但是有一个小缺点,该例子使用到了Swing,演示时需要将代码打成JAR包,使用IDE组件扩展管理功能注册到IDE中,不太方便,因此我特意尝试用JavaBean编辑器读取properties文件作为一个小例子。
定义一个JavaBean
我们先定义一个Person类,用作JavaBean
/** * 一个简单的JavaBean * @author aming * */ public class Person implements Serializable { private static final long serialVersionUID = 6366021085463785872L; /** * 姓名 */ private String name; /** * 年龄 */ private int age; /** * 性别 */ private Gender gender; /** * 生日 */ private Date birthday; /** * 是否结婚 */ private boolean married; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Gender getGender() { return gender; } public void setGender(Gender gender) { this.gender = gender; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public boolean isMarried() { return married; } public void setMarried(boolean married) { this.married = married; } public Person() { super(); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", gender=" + gender + ", birthday=" + birthday + ", married=" + married + "]"; } }
其中Gender是我自定义的枚举,其代码如下:
/** * 表示性别的枚举类 * @author aming * */ public enum Gender { M(1,"男"),F(2,"女"); private int id; private String genderName; public String getGenderName() { return genderName; } Gender(int id,String genderName){ this.id = id; this.genderName = genderName; } public static Gender getGender(int id) { for(Gender gender:values()) { if(gender.id == id) { return gender; } } return M; } public static Gender getGender(String genderName) { for(Gender gender:values()) { if(gender.genderName == genderName) { return gender; } } return M; } }
实现PropertyEditor
Person类中有5个字段,分别使用String、int、Gender、Date和boolean这5中类型,其中对String类型变量name我们使用PropertyEditorSupport这个默认实现就可以了,其他的类型我们需要自定义去实现PropertyEditor接口--int对应IntegerPropertyEditor、Gender对应GenderPropertyEditor、Date对应DatePropertyEditor以及boolean对应BooleanPropertyEditor。
IntegerPropertyEditor
IntegerPropertyEditor类继承了PropertyEditorSupport类和实现PropertyEditor接口(个人习惯实现接口的时候同时继承一个默认实现,这样我可以只关心我所需要实现的方法,当然,这个默认实现最好是一个抽象类),重写了getAsText()方法和setAsText(String text),其代码如下:
@Override public String getAsText() { return String.valueOf((int)getValue()); } @Override public void setAsText(String text) throws IllegalArgumentException { try { setValue(Integer.parseInt(text)); } catch(NumberFormatException e) { throw new IllegalArgumentException(e); } }
GenderPropertyEditor
GenderPropertyEditor主要是将外部获得到的字符串去转换为Gender,其实现思路是,先尝试将该字符串转换为Integer类型,如果转换成功则将转换得到Integer值通过Gender枚举的静态方法getGender(int id)获取Gender类型的变量;如果转换失败,则将该字符串看作genderName,再去获取Gender。至于Gender到字符串,直接返回genderName就可以了。主要代码如下:
@Override public String getAsText() { Gender value = (Gender)getValue(); return value.getGenderName(); } @Override public void setAsText(String value) throws IllegalArgumentException { try { setValue(Gender.getGender(Integer.valueOf(value))); }catch(NumberFormatException ex) { setValue(Gender.getGender(value)); } }
DatePropertyEditor
DatePropertyEditor的思路和GenderPropertyEditor相似,其核心代码献上:
private DateFormat df; @Override public String getAsText() { Date date = (Date)getValue(); return df.format(date); } @Override public void setAsText(String text) throws IllegalArgumentException { try { setValue(df.parse(text)); } catch (ParseException e) { throw new IllegalArgumentException(e); } } public DatePropertyEditor() { super(); this.df = new SimpleDateFormat("yyyy-MM-dd"); }
BooleanPropertyEditor
BooleanPropertyEditor与IntegerPropertyEditor一样,实现非常类似:
@Override public String getAsText() { return String.valueOf((boolean)getValue()); } @Override public void setAsText(String text) throws IllegalArgumentException { setValue(Boolean.valueOf(text)); }
自定义BeanInfo
定义PersonBeanInfo类,用于描述Person属性和相对应的PropertyEditor。代码如下:
public class PersonBeanInfo extends SimpleBeanInfo implements BeanInfo { @Override public PropertyDescriptor[] getPropertyDescriptors() { List<PropertyDescriptor> list = new ArrayList<>(); try { // 将Person中name与PropertyEditorSupport绑定 PropertyDescriptor namePropertyDescriptor = new PropertyDescriptor("name",Person.class); namePropertyDescriptor.setPropertyEditorClass(PropertyEditorSupport.class); // 将Person中age与IntegerPropertyEditor绑定 PropertyDescriptor agePropertyDescriptor = new PropertyDescriptor("age",Person.class); agePropertyDescriptor.setPropertyEditorClass(IntegerPropertyEditor.class); // 将Person中gender与GenderPropertyEditor绑定 PropertyDescriptor genderPropertyDescriptor = new PropertyDescriptor("gender", Person.class); genderPropertyDescriptor.setPropertyEditorClass(GenderPropertyEditor.class); // 将Person中birthday与DatePropertyEditor绑定 PropertyDescriptor birthdayPropertyDescriptor = new PropertyDescriptor("birthday",Person.class); birthdayPropertyDescriptor.setPropertyEditorClass(DatePropertyEditor.class); // 将Person中married与BooleanPropertyEditor绑定 PropertyDescriptor marriedPropertyDescriptor = new PropertyDescriptor("married",Person.class); marriedPropertyDescriptor.setPropertyEditorClass(BooleanPropertyEditor.class); list.add(namePropertyDescriptor); list.add(agePropertyDescriptor); list.add(genderPropertyDescriptor); list.add(birthdayPropertyDescriptor); list.add(marriedPropertyDescriptor); return list.toArray(new PropertyDescriptor[list.size()]); } catch (IntrospectionException ex) { ex.printStackTrace(); } return null; } }
创建配置文件
因为我创建的是一个普通的Java项目,因此我选择在src目录下创建person.properites文件,文件内容如下:
name=arthurming age=18 gender=1 birthday=2017-10-24 married=true
测试
测试代码
@Test public void test() throws IOException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { Properties p = new Properties(); InputStream is =this.getClass().getClassLoader().getResourceAsStream("person.properties"); p.load(is); BeanInfo beanInfo = new PersonBeanInfo(); PropertyDescriptor[] propertyDescriptoies = beanInfo.getPropertyDescriptors(); Person person = new Person(); for(PropertyDescriptor propertyDescriptor : propertyDescriptoies) { String key = propertyDescriptor.getName(); String value = p.getProperty(key); Method method = propertyDescriptor.getWriteMethod(); PropertyEditor pe = (PropertyEditor) propertyDescriptor.getPropertyEditorClass().newInstance(); if(pe.getClass() == PropertyEditorSupport.class) { //① pe.setValue(value); } else { pe.setAsText(value); } method.invoke(person,pe.getValue()); } System.out.println(person); }
说明:代码①表示当pe类型是PropertyEditorSupport,而不是我所定义PropertyEdtior,应该使用setValue方法而不是setAsText方法。
因为在PropertyEditorSupport中,其setAsText()方法为:
public void setAsText(String text) throws java.lang.IllegalArgumentException { if (value instanceof String) { setValue(text); return; } throw new java.lang.IllegalArgumentException(text); }
如果使用setAsText()方法,那么由于value不是String类型,将会抛出IllegalArgumentException。不过很遗憾我搞明白value到底实际上什么类型......
测试结果
Person [name=arthurming, age=18, gender=M, birthday=Tue Oct 24 00:00:00 CST 2017, married=true]
小结
感觉您能容忍我拙劣的文笔看到现在,也希望你在读《精通Spring+4.x++企业应用开发实战》这本书时,看到有关JavaBean编辑器的知识,我的这个例子可以对你有所帮助。