内省(了解)
内省的目标是得到JavaBean属性的读、写方法的反射对象,通过反射对JavaBean属性进行操作的一组API。例如User类有名为username的JavaBean属性,通过两个Method对象(一个是getUsenrmae(),一个是setUsername())来操作User对象。
如果你还不能理解内省是什么,那么我们通过一个问题来了解内省的作用。现在我们有一个Map,内容如下:
Map<String,String> map = new HashMap<String,String>(); map.put("username", "admin"); map.put("password", "admin123"); |
public class User { private String username; private String password;
public User(String username, String password) { this.username = username; this.password = password; } public User() { } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String toString() { return "User [username=" + username + ", password=" + password + "]"; } } |
现在需要把map的数据封装到一个User对象中!User类有两个JavaBean属性,一个叫username,另一个叫password。
你可能想到的是反射,通过map的key来查找User类的Field!这么做是没有问题的,但我们要知道类的成员变量是私有的,虽然也可以通过反射去访问类的私有的成员变量,但我们也要清楚反射访问私有的东西是有“危险”的,所以还是建议通过getUsername和setUsername来访问JavaBean属性。
2.1 内省之获取BeanInfo
我们这里不想去对JavaBean规范做过多的介绍,所以也就不在多介绍BeanInfo的“出身”了。你只需要知道如何得到它,以及BeanInfo有什么。
通过java.beans.Introspector的getBeanInfo()方法来获取java.beans.BeanInfo实例。
BeanInfo beanInfo = Introspector.getBeanInfo(User.class); |
2.2 得到所有属性描述符(PropertyDescriptor)
通过BeanInfo可以得到这个类的所有JavaBean属性的PropertyDescriptor对象。然后就可以通过PropertyDescriptor对象得到这个属性的getter/setter方法的Method对象了。
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); |
每个PropertyDescriptor对象对应一个JavaBean属性:
- String getName():获取JavaBean属性名称;
- Method getReadMethod:获取属性的读方法;
- Method getWriteMethod:获取属性的写方法。
2.3 完成Map数据封装到User对象中
public void fun1() throws Exception { Map<String,String> map = new HashMap<String,String>(); map.put("username", "admin"); map.put("password", "admin123");
BeanInfo beanInfo = Introspector.getBeanInfo(User.class);[崔1]
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();[崔2]
User user = new User();[崔3] for(PropertyDescriptor pd : pds)[崔4] { String name = pd.getName();[崔5] String value = map.get(name);[崔6] if(value != null)[崔7] { Method writeMethod = pd.getWriteMethod();[崔8] writeMethod.invoke(user, value);[崔9] } }
System.out.println(user); } |
3 commons-beanutils
提到内省,不能不提commons-beanutils这个工具。它底层使用了内省,对内省进行了大量的简化!
使用beanutils需要的jar包:
- commons-beanutils.jar;
- commons-logging.jar;
3.1 设置JavaBean属性
User user = new User();
BeanUtils.setProperty(user, "username", "admin");[崔10] BeanUtils.setProperty(user, "password", "admin123");[崔11]
System.out.println(user); |
3.2 获取JavaBean属性
User user = new User("admin", "admin123");
String username = BeanUtils.getProperty(user, "username");[崔12] String password = BeanUtils.getProperty(user, "password");[崔13]
System.out.println("username=" + username + ", password=" + password); |
3.3 封装Map数据到JavaBean对象中
Map<String,String> map = new HashMap<String,String>(); map.put("username", "admin"); map.put("password", "admin123");
User user = new User();
BeanUtils.populate(user, map);[崔14]
System.out.println(user); |
工具类:将Map直接封装到Bean
public static <T> T toBean(Map map, Class<T> clazz) {
try {
/*
* 1. 创建指定类型的javabean对象
*/
T bean = clazz.newInstance();
/*
* 2. 把数据封装到javabean中
*/
BeanUtils.populate(bean, map);
/*
* 返回javabean对象
*/
return bean;
} catch(Exception e) {
throw new RuntimeException(e);
}
}
测试:
public class Demo1 {
@Test
public void fun1() throws Exception {
String className = "cn.itcast.domain.Person";
Class clazz = Class.forName(className);
Object bean = clazz.newInstance();
BeanUtils.setProperty(bean, "name", "张三");
BeanUtils.setProperty(bean, "age", "23");
BeanUtils.setProperty(bean, "gender", "男");
BeanUtils.setProperty(bean, "xxx", "XXX");
String age = BeanUtils.getProperty(bean, "age");
System.out.println(age);
System.out.println(bean);
}
/*
* 把map中的属性直接封装到一个bean中
*
* Map: {"username":"zhangSan", "password","123"}
* 我们要把map的数据封装到一个javabean中!要求map的key与bean的属性名相同!
*/
@Test
public void fun2() throws Exception {
Map<String,String> map = new HashMap<String,String>();
map.put("username", "zhangSan");
map.put("password", "123");
User user = new User();
BeanUtils.populate(user, map);
System.out.println(user);
}
@Test
public void fun3() {
Map<String,String> map = new HashMap<String,String>();
map.put("username", "zhangSan");
map.put("password", "123");
/*
* request.getParameterMap();
*/
User user = CommonUtils.toBean(map, User.class);
System.out.println(user);
}
commons-beanutils
是Apache开源组织提供的用于操作JAVA BEAN的工具包。使用commons-beanutils
,我们可以很方便的对bean对象的属性进行操作。今天为大家介绍一下该包的常用方法。
在介绍常用类之前,我们先来 编写一个用于测试的BEAN类:
package com.gujin.entity;
public class UserInfo
{
private String userId;
private String userName;
public UserInfo()
{
}
public UserInfo(String userId, String userName)
{
this.userId = userId;
this.userName = userName;
}
public String getUserId()
{
return userId;
}
public void setUserId(String userId)
{
this.userId = userId;
}
public String getUserName()
{
return userName;
}
public void setUserName(String userName)
{
this.userName = userName;
}
@Override
public String toString()
{
return String.format("{userId:%s,userName:%s}", userId, userName);
}
}
MethodUtils
MethodUtils
通过反射访问对象的方法并且执行方法。
方法摘要:
返回值 | 方法名 | 说明 |
---|---|---|
static int | clearCache() | 清空方法缓存 |
static Method | getAccessibleMethod(Class<?> clazz, Method method) | 返回一个可访问的方法 |
static Method | getAccessibleMethod(Class<?> clazz, String methodName, Class<?> parameterType) | 返回一个可访问的方法 |
static Method | getAccessibleMethod(Class<?> clazz, String methodName, Class<?>[] parameterTypes) | 返回一个可访问的方法 |
static Method | getAccessibleMethod(Method method) | 返回一个可访问的方法 |
static Method | getMatchingAccessibleMethod(Class<?> clazz, String methodName, Class<?>[] parameterTypes) | 查找与方法名及参数匹配的可访问方法 |
static Class<?> | getPrimitiveType(Class<?> wrapperType) | 获得包装类的基本数据类型 |
static Class<?> | getPrimitiveWrapper(Class<?> primitiveType) | 获得基本数据类型的包装类型 |
static Object | invokeExactMethod(Object object, String methodName, Object arg) | 执行方法 |
static Object | invokeExactMethod(Object object, String methodName, Object[] args) | 执行方法 |
static Object | invokeExactMethod(Object object, String methodName, Object[] args, Class<?>[] parameterTypes) | 执行方法 |
static Object | invokeExactStaticMethod(Class<?> objectClass, String methodName, Object arg) | 执行静态方法 |
static Object | invokeExactStaticMethod(Class<?> objectClass, String methodName, Object[] args) | 执行静态方法 |
static Object | invokeExactStaticMethod(Class<?> objectClass, String methodName, Object[] args, Class<?>[] parameterTypes) | 执行静态方法 |
static Object | invokeMethod(Object object, String methodName, Object arg) | 执行方法 |
static Object | invokeMethod(Object object, String methodName, Object[] args) | 执行方法 |
static Object | invokeMethod(Object object, String methodName, Object[] args, Class<?>[] parameterTypes) | 执行方法 |
static Object | invokeStaticMethod(Class<?> objectClass, String methodName, Object arg) | 执行静态方法 |
static Object | invokeStaticMethod(Class<?> objectClass, String methodName, Object[] args) | 执行静态方法 |
static Object | invokeStaticMethod(Class<?> objectClass, String methodName, Object[] args, Class<?>[] parameterTypes) | 执行静态方法 |
static boolean | isAssignmentCompatible(Class<?> parameterType, Class<?> parameterization) | 确定是否可以使用一个类型作为方法调用参数 |
static void | setCacheMethods(boolean cacheMethods) | 设置缓存方法 |
static Class<?> | toNonPrimitiveClass(Class<?> clazz) | 如果是简单数据类型则返回对应的包装类,否则返回本身 |
使用示例:
package com.gujin.beanutils;
import java.lang.reflect.Method;
import org.apache.commons.beanutils.MethodUtils;
import org.junit.Test;
import com.gujin.entity.UserInfo;
public class MethodUtilsTest
{
@Test
public void test() throws Exception
{
UserInfo userInfo = new UserInfo();
// 通过方法名和参数类型获得可访问方法
Method method = MethodUtils.getAccessibleMethod(UserInfo.class,
"setUserId", String.class);
method.invoke(userInfo, "jianggujin");
// 可以直接通过invokeMethod执行方法
MethodUtils.invokeMethod(userInfo, "setUserName", "蒋固金");
System.out.println(userInfo);
}
}
运行结果:
{userId:jianggujin,userName:蒋固金}
ConstructorUtils
ConstructorUtils
通过反射提供了构造方法相关的便捷操作方法。
方法摘要:
返回值 | 方法名 | 说明 |
---|---|---|
static <T> Constructor<T> | getAccessibleConstructor(Class<T> klass, Class<?> parameterType) | 获得含有一个形参的构造方法 |
static <T> Constructor<T> | getAccessibleConstructor(Class<T> klass, Class<?>[] parameterTypes) | 获得含有指定类型形参的构造方法 |
static <T> Constructor<T> | getAccessibleConstructor(Constructor<T> ctor) | 获得可访问构造方法 |
static <T> T | invokeConstructor(Class<T> klass, Object arg) | 执行构造方法 |
static <T> T | invokeConstructor(Class<T> klass, Object[] args) | 执行构造方法 |
static <T> T | invokeConstructor(Class<T> klass, Object[] args, Class<?>[] parameterTypes) | 执行构造方法 |
static <T> T | invokeExactConstructor(Class<T> klass, Object arg) | 执行构造方法 |
static <T> T | invokeExactConstructor(Class<T> klass, Object[] args) | 执行构造方法 |
static <T> T | invokeExactConstructor(Class<T> klass, Object[] args, Class<?>[] parameterTypes) | 执行构造方法 |
使用示例:
package com.gujin.beanutils;
import java.lang.reflect.Constructor;
import org.apache.commons.beanutils.ConstructorUtils;
import org.junit.Test;
import com.gujin.entity.UserInfo;
public class ConstructorUtilsTest
{
@Test
public void test() throws Exception
{
Constructor<UserInfo> constructor = ConstructorUtils
.getAccessibleConstructor(UserInfo.class, new Class[] {
String.class, String.class });
System.out.println(constructor.newInstance("jianggujin", "蒋固金"));
// 更简洁的写法
UserInfo userInfo = ConstructorUtils.invokeConstructor(UserInfo.class,
new String[] { "jianggujin", "蒋固金" });
System.out.println(userInfo);
}
}
运行结果:
{userId:jianggujin,userName:蒋固金}
{userId:jianggujin,userName:蒋固金}
PropertyUtils
PropertyUtils
通过反射提供了对象属性的便捷操作方法。
方法摘要:
返回值 | 方法名 | 说明 |
---|---|---|
static void | addBeanIntrospector(BeanIntrospector introspector) | 添加一个BeanIntrospector |
static void | clearDescriptors() | 清空所有属性描述信息 |
static void | copyProperties(Object dest, Object orig) | 复制属性 |
static Map<String,Object> | describe(Object bean) | 属性描述 |
static Object | getIndexedProperty(Object bean, String name) | 指定索引属性值 |
static Object | getIndexedProperty(Object bean, String name, int index) | 指定索引属性值 |
static Object | getMappedProperty(Object bean, String name) | 获得Map属性 |
static Object | getMappedProperty(Object bean, String name, String key) | 获得Map属性中指定键对应的值 |
static Object | getNestedProperty(Object bean, String name) | 获得嵌套属性 |
static Object | getProperty(Object bean, String name) | 获得属性 |
static PropertyDescriptor | getPropertyDescriptor(Object bean, String name) | 获得属性描述 |
static PropertyDescriptor[] | getPropertyDescriptors(Class<?> beanClass) | 获得属性描述 |
static PropertyDescriptor[] | getPropertyDescriptors(Object bean) | 获得属性描述 |
static Class<?> | getPropertyType(Object bean, String name) | 获得属性类型 |
static Method | getReadMethod(PropertyDescriptor descriptor) | 返回一个可访问的属性的getter方法 |
static Object | getSimpleProperty(Object bean, String name) | 返回属性值 |
static Method | getWriteMethod(PropertyDescriptor descriptor) | 返回一个可访问的属性的setter方法 |
static boolean | isReadable(Object bean, String name) | 判断是否为可读属性 |
static boolean | isWriteable(Object bean, String name) | 判断是否为可写属性 |
static boolean | removeBeanIntrospector(BeanIntrospector introspector) | 移除BeanIntrospector |
static void | resetBeanIntrospectors() | 重置BeanIntrospector |
static void | setIndexedProperty(Object bean, String name, int index, Object value) | 设置指定索引属性值 |
static void | setIndexedProperty(Object bean, String name, Object value) | 设置指定索引属性值 |
static void | setMappedProperty(Object bean, String name, Object value) | 设置Map属性的值 |
static void | setMappedProperty(Object bean, String name, String key, Object value) | 设置Map属性指定键的值 |
static void | setNestedProperty(Object bean, String name, Object value) | 设置嵌套属性的值 |
static void | setProperty(Object bean, String name, Object value) | 设置属性值 |
static void | setSimpleProperty(Object bean, String name, Object value) | 设置属性值 |
使用示例:
package com.gujin.beanutils;
import org.apache.commons.beanutils.PropertyUtils;
import org.junit.Test;
import com.gujin.entity.UserInfo;
public class PropertyUtilsTest
{
@Test
public void test() throws Exception
{
UserInfo userInfo = new UserInfo("jianggujin", "蒋固金");
UserInfo copyed = new UserInfo();
PropertyUtils.copyProperties(copyed, userInfo);
System.out.println(copyed);
System.out.println(PropertyUtils.describe(userInfo));
PropertyUtils.setProperty(userInfo, "userId", "gjjiang");
System.out.println(userInfo);
}
}
运行结果:
{userId:jianggujin,userName:蒋固金}
{userId=jianggujin, class=class com.gujin.entity.UserInfo, userName=蒋固金}
{userId:gjjiang,userName:蒋固金}
BeanUtils
BeanUtils
通过反射提供了Bean对象的便捷操作方法。
方法摘要:
返回值 | 方法名 | 说明 |
---|---|---|
static Object | cloneBean(Object bean) | 克隆对象 |
static void | copyProperties(Object dest, Object orig) | 复制属性 |
static void | copyProperty(Object bean, String name, Object value) | 复制属性 |
static <K,V> Map<K,V> | createCache() | 创建缓存 |
static Map<String,String> | describe(Object bean) | 描述 |
static String[] | getArrayProperty(Object bean, String name) | 返回指定属性的值,作为字符串数组返回 |
static String | getIndexedProperty(Object bean, String name) | 获取指定索引位置对象作为字符串返回 |
static String | getIndexedProperty(Object bean, String name, int index) | 获取指定索引位置对象作为字符串返回 |
static String | getMappedProperty(Object bean, String name) | 获得Map属性值作为字符串返回 |
static String | getMappedProperty(Object bean, String name, String key) | 获得Map属性中指定键的值作为字符串返回 |
static String | getNestedProperty(Object bean, String name) | 获得嵌套属性作为字符串返回 |
static String | getProperty(Object bean, String name) | 获得属性值作为字符串返回 |
static String | getSimpleProperty(Object bean, String name) | 获得属性值作为字符串返回 |
static void | populate(Object bean, Map<String,? extends Object> properties) | 将Map中的数据注入到Bean对象中 |
static void | setProperty(Object bean, String name, Object value) | 设置属性值 |
使用示例:
package com.gujin.beanutils;
import java.util.HashMap;
import org.apache.commons.beanutils.BeanUtils;
import org.junit.Test;
import com.gujin.entity.UserInfo;
public class BeanUtilsTest
{
@Test
public void test() throws Exception
{
UserInfo userInfo = new UserInfo();
HashMap<String, String> properties = new HashMap<String, String>();
properties.put("userId", "jianggujin");
properties.put("userName", "蒋固金");
BeanUtils.populate(userInfo, properties);
System.out.println(userInfo);
}
}
运行结果:
{userId:jianggujin,userName:蒋固金}
ConvertUtils
ConvertUtils
提供了数据类型相互转换的方法。
方法摘要:
方法摘要:
返回值 | 方法名 | 说明 |
---|---|---|
static String | convert(Object value) | 将对象转换为字符串 |
static Object | convert(Object value, Class<?> targetType) | 将对象转换为指定数据类型对象 |
static Object | convert(String[] values, Class<?> clazz) | 将数组转换为指定数据类型对象 |
static Object | convert(String value, Class<?> clazz) | 将字符串转换为指定数据类型对象 |
static void | deregister() | 移除所有已经注册的转换器 |
static void | deregister(Class<?> clazz) | 移除指定类型的转换器 |
static Converter | lookup(Class<?> clazz) | 查找指定类型的转换器 |
static Converter | lookup(Class<?> sourceType, Class<?> targetType) | 查找将指定类型转换为另一种类型的转换器 |
static void | register(Converter converter, Class<?> clazz) | 注册转换器 |
使用示例:
package com.gujin.beanutils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.junit.Test;
public class ConvertUtilsTest
{
@Test
public void test() throws Exception
{
ConvertUtils.register(new Converter()
{
@Override
public <T> T convert(Class<T> arg0, Object arg1)
{
try
{
return (T) new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.parse((String) arg1);
}
catch (ParseException e)
{
return null;
}
}
}, Date.class);
System.out.println(ConvertUtils
.convert("2016-04-09 12:41:00", Date.class));
}
}
运行结果:
Sat Apr 09 12:41:00 CST 2016