Mybatis-03 MetaObject学习

news2024/11/24 3:16:15

一. 反射调用

如果我们需要给 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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1893535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FreeRTOS之队列上锁和解锁(详解)

这篇文章将记录我学习实时操作系统FreeRTOS的队列上锁和解锁的知识&#xff0c;在此分享给大家&#xff0c;希望我的分享能给你带来不一样的收获&#xff01; 目录 一、简介 二、队列上锁函数prvLockQueue&#xff08;&#xff09; 1、函数初探 2、应用示例 三、队列解锁函…

【代码随想录】【算法训练营】【第55天】 [42]接雨水 [84]柱状图中最大的矩形

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 55&#xff0c;又是一个周一&#xff0c;不能再坚持~ 题目详情 [42] 接雨水 题目描述 42 接雨水 解题思路 前提&#xff1a;雨水形成的情况是凹的, 需要前中后3个元素&#xff0c;计算该元…

Modbus协议转Profinet协议网关模块连智能仪表与PLC通讯

一、现场需求&#xff1a;PLC作为控制器&#xff0c;仪表设备做为执行设备&#xff0c;执行设备能够实时响应PLC传来的指令&#xff0c;并且向PLC回馈数据&#xff0c;从而达到PLC对仪表设备进行控制和监测&#xff0c;实现对生产过程的精准控制。 二、解决方案&#xff1a;通过…

机器学习与AI大数据的融合:开启智能新时代

在当今这个信息爆炸的时代&#xff0c;大数据和人工智能&#xff08;AI&#xff09;已经成为推动社会进步的强大引擎。作为AI核心技术之一的机器学习&#xff08;Machine Learning, ML&#xff09;&#xff0c;与大数据的深度融合正引领着一场前所未有的科技革命&#xff0c;不…

Java判断范围型的数据是否存在重叠(数值类型、日期类型)

为什么写这么一篇文章呢&#xff1f; 遇到了个问题&#xff0c;同一天可以输入多个时间段&#xff0c;但是每个时间段的时间不能出现重叠。 纳尼&#xff0c;这不就是判断数据返回是否有重叠的变种嘛~ 简单&#xff0c;开搞 数字范围是否重叠判断 这里以int类型为例了&…

高德地图轨迹回放并提示具体信息

先上效果图 到达某地点后显示提示语:比如:12:56分驶入康庄大道、左转驶入xx大道等 <!doctype html> <html> <head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta n…

基于Vue.js的电商前端模板:Vue-Dashboard-Template的设计与实现

摘要 随着电子商务的飞速发展&#xff0c;前端页面的设计和实现变得愈发重要。本文介绍了一个基于Vue.js的电商前端模板——Vue-Dashboard-Template&#xff0c;旨在提供一个高性能、易扩展的电商平台前端解决方案。该模板遵循响应式设计、模块化、组件化开发等设计原则&#…

预制菜工厂MES系统:具体功能与应用场景

在现代化食品工业中&#xff0c;预制菜&#xff08;Ready-to-Eat, RTE&#xff09;因其方便快捷、卫生安全及营养均衡的特点&#xff0c;迅速在餐饮行业中占据重要地位。为了进一步提升预制菜工厂的生产效率、保障产品质量并降低生产成本&#xff0c;制造执行系统&#xff08;M…

C# 快速排序算法的详细讲解

目录 一、前言 二、例子 三、快速排序算法图片讲解 四、快速排序算法代码 五、纯净代码 一、前言 用比较好懂的方式讲一下快速排序算法。 二、例子 如果我有一堆钱&#xff0c;想数清楚&#xff0c;最快的方案是什么&#xff1f; 图1 一堆钱 答&#xff1a;先分类&…

【Linux开发实战指南】基于TCP、进程数据结构与SQL数据库:构建在线云词典系统(含注册、登录、查询、历史记录管理功能及源码分享)

目录 项目演示&#xff1a; 1. 主界面 技术讲解&#xff1a; TCP连接 进程的并发 链表 SQLite3 IO对文件的读写 功能实现 实现逻辑 我遇到的问题&#xff1a; 服务器端代码思路解析 必要条件 步骤详解 客户端代码思路解析 步骤详解 服务器源码如下&#xff1a;…

