我们常使用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属性,如下:
那是哪里配置成了GBK呢?我们再在设置queryStringCharset属性的地方,即setQueryStringCharset方法加入断点,重启应用并重新发送请求(重启是为避免缓存导致断点触发不了),如下:
我们再往调用栈上面看,发现setQueryStringCharset的参数来自connector.getURICharset(),如下:
同理,我们在赋值connector.setURICharset的地方打上断点,重启应用,发现在重启的过程中就命中了断点,如下:
我们再往调用栈上面看,发现connector.setURICharset的参数来自this.getUriEncoding()方法,而this是TomcatServletWebServerFactory类型
同理,我们在TomcatServletWebServerFactory.setUriEncoding方法加上断点,重启应用,如下:
我们再往调用栈上面看,在to(Consumer<T> consumer)方法中发现uriEncoding来自this.supplier.get(),而this.supplier.get()取的应该就是ServerProperties$Tomcat对象中的urlEncoding属性
同理,我们在它的setter方法ServerProperties$Tomcat.setUriEncoding里面加上断点,重启应用,如下:
我们再往调用栈上面看,发现uriEncoding是从JavaBeanBinder.bind方法设置进来,从调用方法命名上不难看出,这个方法应该就是将配置文件中的值set到配置类属性中去的,当前被配置的类是ServerProperties$Tomcat,而正在配置的属性是uri-encoding,如下:
我们在此处瞄下各变量与参数的组成,很快就在BeanPropertyBinder参数中找到,uriEncoding来自application-web.yml文件的第4行第19列,如下:
果真,在application-web.yml中第4行,配置了uri-encoding: GBK,如下:
在将其改成UTF-8之后,发现乱码就不存在了,如下:
ok,通过这个例子,相信你已经体会到自底向上调试方法的诀窍了。
上面这个例子其实可以有更快处理方法,比如在当前工程全文搜索GBK,又或者google一下springboot,乱码之类的关键词,也能很快解决问题,但思路是很重要的,比如类似下面的场景,也能运用这里的方法: