还记得Proxy.newProxyInstance吗

91 阅读1分钟

再看Proxy.newProxyInstance与InvocationHandler

public static Object Proxy::newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
/**
 * Returns an instance of a proxy class for the specified interfaces
 * that dispatches method invocations to the specified invocation
 * handler.
*/
为指定得一些接口返回一个代理类实例,该实例分发方法调用到指定的invocation handler

public Object InvocationHandler::invoke(Object proxy, Method method, Object[] args)
proxy参数就是newProxyInstance返回值,method为interfaces中的某个方法,args为方法参数
返回值作为方法的返回值

精简代码测验

public interface BrandMapper {
    List getAll();
}
public class MapperProxy<T> implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        System.out.println("进入代理1:" + method);
        System.out.println("进入代理2:" + proxy);
        //将逻辑委托给真正的实现
        method.invoke(new DefaultBrandMapper(),args);
        return null;
    }

    @Override
    public String toString() {
        System.out.println("MapperProxy toString");
        return "111";
    }
}
public class ProxyTestBrief {
    public static void main(String[] args) {
        MapperProxy<BrandMapper> invocationHandler = new MapperProxy();
        BrandMapper brandMapper = (BrandMapper) Proxy.newProxyInstance(BrandMapper.class.getClassLoader(), new Class[]{BrandMapper.class},invocationHandler);
        brandMapper.getAll();
    }
}

测试1--注释到MapperProxy的toString方法

断点测试执行到System.out.println("进入代理2:" + proxy)时,程序再次进入invoke方法,因为在代理实例上执行的方法都被分发给了invocationHandler的invoke接口,此时if条件生效,执行方法toString为顶层Object实例接口,方法调用转为invocationHandler的方法

灵魂拷问--若把if注释掉会怎样

System.out.println("进入代理2:" + proxy)会一直调用invoke方法,栈溢出,在mybatis源码中的出现

image.png 虽然源码没有打印,但对源码某行debug触发toString时,若没有这样if判断就会一直调用下去