トビーのスプリング[3.4章-3章終了]学習


3.4. コンテキストとDI


3.4.1. JdbcContextの分離


戦略モデル構造では、UserDaoの方法はクライアントの概念から構成されています.
UserDaoメソッド内で匿名の内部クラスを作成するのは個別のポリシーであり、jdbcContextWithStatementStrategy()メソッドはコンテキストです.

に質問

- 컨텍스트 메소드는 UserDao내에서만 동작할 수 있다.
- 다른 DAO에서도 범용성이 있는 로직 흐름이므로 분리할 필요가 있다.

クラスの分離


JdbcContext Class
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) {} }
        }
    }
}
JDbcContextにDIを受信して使用させるUserDao
public class UserDao {
	...
	private JdbcContext jdbcContext;
	public void setJdbcContext(JdbcContext jdbcContext) {
		this.jdbcContext = jdbcContext;
	}

	public void add(final User user) throws SQLException {
		this.jdbcContext.workWithStatementStrategy(
			() -> {...}
		);
	}

	public void deleteAll() throws SQLException {
		this.jdbcContext.workWithStatementStrategy(
			() -> {...}
		);
	}
}

空の依存関係の変更


UserDaoはJdbcContextに依存しています.
ただしJdbcContextはインタフェースDataSourceとは異なり、具体的なクラスである.
スプリングのDIは基本的にインタフェースを介して依存クラスを交換して使用することを目的としているが,その動作方式は一般関係とは異なる.
この場合、JDbcContext自体は独立したJDBCコンテキストを提供するサービスオブジェクトであり、意味があり、実装方法を変更することは不可能であるため、例外的にインタフェースを実装しない.

スプリングの空の設定は、クラスレベルではなく、実行時に作成されたオブジェクトレベルの依存関係に基づいて定義されます.

3.4.2. JdbcContextにおける特殊DI


使用ばね片DI


実際、依存関係を注入する概念は、インタフェースをクラス間に配置し、依存関係をクラスレベルで固定しないことである.実行時に依存オブジェクトとの関係を動的に注入することは正しいが,ばねのDIはより広い観点から,オブジェクトの作成と関係設定の制御権制限をオブジェクトから削除して外部に委任するIoCの概念を含んでいるため,JdbcContextはばねを用いてDIの基本に従うと考えられる.
なぜJdbcContextはインタフェースを使用しない構造のためにUserDaoとDI構造を作成しなければならないのか
-JdbcContextはスプリング容器の単トンレジストリで管理される単トンVINとなるためである.
-JdbcContextはDIを介して他の空に依存するため.82 JdbcContextデータソースプロジェクトを通じてデータソースオブジェクトの注入を受ける.DIを行うためには,注入されたオブジェクトと注入されたオブジェクトの両方をスプリング空孔として登録しなければならない.

コードを使用した手動DI


JdbcContextをSpringBinとして登録し、ユーザーDaoに使用するのではなく、UserDao内部にDIを直接適用する方法があります.
逆に、JdbcContextを単一の色調に変えることを放棄しなければなりません.
public class UserDao {
	...
	private JdbcContext jdbcContext;
	public void setDataSource(DataSource dataSource) {
		this.jdbcContext = new JdbcContext();

		this.jdbcContext.setDataSource(dataSource);
	}
}

3.5. テンプレートとコールバック


一定のモードを持つワークフローが存在し、その一部を頻繁に置き換える必要がある場合、ポリシー・モードのコンテキストはテンプレートと呼ばれ、匿名の内部クラスとして作成されたオブジェクトはコールバックと呼ばれます.
かたわく
...プリセット形状のフレームがあります.
テンプレートメソッドモードは、固定フレームワークロジックを持つテンプレートメソッドをスーパークラスに配置し、変更部分をサブクラスメソッドに配置する構造です.
ダイヤルバック
実行を目的として他のオブジェクトに渡すメソッドのオブジェクト.
パラメータを参照するのではなく、特定の論理を含むメソッドを実行します.
Javaではメソッド自体をパラメータとして渡すことができないため,メソッドを含むオブジェクトを渡す必要がある.したがって、機能オブジェクト(functional object)とも呼ばれる.

3.5.1. テンプレート/コールバックの運動原理


テンプレート/コールバック機能


テンプレートのワークフローでは、通常、特定の機能に対して1回呼び出されるため、テンプレート/コールバックモードのコールバックには単一のメソッドインタフェースが使用されます.
- 클라이언트의 역할은 템플릿 안에서 실행될 로직을 담은 콜백 오브젝트를 만들고, 콜백이 참조할 정보를 제공하는 것. 만들어진 콜백은 클라이언트가 템플릿의 메소드를 호출할 때 파라미터로 전달된다.
- 템플릿은 정해진 작업 흐름을 따라 작업을 진행하다가 내부에서 생성한 참조정보를 가지고 콜백 오브젝트의 메소드를 호출한다. 콜백은 클라이언트 메소드에 있는 정보와 템플릿이 제공한 참조정보를 이용해서 작업을 수행하고 그 결과를 다시 템플릿에 돌려준다.
- 템플릿은 콜백이 돌려준 정보를 사용해서 작업을 마저 수행한다. 경우에 따라 최종 결과를 클라이언트에 다시 돌려주기도 한다.