GD32实战项目-app inventor-BLE低功耗DX-BT24蓝牙上位机制作-文末有关于生成的软件闪退或者卡死问题的解决

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 后续项目主要在下面该专栏中发布&#xff1a; 手把手教你嵌入式国产化_不及你的温柔的博客-CSDN博客 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转&#xff1a; 手把手教你嵌入式国产化-实战项目-无刷电机驱动&am…

什么开放式耳机好用?五大王牌开放式耳机种草!

随着科技的持续进步&#xff0c;开放式蓝牙耳机悄然兴起&#xff0c;逐步取代了经典的入耳式耳机。入耳式耳机以其卓越的隔音性能著称&#xff0c;然而&#xff0c;长时间的使用却容易引发耳道受压&#xff0c;伴随而来的不仅是疼痛与不适&#xff0c;更潜藏着耳膜受损的风险。…

C++面试宝典30题丨第一题:开灯

专栏导读 见得题目越多&#xff0c;考试时抽中的概率也越大。每一题都有详细的解答思路和独有的视频讲解。 本文收录于&#xff1a;C面试宝典&#xff08;送视频讲解&#xff09; ☆☆☆购买专栏后&#xff0c;请加微信会私发讲解视频&#xff01; 题目描述 一条名叫Mango的街…

简过网:一建和二建的含金量,哪个难度更大一些?

你知道&#xff0c;一建和二建有什么区别吗&#xff0c;考哪个更合适自己呢&#xff1f;正在备考一建、二建的小伙伴们&#xff0c;这篇文章千万不要错过哦&#xff01; 首先&#xff0c;先说一下大家比较关注的含金量问题&#xff0c;一建含金量明显比二建高&#xff0c;但是…

MySQL篇-SQL优化实战-减少子查询

回顾 上一篇了解了分析SQL使用的explain&#xff0c;可以点击查看MySQL篇-SQL优化实战了解我在写sql的注意事项还有explain的说明&#xff0c;这次拿一段生产使用的sql进行优化说明。从14s优化到2.6s 待优化的SQL SELECT DISTINCTswpe.tag_number,hca.ACCOUNT_NAME customer…

精准定位推广盲点?Xinstall数据监测让每一分投入都见成效!

在这个数字化时代&#xff0c;App的推广早已不再是简单的“上线即成功”。面对激烈的市场竞争和日益挑剔的用户&#xff0c;如何精准监测推广数据&#xff0c;优化营销策略&#xff0c;成为了每个开发者与营销人员不得不面对的挑战。而在这个关键时刻&#xff0c;Xinstall作为一…

AI 驱动的数据中心变革与前景

文章主要探讨了AI计算时代数据中心的转型&#xff0c;涉及计算技术的多样性、规格尺寸和加速器的发展、大型语言模型&#xff08;LLM&#xff09;的发展、功耗和冷却趋势、基准测试的重要性以及数据中心的发展等方面。为大家提供深入了解AI基础设施发展的视角。 计算技术的多样…

Python 程序打印图案“G”(Python Program to print the pattern ‘G’)

在本文中&#xff0c;我们将学习如何使用星号和空格打印图案 G。给定一个数字 n&#xff0c;我们将编写一个程序&#xff0c;在 n 行或列上打印图案 G。 例子&#xff1a; 输入&#xff1a;7 输出&#xff1a; *** * * * *** * * * * *** 输入&…

红酒的秘密花园:探索葡萄的种植艺术

在远离城市喧嚣的某个角落&#xff0c;隐藏着一座神秘的红酒秘密花园。这里&#xff0c;葡萄藤缠绵交织&#xff0c;绿叶间闪烁着晶莹的露珠&#xff0c;仿佛在诉说着关于红酒与葡萄种植艺术的古老传说。今天&#xff0c;就让我们一起走进这片神秘的花园&#xff0c;探寻葡萄种…

@amap/amap-jsapi-loader 实现高德地图中添加多边围栏,并可编辑,编辑后获得围栏各个点的经纬度

先上一张效果图 看看是不是大家想要的效果&#xff5e; ❤️ 希望其中的小点能帮助大家&#xff0c;主要看怎么绘制在地图上的代码即可 1.第一步要加入项目package.json中或者直接yarn install它都可以 想必大家应该都会 "amap/amap-jsapi-loader": "0.0.7&qu…