mybatis注册一个自定义拦截器,拦截器用于自动填充字段

MetaObject 通过反射机制访问 parameter 对象的类对象(Class 对象),为什么要修改类对象的属性值,类对象里都没有属性值,属性值在实例对象里呀,实例对象的属性值操作不需要反射呀?

场景:mybatisplus有自动生成主键的雪花算法策略,在实体类上,但是前提是数据库操作要走mybatisplus提供的BaseMapper里的insetr或update,

@GeneratedValue是jpa的注解,这里设置了自增,在生表阶段,不会设置id为雪花算法

将id赋值为雪花id是在哪个阶段呢,是在sql执行阶段,很好,那就是对应dao层框架了,他们封装了对于数据库的操作呀,尤其是mybatis,但很遗憾,如果你走的是mybatis的i定义接口和接口映射文件这一套,那就不能触发雪花id的生成策略了,因为mybatis实现操作数据库靠的是:依靠接口动态生成实现类,不要怀疑,真的有class对象和实例对象生成,再根据接口和实现类搞一个代理实现类的代理对象,这个代理对象里就是jdbc操作了,具体操作就看mapper.xml解析出来的内容了,所以mybatisse会调用代理对象的方法,所以,压根和baseMApepr没关系,

所以我们只能搞拦截器了,

复制代码
@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})

public class CustomIdInterceptor implements Interceptor, InnerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(CustomIdInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];

//        如果是插入操作
        if ("insert".equalsIgnoreCase(mappedStatement.getSqlCommandType().toString())) {

//        如果parameter是个List集合,那就是批量插入
            if (parameter instanceof java.util.List) {
//            遍历
                for (Object obj : (java.util.List) parameter) {
                    MetaObject metaObject = SystemMetaObject.forObject(obj);
//                处理id
                    if (metaObject.hasGetter("id")) {
                        long id = IdUtil.getSnowflake().nextId();
                        metaObject.setValue("id", id);
                    }
//                处理deleted字段
                    if (metaObject.hasGetter("deleted")) {
                        metaObject.setValue("deleted", false);
                    }

//                处理创建时间
                    if (metaObject.hasGetter("createdTime")) {
                        metaObject.setValue("createdTime", java.time.LocalDateTime.now());
                    }

//                处理创建人
                    if (metaObject.hasGetter("createdBy")) {
                        metaObject.setValue("createdBy", UserUtil.getCurUser().getId());
                    }


                }
            }else{
//            如果不是集合

//                获取参数的mataObjiect

                MetaObject metaObject = SystemMetaObject.forObject(parameter);
//                处理id
                if (metaObject.hasGetter("id")) {
                    long id = IdUtil.getSnowflake().nextId();
                    metaObject.setValue("id", id);
                }
//                处理deleted字段
                if (metaObject.hasGetter("deleted")) {
                    metaObject.setValue("deleted", false);
                }

//                处理创建时间
                if (metaObject.hasGetter("createdTime")) {
                    metaObject.setValue("createdTime", java.time.LocalDateTime.now());
                }

//                处理创建人
                if (metaObject.hasGetter("createdBy")) {
                    metaObject.setValue("createdBy", UserUtil.getCurUser().getId());
                }


            }

        }


//            如果是更新操作
        if ("update".equalsIgnoreCase(mappedStatement.getSqlCommandType().toString())) {

//            获取参数的mataObjiect
            MetaObject metaObject = SystemMetaObject.forObject(parameter);
//            处理更新时间
            if (metaObject.hasGetter("updatedTime")) {
                metaObject.setValue("updatedTime", java.time.LocalDateTime.now());
            }
//            处理更新人
            if(metaObject.hasGetter("updatedBy")){
                metaObject.setValue("updatedBy", UserUtil.getCurUser().getId());
            }

        }



        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        return InnerInterceptor.super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可以在这里设置拦截器的属性
    }
}

看这小拦截器,其中的mateObject和Excuter,MappedStatementd都是mybatise的核心对象,但是,看了调试上的实例对象的地址,我发现

这个参数其实是你的实体类,

MetaObject 通过反射机制访问 parameter 对象的类对象(Class 对象),为什么要修改类对象的属性值,类对象里都没有属性值,属性值在实例对象里呀,实例对象的属性值操作不需要反射呀?

记住我的神之回答

获取 类对象 不需要知道实例对象的类型,

1. 实例对象与类对象的区别

在 Java 中:

  • 类对象(Class Object)

    • 是类的运行时表示形式,存储在方法区(Metaspace)中。

    • 包含类的元数据,例如字段、方法、构造函数等的定义。

    • 不包含实例字段的值,因为实例字段的值是属于具体实例对象的。

  • 实例对象(Instance Object)

    • 是类的具体实例,存储在堆(Heap)中。

    • 包含实例字段的实际值。

    • 每个实例对象都有自己的字段值,这些值是独立的。


2. 为什么需要反射?

