已在finally块关闭资源仍触发SonarLint阻塞错误,求优化方案
我们已经在finally代码块中手动关闭了Statement、Connection等资源,但SonarLint仍报出两个阻塞级错误:
Use try-with-resources or close this "Statement" in a "finally" clause.Use try-with-resources or close this "Connection" in a "finally" clause.
原示例代码如下:
public String getProductNumber() throws BusinessDelegateException { String productNo = null; Connection con = null; Statement stmt = null; ResultSet rs = null; String query = //some query try { DataSource ds = getDataSource(); con = ds.getConnection(); stmt = con.createStatement(); rs = stmt.executeQuery(query); productNo =....... .................... }catch (Exception e) { String errorMsg = "Error occured in getProductNumber()"; throw new BusinessDelegateException(errorMsg, e); }finally{ try { if(rs != null) rs.close(); if (stmt != null) stmt.close(); if (con != null) con.close(); } catch (SQLException e) { e.printStackTrace(); } } return productNo; }
目前我们通过将finally块拆分为多个独立的try-catch块解决了该问题,但存在代码重复的问题:
finally{ try { if(rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt != null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (con != null) con.close(); } catch (SQLException e) { e.printStackTrace(); } }
下面给你两个更优雅的修复方案:
方案1:使用Java 7+的try-with-resources语法(最优解)
这是Java官方推荐的资源管理方式,它会自动实现资源的关闭(无需手动写finally),能保证每个资源都被正确关闭,完全符合SonarLint的检查标准,代码也更简洁。
改写后的代码如下:
public String getProductNumber() throws BusinessDelegateException { String productNo = null; String query = //some query try (DataSource ds = getDataSource(); Connection con = ds.getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(query)) { productNo =....... .................... } catch (Exception e) { String errorMsg = "Error occured in getProductNumber()"; throw new BusinessDelegateException(errorMsg, e); } return productNo; }
注意:
DataSource、Connection、Statement、ResultSet都实现了AutoCloseable接口,所以可以直接放在try-with-resources的括号里,JVM会在代码块执行完毕后自动按逆序关闭这些资源,就算中间出现异常也不会漏掉。
方案2:封装资源关闭工具方法(兼容Java 6及以下版本)
如果你的项目还在使用Java 7之前的版本,没法用try-with-resources,可以写一个通用的工具方法来封装资源关闭的逻辑,避免代码重复,同时让SonarLint识别到资源被正确处理。
先写一个工具类:
public class DBResourceUtils { public static void closeQuietly(AutoCloseable resource) { if (resource != null) { try { resource.close(); } catch (Exception e) { // 这里可以根据需要记录日志,或者直接忽略 e.printStackTrace(); } } } }
然后在finally块里调用这个方法:
finally{ DBResourceUtils.closeQuietly(rs); DBResourceUtils.closeQuietly(stmt); DBResourceUtils.closeQuietly(con); }
这种方式既解决了代码重复的问题,也能让SonarLint认可资源的关闭逻辑——因为每个资源的关闭都在独立的异常处理逻辑中,不会因为前一个资源关闭失败而导致后续资源无法关闭。
为什么原代码会被SonarLint报错?
SonarLint之所以报错,是因为原finally块中多个资源的关闭在同一个try-catch里:如果rs.close()抛出了SQLException,那么stmt.close()和con.close()就不会被执行,存在资源泄漏的风险。拆分多个try-catch虽然解决了这个问题,但代码冗余。而上面的两种方案都能从根本上避免这个风险,同时让代码更整洁。
内容的提问来源于stack exchange,提问作者vishnu

