You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JDKProxy实现MyBatis时出现StackOverflowError问题求助

问题分析与解决方案

为啥会突然爆StackOverflowError?

你碰到的这个栈溢出问题,本质是无限递归调用搞出来的——虽然你没手动调用proxy.toString(),但大概率是IDE调试时自动触发的:IDE展示代理对象信息时,会默认调用它的toString()方法,这就间接启动了代理逻辑。

具体到你的代码逻辑:
当代理对象触发toString()这类Object自带的方法时,会进入MapperProxy.invoke()。因为toString()不是TestMapper接口里定义的方法,所以mapperData会是null;如果之前的代码是调用method.invoke(proxy, args),那相当于再次调用代理对象的toString(),又会回到invoke()方法,无限循环下去,栈直接被撑爆。

而你改成return method.invoke(sqlSession, args)后,因为sqlSession是真实的MySqlSession实例(不是代理对象),调用它的toString()不会触发代理逻辑,递归直接终止,自然就没异常了。不过这么做其实有点“歪打正着”,因为代理对象的toString()应该返回自身的信息,而不是sqlSession的,不符合代理的语义。

正确的解决姿势

我们需要在invoke()方法里先做个判断:如果当前调用的是Object类的方法(比如toString()、hashCode()、equals()),直接走原生逻辑,别进入代理处理;只有Mapper接口定义的方法,才走SQL执行逻辑。

修改后的MapperProxy代码如下:

public class MapperProxy<T> implements InvocationHandler {
    private final MySqlSession sqlSession;

    // 构造函数注入sqlSession,原代码里应该漏了这个字段声明
    public MapperProxy(MySqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 优先处理Object类的原生方法,避免递归
            if (method.getDeclaringClass() == Object.class) {
                // 调用InvocationHandler实例自身的Object方法,不会触发代理逻辑
                return method.invoke(this, args);
            }

            // 下面是原有的Mapper方法处理逻辑
            int seperate = method.getDeclaringClass().getName().lastIndexOf(".");
            String mapper = method.getDeclaringClass().getName().substring(seperate + 1);
            MapperData mapperData = sqlSession.getConfiguration()
                    .getMapperRegistory()
                    .get(mapper + "." + method.getName());
            
            if (mapperData != null) {
                System.out.println(String.format("SQL [%s],parameter [%s]", mapperData.getSql(), args[0]));
                Class<?> clazz = sqlSession.getClass();
                Method realMethod = clazz.getMethod(method.getName(), MapperData.class, Object.class);
                return realMethod.invoke(sqlSession, mapperData, args);
            }

            // 非Object方法也非Mapper方法的情况,抛出异常提示
            throw new UnsupportedOperationException(String.format("不支持调用方法:%s", method.getName()));
        } catch (InvocationTargetException ite) {
            throw ite.getCause();
        }
    }
}

核心改动点

  1. 新增Object方法判断分支:把Object类的方法单独拎出来处理,直接调用this(也就是MapperProxy实例)的对应方法,完全绕开代理逻辑,从根源上避免递归。
  2. 补全sqlSession字段:原代码里MapperProxy没有显式声明sqlSession成员变量,必须通过构造函数注入,否则编译会报错。

额外提醒

除了toString(),hashCode()和equals()这两个Object方法也可能触发相同的栈溢出问题,所以这个判断分支一定要覆盖所有Object类的方法,不能漏。

内容的提问来源于stack exchange,提问作者dc p

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:32:38