一. 反射调用
如果我们需要给 bean 对象的属性设值,除了我们常用的构造调用方法之外,还可以用反射;
下面我们举一个例子;
public static void main(String[] args) throws Exception {
Class<Bean01> bean01Clazz = Class.forName("com.zengqiang.reflect.Bean01");
Bean01 bean01 = bean01Clazz.newInstance();
Field address = bean01Clazz.getDeclaredField("address");
address.setAccessible(true);
address.set(bean01, "jx");
System.out.println(bean01);
}
我们既可以通过属性,也可以通过方法给 bean 对象反射设值;
二. MetaObject
Mybatis 提供了一种比较方便的类,有助于减少我们手动调用反射,并且功能强大,它就是 MetaObject;
1. 简单使用
首先我们提供了几个演示用的 Bean 对象,如下:
// -------------------------------- Blog ---------------------------------
@Getter
@Setter
@ToString
public class Blog {
private int id;
private String name;
private List<User> users;
private Map<String, String> others;
}
// -------------------------------- User ---------------------------------
@Getter
@Setter
@ToString
public class User {
private int id;
private String username;
private String password;
private Bean02 bean02;
public User() {
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public User(int id, String username, String password, Bean02 bean02) {
this.id = id;
this.username = username;
this.password = password;
this.bean02 = bean02;
}
}
// -------------------------------- Bean02 ---------------------------------
@Getter
@Setter
@ToString
public class Bean02 {
private int id;
private String name;
public Bean02() {
}
public Bean02(int id, String name) {
this.id = id;
this.name = name;
}
}
简答使用如下:
public static void testMetaObject() {
// 生成 JavaBean 类型的 MetaObject
Blog blog = new Blog();
MetaObject metaObject = SystemMetaObject.forObject(blog);
// 操作普通成员属性
metaObject.setValue("name", "zqBlog");
System.out.println(metaObject.getValue("name"));
// 操作 Map 属性
metaObject.setValue("others", new HashMap<String, String>());
metaObject.setValue("others[key1]", "value1");
System.out.println(metaObject.getValue("others"));
System.out.println(metaObject.getValue("others[key1]"));
// 操作 List 属性
// MetaObject 可以操作级联属性
ArrayList<User> list = new ArrayList<>();
User user = new User(1, "zq", "aaa", new Bean02(2, "zw"));
list.add(user);
metaObject.setValue("users", list);
System.out.println(metaObject.getValue("users"));
System.out.println(metaObject.getValue("users[0]"));
System.out.println(metaObject.getValue("users[0].bean02.name"));
System.out.println(blog);
}
2. MetaObject的核心组件
- ObjectWrapper:MetaObject 内部通过 ObjectWrapper 接口来实现对对象的包装。ObjectWrapper 的实现类有多种,比如针对普通 JavaBean 的 BeanWrapper,针对 Map 类型的 MapWrapper,针对 Collection 类型的 CollectionWrapper 等,这样做的目的是抽象出统一的操作接口,便于 MetaObject 统一处理各种类型的对象。
- PropertyTokenizer:PropertyTokenizer 是一个对属性表达式进行解析的工具类,也叫属性分词器,它可以解析复杂的属性表达式,如 order[0].item.name,便于 MetaObject 进行属性的递归访问。
- Reflector 和 ReflectorFactory: 这两个类负责缓存反射操作的元数据,以减少对同一类的反复反射,从而提高性能。
3. PropertyTokenizer
我们分析一下 PropertyTokenizer,它的代码非常简单,是一个堆属性表达式进行解析的工具类、属性分词器;
我们举几个表达式例子来创建 PropertyTokenizer,看下该分词器的各个属性值;
1、users[0].bean02.name
2、bean02.name
3、bean02
PropertyTokenizer 类如下:
// --------------------------- PropertyTokenizer ----------------------------
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
private String name;
private final String indexedName;
private String index;
private final String children;
public PropertyTokenizer(String fullname) {
int delim = fullname.indexOf('.');
if (delim > -1) {
name = fullname.substring(0, delim);
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
indexedName = name;
delim = name.indexOf('[');
if (delim > -1) {
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
public String getName() {
return name;
}
public String getIndex() {
return index;
}
public String getIndexedName() {
return indexedName;
}
public String getChildren() {
return children;
}
@Override
public boolean hasNext() {
return children != null;
}
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
它实现了 Iterator 接口,这个接口我们虽然接触的多,但是很少了解,可以看一下该接口;
可以看到该接口有两个抽象方法,我们的 PropertyTokenizer 实现了这两个抽象方法;
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
4. getValue()分析
我们以表达式 users[0].bean02.name 来看 MetaObject.getValue();
这里比较绕,需要自己 debug 看具体细节流程;
// ------------------------------ MetaObject ---------------------------------
public Object getValue(String name) {
// 1. 根据 name 表达式生成 propertyTokenizer
PropertyTokenizer prop = new PropertyTokenizer(name);
// 2. 如果还有 propertyTokenizer 有 children 的话,走下述流程
if (prop.hasNext()) {
// 2.1 根据 prop.getIndexedName() 获取并生成新的 metaObject
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
// 2.1.1 metaValue 中的 origialObject 为 null,直接返回 null
return null;
} else {
// 2.1.2 metaValue 不为空
// 此时继续调用 metaValue.getValue() 获取 children 的值
return metaValue.getValue(prop.getChildren());
}
} else {
// 3. 此时 propertyTokenizer 没有 children
// 调用 ObjectWrapper.get(propertyTokenizer) 获取并返回值
return objectWrapper.get(prop);
}
}
// ------------------------------ MetaObject ---------------------------------
public MetaObject metaObjectForProperty(String name) {
// 1. 调用套娃的 getValue(),或者 name 对应的值
Object value = getValue(name);
// 2. 调用 MetaObject.forObject(),将 value 值作为 originalObject 生成新的 MetaObject
return MetaObject.forObject(value, objectFactory,
objectWrapperFactory, reflectorFactory);
}
// ------------------------------ MetaObject ---------------------------------
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
// 对应 object 为 null 的情况
return new MetaObject(null, objectFactory,
objectWrapperFactory, reflectorFactory);
} else {
// 对应 object 不为 null 的情况
return new MetaObject(object, objectFactory,
objectWrapperFactory, reflectorFactory);
}
}
我们再来看下 objectWrapper.get(prop),它里面又会进行一个套娃;
// ------------------------------ BeanWrapper ---------------------------------
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
// 1. propertyTokenizer 有索引,如 users[0] 中,index = 0
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
// 2. propertyTokenizer 没有索引
return getBeanProperty(prop, object);
}
}
我们先看没有索引的情况,调用 getBeanProperty();
// ------------------------------ BeanWrapper ---------------------------------
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
// 1. 通过 propertyTokenizer 的 name 获取对象的 getter 方法
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
// 2. 反射调用获取属性值
return method.invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new ReflectionException();
}
}
再来看有索引的情况,先调用 resolveCollection(),再调用 getCollectionValue();
// ------------------------------ BaseWrapper ---------------------------------
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
if ("".equals(prop.getName())) {
return object;
} else {
// 调用 metaObject.getValue()
// 传的是 prop.getName(),如 users[0],prop.getName() = users
// 也就是先获取 users 这个集合
return metaObject.getValue(prop.getName());
}
}
// ------------------------------ BaseWrapper ---------------------------------
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
if (collection instanceof Map) {
// 1. 如果 collection 是 map 的话,调用 map.get()
// 传的是 prop.getIndex(),如 users[key1],prop.getIndex() = key1
return ((Map) collection).get(prop.getIndex());
} else {
// 2. colletion 是 list 等,同理
int i = Integer.parseInt(prop.getIndex());
if (collection instanceof List) {
return ((List) collection).get(i);
} else if (collection instanceof Object[]) {
return ((Object[]) collection)[i];
} else if (collection instanceof char[]) {
return ((char[]) collection)[i];
} else if (collection instanceof boolean[]) {
return ((boolean[]) collection)[i];
} else if (collection instanceof byte[]) {
return ((byte[]) collection)[i];
} else if (collection instanceof double[]) {
return ((double[]) collection)[i];
} else if (collection instanceof float[]) {
return ((float[]) collection)[i];
} else if (collection instanceof int[]) {
return ((int[]) collection)[i];
} else if (collection instanceof long[]) {
return ((long[]) collection)[i];
} else if (collection instanceof short[]) {
return ((short[]) collection)[i];
} else {
throw new ReflectionException();
}
}
}
MetaObject 进行了大量的递归套娃,最好 debug 看一下流程,至此,MetaObject 分析完毕;
流程如下,以 users[0].bean02.name 为例;
// ------------------------------ MetaObject ---------------------------------
public Object getValue(String name) {
// 1. 根据 name 表达式生成 propertyTokenizer
PropertyTokenizer prop = new PropertyTokenizer(name);
// 2. 如果还有 propertyTokenizer 有 children 的话,走下述流程
if (prop.hasNext()) {
// 2.1 根据 prop.getIndexedName() 获取并生成新的 metaObject
// prop.getIndexedName() = users[0]
// 此时会获取到 users[0] 对应的 object 值
// 该 metaValue 的 origialObject 为 users[0] 对应的值
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
// 该 metaValue 的 origialObject 为 users[0] 对应的值
// prop.getChildren() = bean02.name
// 调用并返回 metaValue.getValue("bean02.name")
return metaValue.getValue(prop.getChildren());
}
} else {
// 3. 此时 propertyTokenizer 没有 children
// 调用 ObjectWrapper.get(propertyTokenizer) 获取并返回值
return objectWrapper.get(prop);
}
}