情景
写了个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
https://blog.csdn.net/q2878948/article/details/102491389