本文在MyBatis主流程分析之(三)-准备SQL语句和参数替换、执行的基础上解释了mybatis如何利用反射获取和设置参数对象上的值。
本文涉及java基础的反射,对反射不怎么了解的可以参考java反射 ,mybatis最终也是利用java的反射机制来获取和设置对象的值得。
基本的原理
- 方法一:通过属性设置或获取
Class<?> demo = null;
Object obj = null;
demo = Class.forName("Reflect.Person");
obj = demo.newInstance();
Field field = demo.getDeclaredField("sex");
field.setAccessible(true);
field.set(obj, "男");
System.out.println(field.get(obj));
- 方法二:通过方法设置或获取
public static void getter(Object obj, String att) {
try {
Method method = obj.getClass().getMethod("get" + att);
System.out.println(method.invoke(obj));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setter(Object obj, String att, Object value,
Class<?> type) {
try {
Method method = obj.getClass().getMethod("set" + att, type);
method.invoke(obj, value);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
Class<?> demo = null;
Object obj=null;
try {
demo = Class.forName("com.elements.Reflect.Person");
} catch (Exception e) {
e.printStackTrace();
}
try{
obj=demo.newInstance();
}catch (Exception e) {
e.printStackTrace();
}
setter(obj,"Sex","男",String.class);
getter(obj,"Sex");
}
以下的例子和分析中我们着重了pojo(bean)的方式,其他map和集合的方式这里没有分析。
我们首先看看mybatis的reflection包下的所有类
一、Invoker包
这里也是mybatis最终利用java反射模式获取和设置对象值得地方。
Invoker接口定义
public interface Invoker {
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
Class<?> getType();
}
在这个包下面有三个实现类,分别是GetFieldInvoker,SetFieldInvoker和MethodInvoker。GetFieldInvoker和SetFieldInvoker分别调用Field的get和set用来获取和设置对象的属性值。MethodInvoker用来调用Object的的某个方法(通过方法设置对象的属性)。
- GetFieldInvoker类
public class GetFieldInvoker implements Invoker {
private Field field;
public GetFieldInvoker(Field field) {
this.field = field;
}
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
return field.get(target);//还是调用java最基本模式
}
public Class<?> getType() {
return field.getType();
}
}
- SetFieldInvoker
public class SetFieldInvoker implements Invoker {
private Field field;
public SetFieldInvoker(Field field) {
this.field = field;
}
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
field.set(target, args[0]);//还是调用java最基本模式
return null;
}
public Class<?> getType() {
return field.getType();
}
}
- MethodInvoker
public class MethodInvoker implements Invoker {
private Class<?> type;
private Method method;
public MethodInvoker(Method method) {
this.method = method;
if (method.getParameterTypes().length == 1) {
type = method.getParameterTypes()[0];
} else {
type = method.getReturnType();
}
}
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
return method.invoke(target, args);//调用对象的方法
}
public Class<?> getType() {
return type;
}
}
二、Property包
- PropertyNamer
定义几个静态方法,方便下面几个类使用。
//这个方法把访问方法的名字转换成范围属性的方式,先截取了get和set再将首字母变成小写。
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;
}
//判断是否属性
public static boolean isProperty(String name) {
return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");
}
//判断是否是一个get 方法
public static boolean isGetter(String name) {
return name.startsWith("get") || name.startsWith("is");
}
//判断是否是一个set 方法
public static boolean isSetter(String name) {
return name.startsWith("set");
}
- PropertyCopier
一个静态方法,复制属性,包括它的父类
public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
Class<?> parent = type;
while (parent != null) {
final Field[] fields = parent.getDeclaredFields();
for(Field field : fields) {
try {
field.setAccessible(true);
field.set(destinationBean, field.get(sourceBean));
} catch (Exception e) {
// Nothing useful to do, will only fail on final fields, which will be ignored.
}
}
parent = parent.getSuperclass();
}
}
- PropertyTokenizer(属性标记器)
传入:User[1].age
name=User
children=age
index=1
private String name;
private String indexedName;
private String index;
private String children;
public PropertyTokenizer(String fullname) {
// 对参数进行第一次处理,通过“.”分隔符将propertyName分作两部分
int delim = fullname.indexOf('.');
if (delim > -1) {
name = fullname.substring(0, delim);
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
indexedName = name;
// 对name进行二次处理,去除“[...]”,并将方括号内的内容赋给index属性,如果name属性中包含“[]”的话
delim = name.indexOf('[');
if (delim > -1) {
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
三、Wrapper包装
这里主要是实现了图片左边的ObjectWrapper定义的内容
ObjectWrapper的接口定义
public interface ObjectWrapper {
//获取属性的值
Object get(PropertyTokenizer prop);
//设置属性的值
void set(PropertyTokenizer prop, Object value);
//查找某属性
String findProperty(String name, boolean useCamelCaseMapping);
//获取所有get的名字
String[] getGetterNames();
//获取所有set的名字
String[] getSetterNames();
//set的类型
Class<?> getSetterType(String name);
Class<?> getGetterType(String name);
boolean hasSetter(String name);
boolean hasGetter(String name);
MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
//是否一个集合
boolean isCollection();
//集合的增加
public void add(Object element);
//结合的增加
public <E> void addAll(List<E> element);
}
这里我们简单看一下 BeanWrapper的实现
private Object object;
private MetaClass metaClass;
//构造函数
public BeanWrapper(MetaObject metaObject, Object object) {
super(metaObject);
this.object = object;
this.metaClass = MetaClass.forClass(object.getClass());
}
//获取属性值
public Object get(PropertyTokenizer prop) {
//根据prop.getIndex判断是否一个一个集合
if (prop.getIndex() != null) {
Object collection = resolveCollection(prop, object);
//下面调用的是BaseWrapper的方法
return getCollectionValue(prop, collection);
} else {
return getBeanProperty(prop, object);
}
}
//设置属性的值
public void set(PropertyTokenizer prop, Object value) {
//根据prop.getIndex判断是否一个一个集合
if (prop.getIndex() != null) {
Object collection = resolveCollection(prop, object);
//下面调用的是BaseWrapper的方法
setCollectionValue(prop, collection, value);
} else {
setBeanProperty(prop, object, value);
}
}
设置和获取值得方法。
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
return method.invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t);
}
}
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);
}
}
在这里我们可以看到设置和获取值都是通过MetaClass对象返回一个方法,通过这个方法获取或者设置值的。这个对象在构造这个BeanWrapper对象是就已经初始化好了。
this.metaClass = MetaClass.forClass(object.getClass());
接下来我们看看这个MetaClass类
四、MetaClass类
在这个类中定义了一个Reflector 类,MetaClass通过这个类实现了getGetInvoker和getSetInvoker这两个方法。
private Reflector reflector;
private MetaClass(Class<?> type) {
this.reflector = Reflector.forClass(type);
}
public static MetaClass forClass(Class<?> type) {
return new MetaClass(type);
}
public Invoker getGetInvoker(String name) {
return reflector.getGetInvoker(name);
}
public Invoker getSetInvoker(String name) {
return reflector.getSetInvoker(name);
}
五、Reflector
private Class<?> type;//反射的对象参数的类
private String[] readablePropertyNames = EMPTY_STRING_ARRAY;
private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;
private Map<String, Invoker> setMethods = new HashMap<String, Invoker>();//set方法
private Map<String, Invoker> getMethods = new HashMap<String, Invoker>();//get方法
private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();//set方法的返回类
private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();//get方法的返回类
private Constructor<?> defaultConstructor;//构造函数
private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();//所有属性的字段
private Reflector(Class<?> clazz) {
type = clazz;
addDefaultConstructor(clazz);//构造函数部分
addGetMethods(clazz);//所有get的方法,包括父类的
addSetMethods(clazz);//所有set的方法,包括父类的
addFields(clazz);//所有属性的
//所有读的属性名字
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
//所有写的属性名字
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
//下面都把属性的名字存储到了caseInsensitivePropertyMap 中
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
public static Reflector forClass(Class<?> clazz) {
if (classCacheEnabled) {
// synchronized (clazz) removed see issue #461
Reflector cached = REFLECTOR_MAP.get(clazz);
if (cached == null) {
cached = new Reflector(clazz);
REFLECTOR_MAP.put(clazz, cached);
}
return cached;
} else {
return new Reflector(clazz);
}
}
增加所有包含get的方法
private void addGetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("get") && name.length() > 3) {
if (method.getParameterTypes().length == 0) {
//这里的名字首字符已经变成了小写
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingGetters, name, method);
}
} else if (name.startsWith("is") && name.length() > 2) {
if (method.getParameterTypes().length == 0) {
//这里的名字首字符已经变成了小写
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingGetters, name, method);
}
}
}
resolveGetterConflicts(conflictingGetters);
}
增加所有包含set的方法
private void addSetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("set") && name.length() > 3) {
if (method.getParameterTypes().length == 1) {
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingSetters, name, method);
}
}
}
resolveSetterConflicts(conflictingSetters);
}
所有的属性字段
private void addFields(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (canAccessPrivateMethods()) {
try {
field.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
if (field.isAccessible()) {
//setMethods的方法中不包括某个属性时候再增加到setMethods中
if (!setMethods.containsKey(field.getName())) {
// issue #379 - removed the check for final because JDK 1.5 allows
// modification of final fields through reflection (JSR-133). (JGB)
// pr #16 - final static can only be set by the classloader
int modifiers = field.getModifiers();
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
addSetField(field);
}
}
//getMethods的方法中不包括某个属性时候再增加getMethods中
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
}
if (clazz.getSuperclass() != null) {
addFields(clazz.getSuperclass());
}
}
最后在看看这个类的getSetInvoker和getGetInvoker
public Invoker getSetInvoker(String propertyName) {
Invoker method = setMethods.get(propertyName);
if (method == null) {
throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
}
return method;
}
public Invoker getGetInvoker(String propertyName) {
Invoker method = getMethods.get(propertyName);
if (method == null) {
throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
}
return method;
}
这里根据属性的名字在getMethods或者setMethods的Map获取相应的方法。
六、获取和设置bean的值某个属性值
这里我们通过一个Testmete 类来模拟了MyBatis的获取和设置参数对象的值。
- User类
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return UserName;
}
public void setUserName(String userName) {
UserName = userName;
}
public String getUserEmail() {
return UserEmail;
}
public void setUserEmail(String userEmail) {
UserEmail = userEmail;
}
private int userId;
private String UserName;
private String UserEmail;
@Override
public String toString() {
return "User [userId=" + userId + ", UserName=" + UserName
+ ", UserEmail=" + UserEmail + "]";
}
- 测试类
package com.elements.mete;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.junit.Test;
import com.elements.user.model.User;
public class Testmete {
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
@Test
public void TestMetaObject() {
User user = new User();
user.setUserEmail("likewindy@123.com");
user.setUserName("likewindy");
MetaObject metaObject = newMetaObject(user);
//获取值方法模式
Object value = metaObject.getValue("userName");
//获取值Field模式
Object value2 = metaObject.getValue("UserName");
System.out.println(value);
//设置UserEmail的值
metaObject.setValue("UserEmail", "123456@123.com");
System.out.println(user.getUserEmail());
}
}
获取bean的属性顺序图
最后我们在看这个MetaObject类
七、MetaObject类
它是mybatis对外的接口
private Object originalObject;//例如一个pojo对象,例如User对象类。
private ObjectWrapper objectWrapper;//根据参数对象的不同,在构造函数中配置
private ObjectFactory objectFactory;//Configuration中配置
private ObjectWrapperFactory objectWrapperFactory;//Configuration中配置
//构造函数,私有
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
if (object instanceof ObjectWrapper) {
//如果参数对象实现了ObjectWrapper
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
//如果objectWrapperFactory已经包装了对象,对用objectWrapperFactory的getWrapperFor
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
//是一个Map对象,使用mybatis的MapWrapper
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
//是一个CollectionWrapper对象
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
//其他默认使用BeanWrapper
this.objectWrapper = new BeanWrapper(this, object);
}
}
//对外公开的接口object是我们要获取或设置的对象
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory);
}
}
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else {
return objectWrapper.get(prop);
}
}
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) {
return; // don't instantiate child path if value is null
} else {
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
metaValue.setValue(prop.getChildren(), value);
} else {
objectWrapper.set(prop, value);
}
}
无论是set方法还是get方法它都是调用了objectWrapper的方法。这个objectWrapper就是根据参数对象的不同调用不同的对象,例如map包装对象MapWrapper,集合包装对象CollectionWrapper,和bean包装对象BeanWrapper。
在这里mybatis预留两个接口对象,可以自己封装。
- 参数对象实现了ObjectWrapper接口
- 实现了ObjectWrapperFactory的接口,并且通过配置应用到了Configuration类中。 它有一个默认的实现类DefaultObjectWrapperFactory
public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {
public boolean hasWrapperFor(Object object) {
return false;
}
public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");
}
}