动态代理中debug设置断点会执行invoke方法且args为null的原因

情景

写了个proxyExample,运行一看,竟然debug的结果跟run的结果竟然不一样,debug中会多次执行invoke方法,且并不调用sayHello方法

代码

public class ProxyExample implements InvocationHandler {
    private Object target;
    /**
     * 创建代理,将真实对象的类、构造方法等信息告诉代理类并将代理类返回,此时代理类拥有真实类的一切,
     * 甚至就可以直接看作是真实类的实例。
     * @param target
     * @return
     */
    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader() , target.getClass().getInterfaces() , this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //debug:加上方法名看看调用的是什么
        System.out.println("调度真实对象之前,方法名: " + method.getName() + args);
        Object obj = method.invoke(target , args);
        System.out.println("调度真实对象之后");
        return obj;
    }
    public static void main(String[] args) {
        ProxyExample example = new ProxyExample();
        HelloWorld proxy = (HelloWorld) example.bind(new HelloWorldImpl());
        proxy.sayHelloWorld();

    }
}

 

debug运行结果

调度真实对象之前,方法名: toStringnull
调度真实对象之后
调度真实对象之前,方法名: toStringnull
调度真实对象之后
调度真实对象之前,方法名: sayHelloWorldnull
调度真实对象之前,方法名: toStringnull
调度真实对象之后
[INFO ] 2019-10-09 20:52:26 [main] [javalearning.proxytest.HelloWorldImpl] javalearning.proxytest.HelloWorldImpl.sayHelloWorld(HelloWorldImpl.java:17): Hello World!
调度真实对象之前,方法名: toStringnull
调度真实对象之后
调度真实对象之后
调度真实对象之前,方法名: toStringnull
调度真实对象之后
调度真实对象之前,方法名: toStringnull
调度真实对象之后
Disconnected from the target VM, address: '127.0.0.1:55000', transport: 'socket'

Process finished with exit code 0

run运行结果

调度真实对象之前,方法名: sayHelloWorldnull
[INFO ] 2019-10-09 21:19:41 [main] [javalearning.proxytest.HelloWorldImpl] javalearning.proxytest.HelloWorldImpl.sayHelloWorld(HelloWorldImpl.java:17): Hello World!
调度真实对象之后
Process finished with exit code 0

发现端倪

发现我单步调试时,

1.出现tostring的次数跟暂停的次数成正比,单步调试越多,invoke打印越多;

2.run模式下就没那么多打印,仅有想要的sayHello方法结果

3.如果我把断点禁用掉(把断点红色变灰的那个按钮),那么禁用之后不再打印invoke的东西,仅有sayHello方法结果

 

这样基本就能确定,多次调用invoke必定跟打断点有关系

探索

所以看看调用的到底是啥咯,也就是上面代码中的这一行:

//debug加上方法名看看调用的是什么
System.out.println("调度真实对象之前,方法名: " + method.getName() + args);

可以看到打印结果,被调用的方法名是tostring,猜是idea中debug程序自带的问题,以下是引用内容,鸣谢lkforce:

断点处暂停时,IDEA会调用被代理类的toString()方法获取相关信息,鼠标悬停显示的好像就是那个东西。由于代理类代理该类的所有方法(包括toString),因此暂停一次就会输出一次“调用了toString()方法”的相关信息

多数情况下调用一下toString()方法没有什么问题,但是也有例外,比如重写了toString()方法的类,随意的调用toString()方法会导致未知的问题,比如Dubbo的AbstractConfig类,对这个类的debug会导致其子类ReferenceConfig的initialized属性错误的被修改为true,进而无法正确的生成Dubbo代理。

另外,IDEA在debug时调用toString()方法的情况是可以在配置中关掉的,配置位置是

Settings->Build,Execution,Development->Debugger->Java->Enable ‘toString()’ object view
 

动态代理中debug设置断点会执行invoke方法且args为null的原因_第1张图片

 

你可能感兴趣的:(javaee启航)