mybatis的反射工具类—MetaObject(反射对象类)
mybatis提供了一个底层的反射工具类。我们在业务代码中也可以使用。
1. 相关API的使用
MeatObject是Mybatis的工具类,通过MetaObject获取和设置对象的属性值。
public class TestMetaObject {
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
public static void main(String[] args) {
//第一次读取
Teca teca = new Teca();
List<Teca.Stu> objects = new ArrayList<>();
objects.add(new Teca.Stu());
teca.setName("lili");
teca.setStus(objects);
MetaObject metaObject = SystemMetaObject.forObject(teca);
System.out.println("getGetterNames:" + JSON.toJSONString(metaObject.getGetterNames()));
System.out.println("getSetterNames:" + JSON.toJSONString(metaObject.getSetterNames()));
System.out.println("name的get方法返回值:" + JSON.toJSONString(metaObject.getGetterType("name")));
System.out.println("stus的set方法参数值:" + JSON.toJSONString(metaObject.getGetterType("stus")));
System.out.println("name的hasGetter:" + metaObject.hasGetter("name"));
//出现:UnsupportedOperationException异常
// System.out.println("stus.id(属性为集合)的hasGetter:" + metaObject.hasGetter("stus.id"));
System.out.println("stu.id(属性为对象)的hasGetter:" + metaObject.hasGetter("stu.id"));
System.out.println("获取name的属性值:" + metaObject.getValue("name"));
//重新设置属性值
metaObject.setValue("name","huahua");
System.out.println("设置name的属性值:" + metaObject.getValue("name"));
//设置属性(集合)的元素值
metaObject.setValue("stus[0].id","001");
System.out.println("获取stus集合的第一个元素的属性值:"+JSON.toJSONString(metaObject.getValue("stus[0].id")));
System.out.println("对象的序列化:"+JSON.toJSONString(teca));
}
}
返回结果:
getGetterNames:["stu","price","name","stus"]
getSetterNames:["stu","price","name","stus"]
name的get方法返回值:"java.lang.String"
stus的set方法参数值:"java.util.List"
name的hasGetter:true
stu.id(属性为对象)的hasGetter:true
获取name的属性值:lili
设置name的属性值:huahua
获取stus的第一个元素的属性值:"001"
对象的序列化:{"name":"huahua","price":0.0,"stus":[{"id":"001"}]}
基本的pojo对象:
@Data
@ToString
public class Teca {
private String name;
private double price;
private List<Stu> stus;
private Stu stu;
@Data
public static class Stu {
private String id;
}
}
2. 参数说明
参数 | 说明 | 默认使用值 |
---|---|---|
object | 待处理的原始对象 | 业务对象 |
objectFactory | 创建对象的工厂 | new DefaultObjectFactory() |
objectWrapperFactory | object是否是wrapper | new DefaultObjectWrapperFactory() |
reflectorFactory | 反射的工厂类 | new DefaultReflectorFactory() |
2.1 注意事项:
MetaObject提供了一个工具类:
public final class SystemMetaObject {
public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
private SystemMetaObject() {
// Prevent Instantiation of Static Class
}
private static class NullObject {
}
public static MetaObject forObject(Object object) {
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
}
}
当调用SystemMetaObject
获取MetaObject
对象时,每次均new DefaultReflectorFactory()
了一个反射工厂类。
继续查看:
public class DefaultReflectorFactory implements ReflectorFactory {
private boolean classCacheEnabled = true;
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();
public DefaultReflectorFactory() {
}
@Override
public boolean isClassCacheEnabled() {
return classCacheEnabled;
}
@Override
public void setClassCacheEnabled(boolean classCacheEnabled) {
this.classCacheEnabled = classCacheEnabled;
}
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
Reflector cached = reflectorMap.get(type);
if (cached == null) {
cached = new Reflector(type);
reflectorMap.put(type, cached);
}
return cached;
} else {
return new Reflector(type);
}
}
}
这个类并没有做很复杂的操作,就是获取object的type类型,且存储到Map中。当同一个object调用forObject()
时,后续调用可以在缓存中获取反射对象。
但注意:SystemMetaObject
方法因为每次均new了一个新的DefaultReflectorFactory
工厂。若每次在方法中调用SystemMetaObject.forObject
获取MetaObject
对象。同一个对象可能不会走缓存。
推荐做法:将DefaultReflectorFactory
声明为静态变量:
public class TestMetaObject {
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
public static void main(String[] args) {
//第一次读取
Teca teca = new Teca();
List<Teca.Stu> objects = new ArrayList<>();
objects.add(new Teca.Stu());
teca.setName("lili");
teca.setStus(objects);
MetaObject metaObject = MetaObject.forObject(teca, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
}
}
3. 源码分析
在创建MetaObject
对象的时候,就会将传入的Object对象的所有反射对象都缓存起来。后续调用metaObject.setValue
等方法时,直接在缓存中获取到反射对象,然后执行反射操作。
3.1 创建MetaObject对象
创建MetaObject对象时,选择不同的Wrapper进行包装。
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
//判断传入的对象是否是ObjectWrapper子类
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) { //默认false
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) { //判断是否是Map
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) { //判断是否是集合
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else { //普通的pojo处理
this.objectWrapper = new BeanWrapper(this, object);
}
}
以普通的pojo为例,创建BeanWrapper
对象,在forClass
方法中会遍历object的所有方法。获取反射对象。
public BeanWrapper(MetaObject metaObject, Object object) {
super(metaObject);
this.object = object;
//getReflectorFactory为传入的参数。
this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
}
到此处:
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
到达此处,判断缓存中是否存在某对象的解析结果:
注意:MetaObject.forObject
会将object结果缓存起来,后续在使用MetaObject.forObject创建对象时,直接在缓存中获取。
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
Reflector cached = reflectorMap.get(type);
if (cached == null) {
cached = new Reflector(type);
reflectorMap.put(type, cached);
}
return cached;
} else {
return new Reflector(type);
}
}
若没有存在,则调用new Reflector(type);
去创建cache对象。
public Reflector(Class<?> clazz) {
type = clazz;
//解析构造函数
addDefaultConstructor(clazz);
//解析get方法
addGetMethods(clazz);
//解析set方法
addSetMethods(clazz);
//解析Field属性
addFields(clazz);
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
3.2 解析object所有的set方法
总方法:
private void addSetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
//获取到object所有的方法。
Method[] methods = getClassMethods(cls);
//遍历所有Method
for (Method method : methods) {
String name = method.getName();
//判断name是否以set开头
if (name.startsWith("set") && name.length() > 3) {
//set方法的参数只有一个
if (method.getParameterTypes().length == 1) {
//移除set前缀。且均变成小写
name = PropertyNamer.methodToProperty(name);
//将method和name存储到Map集合
addMethodConflict(conflictingSetters, name, method);
}
}
}
//将conflictingSetters放入到属性字段(缓存)
resolveSetterConflicts(conflictingSetters);
}
2. 删除方法的前缀,获取属性名:
public static String methodToProperty(String name) {
if (name.startsWith("is")) {
name = name.substring(2);
} else if (name.startsWith("get") || name.startsWith("set")) { //截取字段
name = name.substring(3);
} else {
throw new ReflectionException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'.");
}
//都转换为小写
if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
return name;
}
3. key为属性名,value是集合:
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
List<Method> list = conflictingMethods.get(name);
if (list == null) {
list = new ArrayList<Method>();
conflictingMethods.put(name, list);
}
list.add(method);
}
4. 将局部变量放入到属性集合中:
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
//遍历缓存的set方法集合
for (String propName : conflictingSetters.keySet()) {
List<Method> setters = conflictingSetters.get(propName);
Method firstMethod = setters.get(0);
//若是
if (setters.size() == 1) {
addSetMethod(propName, firstMethod);
} else {
//期待的type为对应get方法的返回值。
Class<?> expectedType = getTypes.get(propName);
if (expectedType == null) {
throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
+ propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " +
"specification and can cause unpredicatble results.");
} else { //conflictingSetters一个name存在多个value时
Iterator<Method> methods = setters.iterator();
Method setter = null;
while (methods.hasNext()) {
Method method = methods.next();
//寻找:方法只有一个参数,且参数是期待的参数(getXxx方法的返回值)。
if (method.getParameterTypes().length == 1
&& expectedType.equals(method.getParameterTypes()[0])) {
setter = method;
break;
}
}
if (setter == null) {
throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
+ propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " +
"specification and can cause unpredicatble results.");
}
addSetMethod(propName, setter);
}
}
}
}
5. 放入到属性变量中
private void addSetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
setMethods.put(name, new MethodInvoker(method));
Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
setTypes.put(name, typeToClass(paramTypes[0]));
}
}
3.3 反射设置值
public class TestMetaObject {
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
public static void main(String[] args) {
//第一次读取
Teca teca = new Teca();
MetaObject metaObject = MetaObject.forObject(teca, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
//重新设置属性值
metaObject.setValue("name","huahua");
}
}
设置分词器:
public void setValue(String name, Object value) {
//分词器
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
if (value == null && prop.getChildren() != null) {
// don't instantiate child path if value is null
return;
} else {
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
metaValue.setValue(prop.getChildren(), value);
} else {
//object的包装器
objectWrapper.set(prop, value);
}
}
分词器的bug.png
@Override
public void set(PropertyTokenizer prop, Object value) {
if (prop.getIndex() != null) {
Object collection = resolveCollection(prop, object);
setCollectionValue(prop, collection, value);
} else {
//分词器对象、object对象,设置的值
setBeanProperty(prop, object, value);
}
}
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
try {
//在缓存中获取反射对象
Invoker method = metaClass.getSetInvoker(prop.getName());
Object[] params = {value};
try {
//反射设置值。
method.invoke(object, params);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (Throwable t) {
throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
}
}
推荐阅读
https://www.cnblogs.com/javadeveloper/archive/2004/01/13/13151682.html