在你的代码中,MetaObject 通过反射机制访问和修改实例对象的字段值。这是因为:

  1. 动态性

    • 在运行时,你可能不知道 parameter 对象的具体类型。它可能是任意实现了接口的类的实例
    • 反射机制允许你在运行时动态地访问和修改对象的字段,而无需在编译时知道具体类型。
  2. 封装性

    • Java 的封装性使得字段通常是私有的(private),无法直接访问。

    • 反射机制允许绕过封装性,直接访问和修改私有字段的值。

      • 你用getter和setter每个方法都不一样,怎解,就问你怎解。。。。

      • ​​​​​​​就算是通过反射,类对象里只有字段,没有字段值,所以修改字段值必定是操作了实例对象,这个没跑,就看是怎么操作的了,

        • 这是先将字段属性设置成外部可直接访问的,再对实例对象进行修改,改玩在设置成不可访问。。。。。。
  3. 通用性

    • MetaObject 提供了一种通用的方式来操作对象的字段,而无需针对每个类编写特定的代码。

    • 这使得拦截器可以处理任意类型的对象,只要这些对象有相应的字段。


3. 实例对象的属性值操作为什么需要反射?

虽然实例对象的属性值存储在实例对象中,但直接操作这些属性值需要知道对象的具体类型。例如:

java复制

复制代码
MyEntity entity = (MyEntity) parameter;
entity.setId(IdUtil.getSnowflake().nextId());

这种方式的问题是:

  • 类型硬编码 :你需要知道 parameter 的具体类型(如 MyEntity),并且在代码中显式地进行类型转换。

  • 不通用 :如果 parameter 是其他类型的对象,这段代码就会失败。

使用反射的优势

通过反射,你可以动态地访问和修改字段值,而无需知道具体类型。例如:

java复制

复制代码
MetaObject metaObject = SystemMetaObject.forObject(parameter);
if (metaObject.hasGetter("id")) {
    long id = IdUtil.getSnowflake().nextId();
    metaObject.setValue("id", id);
}

这种方式的优点是:

  • 通用性 :可以处理任意类型的对象,只要这些对象有 id 字段。

  • 动态性:在运行时检查字段是否存在,并动态地设置值。


4. MetaObject 的实现原理

MetaObject 是 MyBatis 提供的一个工具类,它封装了反射操作,使得字段访问和修改更加方便。它的实现基于以下机制:

  1. 反射机制

    • 使用 Class 对象获取字段的定义。

    • 使用 Field 对象访问和修改字段值。

  2. 动态代理

    • MetaObject 提供了一种代理机制,使得你可以通过统一的接口访问和修改字段值,而无需直接使用反射 API。
  3. 缓存机制

    • 为了提高性能,MetaObject 会缓存类的元数据,避免重复的反射操作。

5. 示例代码解析

在你的代码中:

java复制

复制代码
MetaObject metaObject = SystemMetaObject.forObject(parameter);
if (metaObject.hasGetter("id")) {
    long id = IdUtil.getSnowflake().nextId();
    metaObject.setValue("id", id);
}
  • SystemMetaObject.forObject(parameter) 创建了一个 MetaObject 实例,封装了对 parameter 对象的反射操作。

  • hasGetter("id") 检查 parameter 对象的类是否有 id 字段。

  • setValue("id", id) 动态地为 parameter 对象的 id 字段设置值。


6. 总结

  • 类对象(Class Object):存储类的元数据,不包含实例字段的值。

  • 实例对象(Instance Object):存储实例字段的实际值。

  • 反射机制:允许在运行时动态地访问和修改实例对象的字段值,而无需知道具体类型。

  • MetaObject:封装了反射操作,提供了一种通用的方式来操作实例对象的字段值。

通过反射和 MetaObject,你的拦截器可以动态地为任意类型的对象设置字段值,而无需在编译时知道具体类型。这种机制使得拦截器具有很高的通用性和灵活性。

相关推荐
毕设源码-朱学姐5 小时前
【开题答辩全过程】以 工厂能耗分析平台的设计与实现为例,包含答辩的问题和答案
java·vue.js
喵了meme5 小时前
C语言实战4
c语言·开发语言
码界奇点5 小时前
Python从0到100一站式学习路线图与实战指南
开发语言·python·学习·青少年编程·贴图
9ilk5 小时前
【C++】--- 特殊类设计
开发语言·c++·后端
sali-tec6 小时前
C# 基于halcon的视觉工作流-章68 深度学习-对象检测
开发语言·算法·计算机视觉·重构·c#
Spring AI学习6 小时前
Spring AI深度解析(9/50):可观测性与监控体系实战
java·人工智能·spring
java1234_小锋7 小时前
Spring IoC的实现机制是什么?
java·后端·spring
生骨大头菜7 小时前
使用python实现相似图片搜索功能,并接入springcloud
开发语言·python·spring cloud·微服务
绝不收费—免费看不了了联系我7 小时前
Fastapi的单进程响应问题 和 解决方法
开发语言·后端·python·fastapi
xqqxqxxq7 小时前
背单词软件技术笔记(V2.0扩展版)
java·笔记·python