java 内省以及介绍beanutils使用

来源:互联网 发布:淘宝直通车标志图片 编辑:程序博客网 时间:2024/06/10 15:58

先上来不直接讲内省了,我们现在有个需求就是从配置文件读取数据然后封装成一个bean对象,比如我现在有bean对象叫Person:
package com.introspection;
public class Person {
int id;
String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "编号:"+id+"姓名叫:"+name;
}
}
在我们项目的根目录下有个文件config.txt,里面就写了
id=120
name=张三
这样的 然后读取他封装成一个Person对象,
分析:
首先要读取这个配置文件到内存中,然后通过流去读取每一行的数据,读取到每行数据后要分割因为有个=号,读取到了数据就封装到对象中,那么在封装数据到对象中,我们又不知道它到底有多少个属性值,而且又不知道对象,我 们无法调用对象的set方法设置值,所以就要用到反射技术了,但是如果我们Person类改成Animor类呢?是不是要强转成Animor,所以我们要用到泛型,传递我们什么类型最后返回的对象就是什么类型,代码就是这样的
 //根据配置文件的内容生产对象的对象并且要把对象的属性值封装到对象中。
public static <T> T getInstance(Class<T> clazz) throws Exception{
BufferedReader bufferedReader = new BufferedReader(new FileReader("config.txt"));
//通过class对象获取到无参的构造方法
Constructor<T> constructor = clazz.getConstructor(null);
//创建对象
T o  = constructor.newInstance(null);
//读取属性值
String line = null;
while((line = bufferedReader.readLine())!=null){
String[] datas = line.split("=");
//通过属性名获取到对应的Field对象。
Field field = clazz.getDeclaredField(datas[0]);
if(field.getType()==int.class){
field.set(o, Integer.parseInt(datas[1]));
}else if(field.getType()==String.class){
field.set(o, datas[1]);
}
}
return  o;
}
调用:
Person p = getInstance(Person.class);
System.out.println(p);




Person类我们id和name属性值修饰符都是默认的,这样的话就打破了我们的封装性,会造成在外面随意可以改变他的值,有危险,比如我们现在把id和name都用private修饰,会发现报错:


Class com.introspection.Demo1 can not access a member of class com.introspection.Person with modifiers "private"




就是说访问我们类的属性使用了private修饰,我们只要暴力反射就行了,添加这行代码搞定field.setAccessible(true);
上面虽然实现了我们的需求但是如果属性值多的话,写起来很累,也很容易出错,现在使用内省帮忙我们解决这个问题,那么内省主要是干什么的呢?
开发框架时,经常需要使用对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以公司开发了一套</span><span style="font-family:Tim专门用于操作对象的属性。内省是用于操作对象的属性的 {
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException {
testProperty();
}
public static void testProperty() throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
Person p = new Person();
/**
* 构造第一个参数是类的属性名
* 第二个参数是那个类
*/
PropertyDescriptor  propertyDescriptor = new PropertyDescriptor("id", Person.class);//创建一个属性描述器
//获取属性的set或者get方法
Method writeMethod = propertyDescriptor.getWriteMethod();
Method readMethod = propertyDescriptor.getReadMethod();
propertyDescriptor.getReadMethod();
//设置属性值
writeMethod.invoke(p, 12);//也就是为id这个属性设置12这个值
System.out.println(readMethod.invoke(p, null));
}
}
发现报错了:Exception in thread "main" java.beans.IntrospectionException: Method not found: setId
因为我没有这个方法,把setId()方法改了发现就报错了,于是可以得出他给属性设置值是依赖set方法的
但是这个也有很大的缺点,也是属性一多,发现要创建很多属性描述器,那么它肯定会有一次性获取所有属性的方法
public static void testIntrospector() throws Exception{
BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
PropertyDescriptor[]  descriptor = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor itemProperty : descriptor){
System.out.println(itemProperty.getReadMethod().getName());
}
}
这个获取所有的属性也不方便,由于公司的内省过于繁琐,所以:组织结合很多实际开发中的应用场景开发了一套简单、易用的操作的属性——BeanUtils,其实它底层还是使用了反射,只是封装的很好,然后直接上层给我们提供了方法直接使用就行,在使用之前要导入这个jar,commons-beanutils-1.8.0.jar</span>
使用非常简单,
               Person p = new Person();
