3장 템플릿 - 3.4 컨텍스트와 DI

2 분 소요

https://www.yes24.com/Product/Goods/7516911

3장 템플릿

3.4 컨텍스트와 DI

3.4.1 jdbcContext 의 분리

전략 패턴의 구조로 보면

  • UserDao() 메소드 : 클라이언트
  • 익명 내부 클래스 : 개별적인 전략
  • jdbcContextWithStatementStrategy() 메소드 : 컨텍스트

이다.

jdbcContextWithStatementStrategy() 메소드를 다른 Dao 에서도 사용하기 위해 UserDao 클래스 밖으로 독립시켜보자.

JdbcContext 클래스를 만들고 workWithStatementStrategy() 를 생성한다.

public class JdbcContext {

  private DataSource dataSource;
  
  public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
  }

  public void workWithStatementStrategy(Statementstrategy stmt) throws SQLException {
    Connection c = null;
    PreparedStatement ps = null;
    
    try {
      c = this.dataSource.getConnection();
      ps = stmt.makePreparedStatement(c);
      ps.executeUpdate();
    } catch (SQLException e) {
      throw e; 
    } finally {
      if (ps != null) { try { ps.close(); } catch (SQLException e) {} } 
      if (c != null) { try {c.close(); } catch (SQLException e) {} }
    }
  }
  
}

UserDao 는 JdbcContext 를 주입받는다.

public class UserDao {
  
  // highlight-start
  private JdbcContext jdbcContext;
  
  public void setJdbcContext(JdbcContext jdbcContext) {
    this. jdbcContext = jdbcContext;
  }
  // highlight-end
  
  public void add(final User user) throws SQLException {
    // highlight-next-line
    this.jdbcContext.workWithStatementStrategy(
      new Statementstrategy() {
        ...
      }
    );
  }
  
  public void deleteAll() throws SQLException {
    // highlight-next-line
    this.jdbcContext.workWithStatementStrategy(
      new StatementStrategy() {
        ...
      }
    );
  }
  
}

JdbcContext 는 그 자체로 독립적인 JDBC 컨텍스트를 제공해주는 서비스 오브젝트이기 때문에 인터페이스를 구현하도록 만들지 않았다는 것을 확인한다.

3.4.2 JdbcContext 의 특별한 DI

UserDao 는 인터페이스를 거치치 않고 JdbcContext 를 사용하고 있어서 구현 클래스를 변경할 수 없다.

의존관계 주입 DI 개념을 충실히 따르자면 인터페이스를 둬서 클래스 레벨의 의존관계를 피하고, 런타임 시에 의존할 오브젝트와의 관계를 다이나믹하게 주입해주는 것이 맞다.

하지만 스프링의 DI 를 넓게 보자면 제어권한을 외부로 위임했다는 IoC 개념을 포괄하고 있기 때문에 JdbcContext 를 스프링을 이용해서 UserDao 에 주입했다는 건 DI 의 기본을 따르고 있다고 봐야 한다.

JdbcContext 는 스프링 컨테이너의 싱글톤 레지스트리에서 관리되는 싱글톤 빈이다.

따라서 여러 오브젝트에서 공유해 사용이 가능하다.

JdbcContext 가 DI 를 통해 DataSource 에 의존하고 있다.

DI 를 위해서는 주입되는 오브젝트, 주입받는 오브젝트 양쪽 모두 스프링 빈으로 등록되어야 한다.

JdbcContext 와 UserDao 처럼 매우 긴밀한 관계를 가지고 강하게 결합되어 있다면 JdbcContext 에 대한 DI 필요성을 위해 스프링의 빈으로 등록해서 UserDao 에 DI 되도록 만들어도 좋다.

UserDao 내부에서 직접 DI 를 적용할 수도 있다.

다만 JdbcContext 를 싱글톤으로 만들려는 것은 포기해야 된다.

대신 DAO 마다 하나의 JdbcContext 를 두게 해야 한다.

스프링 컨테이너 대신 JdbcContext 의 생성과 초기화를 UserDao 가 맡는다.

JdbcContext 가 주입받아야 하는 DataSource 는 UserDao 가 대신 주입받는다.

JdbcContext 는 이제 빈이 아니라 주입받을 수 없기 때문이다.

public class UserDao {

  private JdbcContext jdbcContext;
  
  public void setDataSource(DataSource dataSource) {
    this.jdbcContext=newJdbcContext();
    // highlight-next-line
    this.jdbcContext.setDataSource(dataSource);
    this.dataSource = dataSource;
  }
  
}

수동으로 DI 를 하는 방법으로 굳이 인터페이스를 두지 않아도 될 만큼 긴밀한 DAO 와 JdbcContext 라면 내부에서 직접 만들어 사용하면서도 다른 오브젝트에 대한 DI 를 적용할 수 있다.