1.1.4 使用模板消除样式代码

你是否写过这样的代码,当编写的时候总会感觉以前曾经这么写过?我的朋友,这不是似曾相识。这是样板式的代码(boilerplate code)。通常为了实现通用的和简单的任务,你不得不一遍遍地重复编写这样的代码。

遗憾的是,它们中的很多是因为使用 Java API 而导致的样板式代码。样板式代码的一个常见范例是使用 JDBC 访问数据库查询数据。举个例子,如果你曾经用过 JDBC,那么你或许会写出类似下面的代码。

程序清单 1.12 许多 Java API,例如 JDBC,会涉及编写大量的样板式代码
public Employee getEmployeeById(long id) {
  
  Connection conn = null;
  PreparedStatement stmt = null;
  Result rs = null;
  
  try {
    conn = dataSource.getConnection();
    stmt = conn.prepareStatment(
      "select id, firstname, lastname, salary from " +
      "employee where id=?");
    stmt.setLong(1, id);
    rs = stmt.executeQuery();
    Employee employee = null;
    if (rs.next()) {
      employee = new Employee();
      employee.setId(rs.getLong("id"));
      employee.setFirstName(rs.getString("firstname"));
      employee.setLastName(rs.getString("lastname"));
      employee.setSalary(rs.getBigDecimal("salary"));
    }
    return employee;
  } catch (SQLException e) {
  } finally {
    if (rs != null) {
      try {
        rs.close();
      } catch (SQLException e) {
      }
    }
    
    if (stmt != null) {
      try {
        stmt.close();
      } catch (SQLException e) {
      }
    }
    
    if (conn != null) {
      try {
        conn.close();
      } catch (SQLException e) {
      }
    }    
  }
  return null;
}

正如你所看到的,这段 JDBC 代码查询数据库获得员工姓名和薪水。我打赌你很难把上面的代码逐行看完,这是因为少量查询员工的代码淹没在一堆 JDBC 的样板式代码中。首先你需要创建一个数据库连接,然后再创建一个语句对象,最后你才能进行查询。为了平息 JDBC 可能会出现的怒火,你必须捕捉 SQLException,这是一个检查型异常,即使它抛出后你也做不了太多事情。

最后,毕竟该说的也说了,该做的也做了,你不得不清理战场,关闭数据库连接、语句和结果集。同样为了平息 JDBC 可能会出现的怒火,你依然要捕捉 SQLException。

程序清单 1.12 中的代码和你实现其他JDBC操作时所写的代码几乎是相同的。只有少量的代码与查询员工逻辑有关系,其他的代码都是 JDBC 的样板代码。

JDBC 不是产生样板式代码的唯一场景。在许多编程场景中往往都会导致类似的样板式代码,JMS、JNDI 和使用 REST 服务通常也涉及大量的重复代码。

Spring 旨在通过模板封装来消除样板式代码。Spring 的 JdbcTemplate 使得执行数据库操作时,避免传统的 JDBC 样板代码成为了可能。

举个例子,使用 Spring 的 JdbcTemplate(利用了 Java 5 特性的 JdbcTemplate 实现)重写的 getEmployeeById() 方法仅仅关注于获取员工数据的核心逻辑,而不需要迎合 JDBC API 的需求。程序清单 1.13 展示了修订后的 getEmployeeById() 方法。

程序清单 1.13 模板能够让你的代码关注于自身的职责
public Employee getEmployeeById(long id) {
  return jdbcTemplate.queryForObject(
    "select id, firstname, lastname, salary " +
    "from employee where id=?",
    new RowMapper<Employee>() {
      public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
        Employee employee = new Employee();
        employee.setId(rs.getLong("id"));
        employee.setFirstName(rs.getString("firstname"));
        employee.setLastName(rs.getString("lastname"));
        employee.setSalary(rs.getBigDecimal("salary"));
        return employee;
      }
    }, 
    id);
}

正如你所看到的,新版本的 getEmployeeById() 简单多了,而且仅仅关注于从数据库中查询员工。模板的 queryForObject() 方法需要一个 SQL 查询语句,一个 RowMapper 对象(把数据映射为一个域对象),零个或多个查询参数。GetEmployeeById() 方法再也看不到以前的 JDBC 样板式代码了,它们全部被封装到了模板中。

我已经向你展示了 Spring 通过面向 POJO 编程、DI、切面和模板技术来简化 Java 开发中的复杂性。在这个过程中,我展示了在基于 XML 的配置文件中如何配置 bean 和切面,但这些文件是如何加载的呢?它们被加载到哪里去了?让我们再了解下 Spring 容器,这是应用中的所有 bean 所驻留的地方。

Last updated

Was this helpful?