前言

之前在写-->聊聊基于jdk实现的spi如何与spring整合实现依赖注入这篇文章的demo时,用到了动静代理,在进行调试,发现一个神奇的景象。如下图


代理对象变成null,但不会有空指针异样

景象剖析

首先看下示例代理的外围实现逻辑

 @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        boolean canPass = preHandle(method,args);        Object result = null;        if(canPass){             result = method.invoke(target,args);             afterCompletion(method,args);        }        return result;    }

这段逻辑当canpass为false时,result会为null。idea开启调试,调用对象时,默认会调用toString办法,当代理触发invoke,因为preHandle找不到toString办法,会导致canPass为false,从而触发null景象

口说无凭,咱们能够验证下,咱们对代理外围办法进行调整下

   @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if("toString".equals(method.getName())){            return "我是toString办法";        }        boolean canPass = preHandle(method,args);        Object result = null;        if(canPass){             result = method.invoke(target,args);             afterCompletion(method,args);        }        return result;    }

此时再进行dubug,如下图

问题修复

1、办法一:禁用掉idea默认调用toString办法

2、办法二:在代理invoke办法中,增加如下如下代码片段
 // 如果Object办法间接反射调用        if(Object.class.equals(method.getDeclaringClass())){            return method.invoke(this, args);        }

这种解决思路,在mybatis实现的动静代理就有呈现

@Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    try {      if (Object.class.equals(method.getDeclaringClass())) {        return method.invoke(this, args);      } else if (method.isDefault()) {        if (privateLookupInMethod == null) {          return invokeDefaultMethodJava8(proxy, method, args);        } else {          return invokeDefaultMethodJava9(proxy, method, args);        }      }    } catch (Throwable t) {      throw ExceptionUtil.unwrapThrowable(t);    }    final MapperMethod mapperMethod = cachedMapperMethod(method);    return mapperMethod.execute(sqlSession, args);  }

上文摘自mybatis的MapperProxy代理片段

总结

多点好奇,多看源码