3.5.2. 便利なコールバックを回収


コールバックの分離と回収


すべての()メソッドを削除して不変の部分を分離
public void deleteAll() throws SQLException {
	executeSql("delete from users");
}

public void executeSql(final String query) throws SQLException {
	this.jdbcContext.workWithStatementStrategy(
		new StatementStrategy() {
			public PreparedStatement makePreparedStatement(Connection c) throws SQLException  { return c.prepareStatement(query); }
	);
}

コールバックとテンプレートのマージ


JdbcContextに移行するexecutesql()メソッド
public class JdbcContext {
	...
	public void executeSql(final String query) throws SQLException {
		workWithStatementStrategy(
			new StatementStrategy() { public PreparedStatement makePreparedStatement(Connection c) throws SQLException { return c.prepareStatement(query); }
		}
	);
}
JdbcContextに移行したexecutesql()を使用したdeleteAll()メソッド
public void deleteAll() throws SQLException {
	this.jdbcContext.executeSql("delete from users");
}

3.5.3. テンプレート/コールバックの適用


テンプレート/コールバックの概念をさらに学習するために,簡単な例で理解した.
要求
-プログラムを作成し、ファイルを開き、すべての行の数値の合計を返します.
numbers.txt
1
2
3
4
計算機コンテキスト
public class Calculator {
    ...
    public Integer calcLineSum(String filepath) throws IOException {
        LineCallback<Integer> sumCallback =
                (line, value) -> {
                    return value + Integer.parseInt(line);
                };

        return lineReadTemplate(filepath, sumCallback, 0);
    }

    public Integer calcLineMultiply(String filepath) throws IOException {
        LineCallback<Integer> multiplyCallback =
                (line, value) -> {
                    return value * Integer.parseInt(line);
                };

        return lineReadTemplate(filepath, multiplyCallback, 1);
    }

    public String concatenate(String filepath) throws IOException {
        LineCallback<String> concatenateCallback =
                (line, value) -> {
                    return value + line;
                };

        return lineReadTemplate(filepath, concatenateCallback, "");
    }

    public <T> T lineReadTemplate(String filepath, LineCallback<T> callback, T initVal) throws IOException {
        BufferedReader br = null;

        try {
            br = new BufferedReader(new FileReader(filepath));
            T res = initVal;
            String line = null;
            while ((line = br.readLine()) != null) {
                res = callback.doSomethingWithLine(line, res);
            }
            return res;
        } catch (IOException e) { throw e; }
        finally {
            if (br != null) {
                try { br.close(); }
                catch (IOException e) { System.out.println(e.getMessage()); }
            }
        }
    }
}
CallbackStrategy
public interface LineCallback<T> {
    T doSomethingWithLine(String line, T value);
}

public interface BufferedReaderCallback {
    Integer doSomethingWithReader(BufferedReader br) throws IOException;
}
テストコード
import static org.hamcrest.MatcherAssert.*;

import org.hamcrest.CoreMatchers;
import org.hamcrest.core.Is;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

public class CalcSumTest {bfggbv
    Calculator calculator;
    String numFilepath;

    @BeforeEach
   public void setUp() {
        this.calculator = new Calculator();
        this.numFilepath = getClass().getResource("/numbers.txt").getPath();
    }
    @Test
    public void sumOfNumbers() throws IOException {
        Calculator calculator = new Calculator();
        int sum = calculator.calcSum(this.numFilepath);

        assertThat(sum, Is.is(10));

        assertThat(calculator.calcSum(this.numFilepath), CoreMatchers.is(10));

        assertThat(calculator.calcMultiply(this.numFilepath), CoreMatchers.is(24));
    }
}

3.6. スプリングのJdbcTemplate


JdbcTemplateとは?


Springが提供するJDBCコードの基本テンプレート.
JDBCのDAOを使用するために、さまざまなテンプレートとコールバックが用意されています.

3.6.1. update()


Insert/update/delete用のテンプレートメソッド.
deleteAll()メソッド
public void deleteAll() {
    this.jdbcTemplate.update("delete from users");
}
add()メソッド
public void add(final User user) throws SQLException {
    this.jdbcTemplate.update("insert into user(id, name, password) values(?, ?, ?)"
            , user.getId(), user.getName(), user.getPassword());
}

3.6.2. queryForObject()

public int getCount() {
        return this.jdbcTemplate.queryForObject("select count(*) from users", Integer.class);
    }

query()テンプレートを使用してgetAll()を実装する

private RowMapper<User> userRowMapper = (rs, rowNum) -> {
    User user = new User();

    user.setId(rs.getString(1));
    user.setName(rs.getString(2));
    user.setPassword(rs.getString(3));
    return user;
};


public List<User> getAll() {
    return this.jdbcTemplate.query("select * from users", userRowMapper);
}