int id = 24;
String name="陈其";
BeanUtils.setProperty(p, "id", id);
BeanUtils.setProperty(p, "name",name);
System.out.println(p);
这就是给Person这个bean设置属性值,但是运行起来会报错:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
(ConvertUtilsBean.java:157)
org.apache.commons.beanutils.BeanUtilsBean.<init>(BeanUtilsBean.java:117)
at org.apache.commons.beanutils.BeanUtilsBean$1.initialValue(BeanUtilsBean.java:68)
at org.apache.commons.beanutils.ContextClassLoaderLocal.get(ContextClassLoaderLocal.java:153)
at org.apache.commons.beanutils.BeanUtilsBean.getInstance(BeanUtilsBean.java:80)
at org.apache.commons.beanutils.BeanUtils.setProperty(BeanUtils.java:456)
at com.introspection.Demo4.main(Demo4.java:12)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
7 more








这是因为commons-beanutils.jar要依赖另外一个commons-logging.jar,这样就解决了上面的报错问题,但是Person类中我们的属性都是基本类型的,如果我现在放一个引用类型的属性会怎么样,比如放一个Date类型:

Person p = new Person();
int id = 24;
String name="周桂枝";
String birthday = "2016-03-19";
BeanUtils.setProperty(p, "birthday",birthday);
BeanUtils.setProperty(p, "id", id);
BeanUtils.setProperty(p, "name",name);
System.out.println(p);






运行起来会报错:


三月 19, 2016 1:31:32 下午 org.apache.commons.beanutils.converters.DateTimeConverter toDate
警告:     DateConverter does not support default String to 'Date' conversion.
三月 19, 2016 1:31:32 下午 org.apache.commons.beanutils.converters.DateTimeConverter toDate
警告:     (N.B. Re-configure Converter or use alternative implementation)
Exception in thread "main" org.apache.commons.beanutils.ConversionException: DateConverter does not support default String to 'Date' conversion.
at org.apache.commons.beanutils.converters.DateTimeConverter.toDate(DateTimeConverter.java:468)
at org.apache.commons.beanutils.converters.DateTimeConverter.convertToType(DateTimeConverter.java:343)
at org.apache.commons.beanutils.converters.AbstractConverter.convert(AbstractConverter.java:156)
at org.apache.commons.beanutils.converters.ConverterFacade.convert(ConverterFacade.java:60)
at org.apache.commons.beanutils.ConvertUtilsBean.convert(ConvertUtilsBean.java:470)
at org.apache.commons.beanutils.BeanUtilsBean.setProperty(BeanUtilsBean.java:1004)
at org.apache.commons.beanutils.BeanUtils.setProperty(BeanUtils.java:456)
at com.introspection.Demo4.main(Demo4.java:13)






这是因为common-beanutils这个框架没有做处理,还要我们手动做下处理:

Person p = new Person();
int id = 24;
String name="周桂枝";
String birthday = "2016-03-19";


//注册一个类型转换器  解决common-beanutils 给引用类型赋值
ConvertUtils.register(new Converter() {
@Override
public Object convert(Class type, Object value) { // type : 目前所遇到的数据类型。  value :目前参数的值。
Date date = null;
try{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
date = dateFormat.parse((String)value);
}catch(Exception e){
e.printStackTrace();
}
return date;
}

}, Date.class);

BeanUtils.setProperty(p, "birthday",birthday);
BeanUtils.setProperty(p, "id", id);
BeanUtils.setProperty(p, "name",name);
System.out.println(p);
总结beanUtils:
 BeanUtils主要解决 的问题: 把对象的属性数据封装 到对象中。
 
 BeanUtils的好处:
1. BeanUtils设置属性值的时候,如果属性是基本数据 类型,BeanUtils会自动帮我转换数据类型。
2. BeanUtils设置属性值的时候底层也是依赖于get或者Set方法设置以及获取属性值的。
3. BeanUtils设置属性值,如果设置的属性是其他的引用 类型数据,那么这时候必须要注册一个类型转换器。




ok,关于内省就写到这里了,如果想要了解更多的关于内省方面的知识,一时看源码,二就要去baidu或者google更多关于内省方面的资料了!


0 0
原创粉丝点击