您当前的位置:首页 > 计算机 > 编程开发 > 编程箴言

自底向上代码调试技巧

时间:01-26来源:作者:点击数:

简介#

我们常使用IDE的调试功能解决程序问题,但很多同学用的是自上而下调试法,即找到一个代码入口,打上断点然后单步调试。

但一些特殊的调试场景,比如调试框架代码,在不太熟悉框架代码的情况,会因为不知道从哪个入口开始调试而感到迷茫,这时可以试试本文介绍的方法。

例子#

比如,我们写了一个接口,在获取参数的时候乱码了如下:

乱码了

如何快速定位这个问题呢?

众所周知,乱码基本都是因为请求方与服务方字符集配置不一致产生的,比如请求方使用UTF-8,服务方使用GBK。

另外,既然上述接口中参数定义的是String类型,那么spring或tomcat框架中一定有个地方将网络请求中的字节流数据转换成这个String对象。

那么就可以这样,我们在String类的所有构造方法中加入条件断点,加断点后在断点处点鼠标右键即可添加条件,比如new String().contains("abcdefg"),然后请求这个接口时使用一个特殊的参数值abcdefg,这样当框架中new出包含abcdefg的String对象时,我们的条件断点就会命中,如下:

打条件断点

在String中添加条件断点后,我们重启应用,发如下请求来触发断点:

curl http://192.168.0.103:8080/test?data=abcdefg  

命中了条件断点,如下:

断点命中

我们往调用栈上面看,发现是handleQueryParameters中创建字符串时使用的GBK来new出String,而GBK来自queryStringCharset属性,如下:

handleQueryParameters

那是哪里配置成了GBK呢?我们再在设置queryStringCharset属性的地方,即setQueryStringCharset方法加入断点,重启应用并重新发送请求(重启是为避免缓存导致断点触发不了),如下:

setQueryStringCharset

我们再往调用栈上面看,发现setQueryStringCharset的参数来自connector.getURICharset(),如下:

setQueryStringCharset

同理,我们在赋值connector.setURICharset的地方打上断点,重启应用,发现在重启的过程中就命中了断点,如下:

setQueryStringCharset

我们再往调用栈上面看,发现connector.setURICharset的参数来自this.getUriEncoding()方法,而this是TomcatServletWebServerFactory类型

setQueryStringCharset

同理,我们在TomcatServletWebServerFactory.setUriEncoding方法加上断点,重启应用,如下:

setQueryStringCharset

我们再往调用栈上面看,在to(Consumer<T> consumer)方法中发现uriEncoding来自this.supplier.get(),而this.supplier.get()取的应该就是ServerProperties$Tomcat对象中的urlEncoding属性

setQueryStringCharset

同理,我们在它的setter方法ServerProperties$Tomcat.setUriEncoding里面加上断点,重启应用,如下:

setQueryStringCharset

我们再往调用栈上面看,发现uriEncoding是从JavaBeanBinder.bind方法设置进来,从调用方法命名上不难看出,这个方法应该就是将配置文件中的值set到配置类属性中去的,当前被配置的类是ServerProperties$Tomcat,而正在配置的属性是uri-encoding,如下:

setQueryStringCharset

我们在此处瞄下各变量与参数的组成,很快就在BeanPropertyBinder参数中找到,uriEncoding来自application-web.yml文件的第4行第19列,如下:

setQueryStringCharset

果真,在application-web.yml中第4行,配置了uri-encoding: GBK,如下:

setQueryStringCharset

在将其改成UTF-8之后,发现乱码就不存在了,如下:

setQueryStringCharset

ok,通过这个例子,相信你已经体会到自底向上调试方法的诀窍了。

总结#

上面这个例子其实可以有更快处理方法,比如在当前工程全文搜索GBK,又或者google一下springboot乱码之类的关键词,也能很快解决问题,但思路是很重要的,比如类似下面的场景,也能运用这里的方法:

  1. 系统运行时,不知道什么地方的代码老是执行了一条删除的sql,我们可以和上面一样,在String里面打上条件断点,条件是包含删除sql的部分片段。
  2. 系统启动时,不知道什么地方的代码监听了12345端口,我们可以在ServerSocket里面加上条件断点,条件是port==12345
  3. 系统运行时,不知道什么地方的代码老是写/tmp/app.log文件,我们可以在FileOutputStream.write方法加上条件断点,条件是File=="/tmp/app.log"
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门