MetaObject源码解析
MetaObject是个反射工具类它支持以“点.”的方式向下递归反射创建对象,属性赋值以及通过key名获取集合对象。
测试
public void shouldGetAndSetNestedField() { // 创建一个普通bean RichType rich = new RichType(); // 将bean包装成MetaObject对象 MetaObject meta = SystemMetaObject.forObject(rich); // 以map的方式 meta.setValue("richType.richMap[key]", "foo"); // 以属性的方式 meta.setValue("richType.richField", "foo"); // 不用.的方式,只能支持到二级属性 meta.setValue("richProperty", "foo"); System.out.println(meta.getValue("richType.richMap[key]")); System.out.println(meta.getValue("richType.richField")); System.out.println(meta.getValue("richProperty")); } // bean的结构 public class RichType { private String richField; private String richProperty; private Map richMap = new HashMap(); }
forObject,简单来说就是包装一个对象赋予一些能力
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) { this.originalObject = object;// 源对象 this.objectFactory = objectFactory;// 对象工厂 this.objectWrapperFactory = objectWrapperFactory;// 对象包装器工厂 // 对象包装器 if (object instanceof ObjectWrapper) { //如果对象本身已经是ObjectWrapper型,则直接赋给objectWrapper this.objectWrapper = (ObjectWrapper) object; } else if (objectWrapperFactory.hasWrapperFor(object)) { //如果有包装器,调用ObjectWrapperFactory.getWrapperFor this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object); } else if (object instanceof Map) { //如果是Map型,返回MapWrapper this.objectWrapper = new MapWrapper(this, (Map) object); } else if (object instanceof Collection) { //如果是Collection型,返回CollectionWrapper this.objectWrapper = new CollectionWrapper(this, (Collection) object); } else { //除此以外,返回BeanWrapper this.objectWrapper = new BeanWrapper(this, object); } }
分词器源码,主要是将我们的传入的值进行分割
public class PropertyTokenizer implements Iterable<PropertyTokenizer>, Iterator<PropertyTokenizer> { // 例子:richType.richMap[key] // 当前字段名 private String name; //richType private String indexedName; //richType // 下标,如果有值说明子属性有集合类型的 private String index; //0 // .后面的字段 private String children; //richMap 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); } }
setValue,递归调用,思路是先创建上层对象再创建下层属性(对象),知道最上层的对象才能知道它拥有什么属性
public void setValue(String name, Object value) { // 分词器进行分词,如richType.richMap[key],返回richType,子属性为richMap[key] PropertyTokenizer prop = new PropertyTokenizer(name); // 是否有子属性 if (prop.hasNext()) { // 内部就是递归调用,并将调用获取的值进行包装成MetaObject对象 // 此时传入的richType后面没有.就是直接创建这个对象了。 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 //如果上层就是null了,还得看有没有儿子,没有那就结束 return; } else { //否则还得new一个,委派给ObjectWrapper.instantiatePropertyValue创建新实例 metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory); } } //递归调用setValue,给子属性赋值 metaValue.setValue(prop.getChildren()/*子属性名称*/, value); } else { //到了最后一层了,所以委派给ObjectWrapper.set,调用setter方法 objectWrapper.set(prop, value); } }
metaObjectForProperty 没什么好说的,就是将当前对象包装成MetaObject,如果返回null说明是类不是属性就会进入上面的第5行代码里面的逻辑
public MetaObject metaObjectForProperty(String name) { //实际是递归调用 Object value = getValue(name); return MetaObject.forObject(value, objectFactory, objectWrapperFactory); }
getValue套路跟setValue一样的,获取prop.name字段的对象
public Object getValue(String name) { PropertyTokenizer prop = new PropertyTokenizer(name); if (prop.hasNext()) { MetaObject metaValue = metaObjectForProperty(prop.getIndexedName()); if (metaValue == SystemMetaObject.NULL_META_OBJECT) { //如果上层就是null了,那就结束,返回null return null; } else { //否则继续看下一层,递归调用getValue return metaValue.getValue(prop.getChildren()); } } else { return objectWrapper.get(prop); } }
objectWrapper.get(prop, value)(objectWrapper.set(prop, value)); 两个作用,解析集合并赋值返回,反射调用对象的getter方法
@Override public Object get(PropertyTokenizer prop) { //如果有index(有中括号),说明是集合,那就要解析集合 if (prop.getIndex() != null) { // 内部还是递归调用getValue,主要是通过反射获取并创建集合属性对象,到最终都会走向getBeanProperty方法 Object collection = resolveCollection(prop, object); // 给集合赋值并获取 return getCollectionValue(prop, collection); } else { //否则,getBeanProperty,底层通过反射调用getter方法 return getBeanProperty(prop, object); } } // 如上 public void set(PropertyTokenizer prop, Object value) { if (prop.getIndex() != null) { Object collection = resolveCollection(prop, object); setCollectionValue(prop, collection, value); } else { setBeanProperty(prop, object, value); } }
集合赋值方法
protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) { if (collection instanceof Map) { ((Map) collection).put(prop.getIndex(), value); } else { int i = Integer.parseInt(prop.getIndex()); if (collection instanceof List) { ((List) collection).set(i, value); } else if (collection instanceof Object[]) { ((Object[]) collection)[i] = value; } else if (collection instanceof char[]) { ((char[]) collection)[i] = (Character) value; } else if (collection instanceof boolean[]) { ((boolean[]) collection)[i] = (Boolean) value; } else if (collection instanceof byte[]) { ((byte[]) collection)[i] = (Byte) value; } else if (collection instanceof double[]) { ((double[]) collection)[i] = (Double) value; } else if (collection instanceof float[]) { ((float[]) collection)[i] = (Float) value; } else if (collection instanceof int[]) { ((int[]) collection)[i] = (Integer) value; } else if (collection instanceof long[]) { ((long[]) collection)[i] = (Long) value; } else if (collection instanceof short[]) { ((short[]) collection)[i] = (Short) value; } else { throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array."); } } }
整体流程图
总结:通过分词区分当前属性和子属性,通过反射先创建当前属性,后创建子属性,反复循环操作。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报