diff --git "a/\354\230\244\354\210\230\354\247\204/Vol2/1\354\236\245_IoC\354\273\250\355\205\214\354\235\264\353\204\210\354\231\200_DI/1.5 \354\212\244\355\224\204\353\247\201 3.1\354\235\230 IoC \354\273\250\355\205\214\354\235\264\353\204\210\354\231\200 DI.md" "b/\354\230\244\354\210\230\354\247\204/Vol2/1\354\236\245_IoC\354\273\250\355\205\214\354\235\264\353\204\210\354\231\200_DI/1.5 \354\212\244\355\224\204\353\247\201 3.1\354\235\230 IoC \354\273\250\355\205\214\354\235\264\353\204\210\354\231\200 DI.md" index 3a57824..59be437 100644 --- "a/\354\230\244\354\210\230\354\247\204/Vol2/1\354\236\245_IoC\354\273\250\355\205\214\354\235\264\353\204\210\354\231\200_DI/1.5 \354\212\244\355\224\204\353\247\201 3.1\354\235\230 IoC \354\273\250\355\205\214\354\235\264\353\204\210\354\231\200 DI.md" +++ "b/\354\230\244\354\210\230\354\247\204/Vol2/1\354\236\245_IoC\354\273\250\355\205\214\354\235\264\353\204\210\354\231\200_DI/1.5 \354\212\244\355\224\204\353\247\201 3.1\354\235\230 IoC \354\273\250\355\205\214\354\235\264\353\204\210\354\231\200 DI.md" @@ -140,6 +140,7 @@ public class DataConfig { - 해결 방법은? #### 빈 설정파일의 변경 + - 일부 빈만 바뀌어도 다른 설정 파일 필요 - 실수 가능 - 바람직하지 않음 @@ -210,7 +211,7 @@ spring.datasource.username=${DB_USERNAME:default} ### 프로퍼티 소스의 사용 - @Value 사용 (가장 일반적) ```java -@Component +@Co66mponent public class MyService { @Value("${app.name}") private String appName; diff --git "a/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.1 \352\263\265\355\206\265 \352\260\234\353\205\220.md" "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.1 \352\263\265\355\206\265 \352\260\234\353\205\220.md" new file mode 100644 index 0000000..1becd9c --- /dev/null +++ "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.1 \352\263\265\355\206\265 \352\260\234\353\205\220.md" @@ -0,0 +1,96 @@ +2.1 공통 개념 += +- 모든 기술에 공통적으로 적용되는 원칙과 기본 개념 +## DAO 패턴 +- DTO 또는 도메인 오브젝트만을 사용하는 인터페이스를 통해 데이터 엑세스 기술을 외부에 노출하지 않도록 만드는 것 +- 코드에 영향을 주지 않고 데이터 접근 기술 변경 & 하나 이상의 기술 사용을 가능하게 해줌 +- 순수한 POJO로 개발할 수 있도록 해줌 +- 목 오브젝트를 사용하여 단위 테스트 가능 + +### DAO 인터페이스와 DI +- 인터페이스를 이용해 접근하고 DI 되도록 만들어야 +- 인터페이스를 만들 때, + - 구체적 기술 노출 X + - 모든 public 메소드를 추가하는 습관 X -> Service 계층에서 의미 있는 메소드만 공개해야 + - 특정 기술에만 의미있는 이름 X (ex. JPA의 persist(), merge() 등) + +### 예외처리 +- 데이터 접근 중 발생한 에러는 복구 불가능하기 때문에 런타임 예외여야 +- 또한, DAO 메소드 선언부에 + - `throws SQLException` 같은 기술을 노출하는 예외처리 x + - `throws Exception` 같은 무책임한 예외처리도 x +- DAO가 던지는 대부분의 예외는 서비스 계층 코드가 다뤄야 할 이유가 없는 예외 +- but, 때로는 의미 있는 예외를 잡아서 로직에 적용하는 경우가 있음 (ex. 중복키 예외, 낙관적인 락킹 등) +- 이때, 기술마다 던지는 예외에 일관성이 없기 때문에 서비스 계층에서 이를 알고 있어야 한다는 문제 발생 +- 스프링은 이를 위해 예외 추상화 제공하여 각 기술의 예외를 스프링의 데이터 예외로 변환해주는 변환 서비스를 제공 +- 데이터 기술의 API를 직접 사용할 때는 AOP를 이용해 예외를 전환해주는 기능을 사용 +- 최신 데이터 접근 기술을 JDBC와 다르게 런타임 예외 사용 + +## 2.1.2 템플릿과 API +- 데이터 접근 기술은 대부분 try/catch/finally & 반복되는 코드 +- 외부 리소스와 연동 과정에서 발생하는 예외에서도 리소스 반환코드 필요하기 때문 +- 스프링은 템플릿/콜백 패턴을 이용해 템플릿을 제공 -> 반복 코드 제거, 예외 변환, 트랜잭션 동기화 +- 단점 + - 템플릿이 제공하는 API 사용해야 함 (특정 데이터 접근 기술이 제공하는 고유한 기능이나 확장 API를 사용하려면 템플릿이 이를 지원하지 않을 수 있음) + - 콜백 오브젝트를 익명 내부 클래스로 작성해야 하는 경우 -> 가독성↓ +- 따라서, 스프링은 일부 데이터 접근 기술을 API 그대로 사용할 수 있도록 제공 +- 데이터 접근 기술이 제공하는 확장 기법과 API 등을 이용해 예외 변환과 트랜잭션 동기화 제공 가능하기 때문 + +## 2.1.3 DataSource +- Connection은 모든 데이터 접근 기술에서 사용되는 필수 리소스임 +- 사용자의 요청이 빈번하게 일어나는 엔터프라이즈 환경에서 각 요청마다 커넥션을 매번 새롭게 만드는 것은 비효율적이며 성능하락 +- 커넥션을 pool에 미리 준비해두고, 할당하는 풀링 기법 사용 +- 스프링은 DataSource를 빈으로 등록하고 관리하기 위해 데이터 접근 기술이 자체적인 생성 방식을 사용하는 대신 스프링 빈으로 등록된 DataSource를 사용하는데 필요한 방법을 제공한다. + +### 스프링에서 사용하는 DataSource의 종류 +### 학습 테스트와 통합 테스트를 위한 DataSource +- 순차적으로 진행하는 통합 테스트나 단순한 학습 테스트 +- 스프링이 제공하는 단순한 DataSource 사용 +- but 운영환경에서는 절대 사용 x + - 설정파일을 환경별로 따로 만들어 사용 권장 +#### 1. SimpleDriverDataSource +- 가장 단순한 DataSource +- 풀 관리 x, 매번 생성 +#### 2. SingleConnectionDataSource +- 하나의 물리적 Connection을 계속 사용 +- 순차적, 독립적 o / 멀티 스레드 x +- 매번 생성 x -> SimpleDriverDataSource 보다 빠름 + +### 오픈소스 또는 사용 DB 커넥션 풀 +- 서버의 DB 풀로 등록해서 사용 가능하지만 일반적으로 애플리케이션 레벨에서 애플리케이션 전용 DB 풀을 만들어 사용 +- 두 오픈소스 DBCP 모두 스프링 빈으로 바로 등록해서 사용 가능한 수정자 메소드를 가진 클래스가 제공되어 사용이 편리 +- 다양한 풀 클래스와 설정 옵션 제공 +#### 1-1. 아파치 Commons DBCP +- 가장 유명 +#### 1-2. c3p0 JDBC/DataSource Resource Pool +- JDBC 3.0 스펙을 준수하는 Connection과 Statement 풀을 제공하는 라이브러리 +#### 2. 상용 DB 커넥션 풀 +- 일부 상용 DB는 자체적으로 커넥션 풀 라이브러리를 제공 + +### JDNI/WAS DB 풀 +- 대부분의 자바 서버는 자체적으로 DB 풀 서비스 제공 +- 서버가 제공하는 DB풀을 사용 하는 경우 JNDI를 사용 +- 주의: JNDI로 가져오는 빈 오브젝트는 서버 밖에서는 제대로 동작 x -> 스프링 테스트 프레임워크는 JNDI 목 오브젝트를 제공 (`SimpleNamingContextBuilder`) + + + +## JNDI(Java Naming and Directory Interface) +- Java에서 네트워크나 디렉토리 서비스와 같은 리소스에 접근하기 위한 표준 API +- JNDI는 복잡한 리소스 연결을 쉽게 해주기 때문에, 주로 애플리케이션 서버에서 데이터베이스, 메시지 큐, 원격 서비스 등의 리소스를 관리하고 연결할 때 사용 + + +### JNDI는 무엇을 하는가? +- 데이터베이스 연결, 메시징 서비스, EJB(Enterprise Java Beans) 등과 같은 리소스의 이름(Name)을 통해 애플리케이션이 해당 리소스를 찾을 수 있도록 도와 줌 +- 리소스를 추상화하여 사용자가 직접 리소스를 관리하거나 설정하지 않도록 도와 줌 + +### 왜 사용하는가? +- 리소스를 코드에 직접 하드코딩하지 않기 위해. +- 환경(개발, 테스트, 운영)에 따라 리소스 설정이 다를 수 있는데, JNDI를 사용하면 설정을 중앙화하고 관리하기 쉬워짐 + +### JNDI 작동 방식 +#### 1. 리소스 등록 +- 애플리케이션 서버(예: Tomcat, WildFly)는 데이터베이스 커넥션 풀, JMS 큐, 이메일 세션 등 다양한 리소스를 JNDI 네이밍 서비스에 등록 +- 각 리소스는 고유한 이름(예: java:/comp/env/jdbc/myDataSource)으로 식별 +#### 2. 리소스 검색 +- 애플리케이션은 JNDI를 통해 이름으로 리소스를 조회 +- JNDI는 이 요청을 처리하여 리소스 객체를 반환 \ No newline at end of file diff --git "a/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.2 JDBC.md" "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.2 JDBC.md" new file mode 100644 index 0000000..3a674ce --- /dev/null +++ "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.2 JDBC.md" @@ -0,0 +1,258 @@ +2.2 JDBC += +- 자바의 데이터 액세스 기술의 기본이 되는 로우레벨의 API +- JDBC는 인터페이스를 제공 -> 각 DB 벤더에서 구현 드라이버 제공 +- 모든 데이터 접근 기술의 근간 +- 장점 + - 호환성 + - 단순성 + - 안정적이고 유연함 +- 단점 + - 복잡한 코드 + - 일관성 없는 정보를 가진 체크 예외 처리 필요 + - SQL 문자로 제공 + - 릴리즈 문제 +- 스프링은 장점을 가져가면서 단점을 극복하는 JDBC API 제공 + +## 2.2.1 스프링 JDBC 기술과 동작원리 +### 스프링의 JDBC 접근 방법 +- `SimpleJdbcTemplate` + - 방대한 템플릿 메소드와 내장 콜백 제공 + - JDBC의 모든 기능 최대한 활용 +- `SimpleJdbcInsert, SimpleJdbcCall` + - DB가 제공해주는 메타정보를 이용하여 최소한의 코드로 단순한 JDBC 코드 작성을 도와 줌 + - 컬럼 정보와 파라미터 정보를 가져와서 Insert문과 프로시저 호출 작업에 사용 +- `*deprecated 됐다고 함` + + +### 스프링 JDBC가 해주는 작업 +- 다음의 작업을 스프링 JDBC가 제공하는 오브젝트에 맡길 수 있음 +- 반복 작업, 실수, 에러 등 +- 개발자는 데이터 액세스 로직에 따라 달라지는 부분만 정의해주면 됨 + - SQL & 바인딩할 파라미터 & 쿼리 실행 결과를 받을 오브젝트 등 + - DataSource를 정의 -> dataSource라는 이름의 빈으로 정의 + +#### 1. Connection 열기와 닫기 +- 예외 발생 시 리소스 릴리즈 +- 닫는 시점은 스프링 트랜잭션 기능과 맞물려서 결정 + +#### 2. Statement 준비와 닫기 +- 개발자가 파라미터 바인딩에 사용할 정보가 담긴 맵이나 오브젝트를 전달 + +#### 3. Statement 실행 + +#### 4. ResultSet 루프 +- 쿼리 결과가 한 건 이상이면 원래는 루프 -> 각 로우 처리해줘야 함 +- 루프 내에서 처리될 내용은 콜백으로 제공해야 함 + +#### 5. 예외처리와 변환 +- JDBC 작업 중 발생하는 모든 예외 처리 +- 체크 예외를 런타임 예외로 전환 +- 특정 기술 종속 예외를 추상적 예외로 전환 (SQLException -> DataAccessException) + +#### 6. 트랜잭션 처리 +- 트랜잭션 동기화 기법을 사용하여 선언적 트랜잭션 기능과 맞물려서 돌아감 +- 트랜잭션 시작 후에 작업 요청 시, 진행 중인 트랜잭션에 참여 / 없으면 새로 시작 +- 스프링 JDBC 사용하면 트랜잭션 작업 신경쓰지 않아도 됨 -> 스프링이 알아서 트랜잭션 선언에 따라 처리 + +## 2.2.2 SimpleJdbcTemplate +- 가장 많이 이용하는 JDBC 템플릿 +- JdbcTemplate 을 더 편리하게 사용할 수 있도록 기능을 향상 시킨 것 +- 실행, 조회, 배치의 세 가지 작업으로 구분 (실행 : INSERT, UPDATE / 조회 : SELECT / 배치 : 하나 이상의 작업을 한 번에 수행) + +### SimpleJdbcTemplate 생성 +```java +SimpleJdbcTemplate template = new SimpleJdbcTemplate(dataSource); +``` +- SimpleJdbcTemplate는 멀티스레드 환경에서도 안전하게 공유 가능 (상태 변경 x, 공유 자원 문제 x) + - DAO의 인스턴스 변수에 저장하고 사용 가능 + - 싱글톤 빈으로 등록해서 공유 가능 +- 관계적으로 DAO에서 생성 방식 권장 -> 다른 방식의 JDBC 오브젝트도 만들어 사용 가능하기 때문 + +```java +public class MemberDao { + SimpleJdbcTemplate simpleJdbcTemplate; + + public void setDataSource(DataSource dataSource) { + this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); + } +} +``` + +### SQL 파라미터 +- SimpleJdbcTemplate에 작업을 요청할 때는 SQL을 제공해야 +- 위치 치환자 '?' 지원 +- 이름 치환자 지원 + +```sql +# 원 SQL 문 +INSERT INTO MEMBER(ID, NAME, POINT) VALUES (1, "Spring", 2.3); + +# 위치 치환자 +INSERT INTO MEMBER(ID, NAME, POINT) VALUES (?, ?, ?); + +# 이름 치환자 +INSERT INTO MEMBER(ID, NAME, POINT) VALUES (:id, :name, :point); +``` + +### Map/MapSqlParameterSource + +```java +public static void main(String[] args) { + Map map = new HashMap(); + map.put("id", 1); + map.put("name", "Spring"); + map.put("point", 2.3); +} +``` +- Map을 바인딩 파라미터로 사용하기 + +```java +MapSqlParameterSource params = new MapSqlParameterSource() + .addValue("id", 1) + .addValue("name", "Spring") + .addValue("point", 3.5); +``` +- 체인 형식으로 이어서 작성 가능 + +#### BeanPropertySqlParameterSource +- 맵 대신 도메인 오브젝트나 DTO 사용 가능 +- 프로퍼티 이름과 이름 치환자 매핑 + +```java +public class Member { + int id; + String name; + double point; + + public Member(int id, String name, double point) { + this.id = id; + this.name = name; + this.point = point; + } +} + +public static void main(String[] args) { + Member member = new Member(1, "Spring", 3.5); + BeanPropertySqlParameterSource params = new BeanPropertySqlParameterSource(member); +} +``` + +### SQL 실행 메소드 +- update() 메소드 실행 +- 실행 시 바인딩 할 파라미터 +#### varargs +- 위치 치환자 사용 시 팔라미터 순서대로 전달 +```java +public static void main(String[] args) { + simpleJdbcTemplate.update( + "INSERT INTO MEMBER(ID, NAME, POINT, args) VALUES (?,?,?)", 1, "Spring", 3.5); + + // 필요 없으면 생략 가능 + simpleJdbcTemplate.delete( + "DELETE FROM MEMBER"); +} +``` + +#### Map +- 이름 치환자 사용 시 파라미터를 Map으로 전달 +```java +public static void main(String[] args) { + simpleJdbcTemplate.update( + "INSERT INTO MEMBER(ID, NAME, POINT, args) VALUES (:id, :name, :point)", map); + +} +``` + +#### SqlParameterSource +- 도메인 오브젝트나 DTO 사용 +```java +public static void main(String[] args) { + simpleJdbcTemplate.update( + "INSERT INTO MEMBER(ID, NAME, POINT, args) VALUES (:id, :name, :point)", + new BeanPropertySqlParameterSource(member)); + +} +``` + +### SQL 조회 메소드 +- 값이나 오브젝트 / 리스트 +#### 1. int queryForInt(String sql, [SQL 파라미터]) : 하나의 int 값 조회 시 사용 +#### 2. long queryForLong(String sql, [SQL 파라미터]) : 하나의 long 값 조회 시 사용 +#### 3. T queryForObject(String sql, Class requiredType, [SQL 파라미터]) : 하나의 값(단일 칼럼) 조회 시 사용 +- 결과가 여러개면 예외 발생 +- 조회되는 로우가 없다면 EmptyResultDataAccessException 발생 + +#### 4. T queryForObject(String sql, RowMapper rm, [SQL 파라미터]) : 하나의 로우(다중 칼럼) 조회 시 사용 +- RowMapper에 BeanPropertyRowMapper 사용하는 것이 편리 + - 생성자에 매핑할 클래스 넣어서 생성하면 RowMapper 콜백 오브젝트로 사용 가능 +```java +Member m = simpleJdbcTemplate.queryForObject("select * from member where id = ?", + new BeanPropertyRowMapper(Member.class)); +``` + +#### 5. List query(String sql, RowMapper rm, [SQL 파라미터]) : 하나 or 여러 개의 로우(다중 칼럼) 조회 시 사용 +#### 6. Map queryForMap(String sql, [SQL 파라미터]) : 하나의 로우 (다중 칼럼) 조회 시 사용, Map에 매핑 +#### 7. List> queryForList(String sql, [SQL 파라미터]) : 하나 or 여러 개의 로우(다중 칼럼) 조회 시 사용 +- queryForMap의 다중 로우 버전 + +### SQL 배치 메소드 +- update()로 실행하는 SQL 들을 배치 모드로 실행하게 해준다. +- 내부적으로 JDBC Statement의 addBatch(), executeBatch() 메소드 이용 +- DB 호출 최소화 -> 성능 향상 +- 동일한 SQL을 파라미터만 바꾸면서 실행하는 경우에 사용 + +#### 1. int[] batchUpdate(String sql, Map[] batchValues) +- 이름 치환자 & 파라미터 정보가 담긴 맵 배열 이용 + +#### 2. int[] batchUpdate(String sql, SqlParameterSource[] batchArgs) +- SqlParameterSource 타입 오브젝트의 배열로 파라미터 제공 +- ex) MapSqlParameterSource, BeanPropertySqlParameterSource 모두 SqlParameterSource 타입 + +#### 3. int[] batchUpdate(String sql, List batchArgs) +- 위치 치환자 & Object 배열 + +## 2.2.3 SimpleJdbcInsert +- DB의 카탈로그 등 메타정보를 활용해 Insert 문을 간단하게 + +### 생성 +- SimpleJdbcInsert withTableName(String tableName) +- SimpleJdbcInsert withSchemaName(String schemaName), SimpleJdbcInsert withCatalogName(String catalogName) +- SimpleJdbcInsert withColumns(String... columnsNames) +- SimpleJdbcInsert withGeneratedKeyColumns(String... columnsNames) + - 자동 생성 키 칼럼을 지정 +- SimpleJdbcInsertOperations withoutTableColumnMetaDataAccess() + - DB에서 테이블 메타데이터를 가져오지 않도록 만든다 + +### 실행 +- int execute([이름 치환자 SQL 파라미터]) + - 이름 치환자를 가진 INSERT문을 내부적으로 생성 + - 맵 or SqlParameterSource 타입 오브젝트로 파라미터 지정 가능 +- Number executeAndReturnKey([이름 치환자 SQL 파라미터]) + - 자동생성된 키 값을 Number 타입으로 돌려줌 + - Number는 Integer, Long, Float, Double 등이 상속하고 있는 상위 클래스 +- KeyHolder executeAndReturnKeyHolder([이름 치환자 SQL 파라미터]) + - 하나 이상의 자동생성 키 컬럼을 갖는 테이블의 경우 + +## 2.2.4 SimpleJdbcCall +- DB에 생성해둔 저장 프로시저 또는 저장 펑션을 호출할 때 사용 + +### 생성 +- SimpleJdbcCallOperations withProcedureName(String procedureName) +- SimpleJdbcCallOperations withFunctionName(String functionName) +- SimpleJdbcCallOperations returningResultSet(String parameterName, ParameterizedRowMapper rowMapper) + - 프로시져가 ResultSet을 돌려주는 경우에 RowMapper를 사용해 매핑 + +### 실행 +- DB의 메타정보를 이용해 필요한 파라미터 정보를 가져온다. +- 이에 맞게 파라미터 값을 전달해야 함. +- T executeFunction(Class returnType, [SQL 파라미터]) +- T executeObject(Class returnType, [SQL 파라미터]) +- Map execute([SQL 파라미터]) + +## 2.2.5 스프링 JDBC DAO +- 보통 DAO는 도메인 오브젝트, DB 테이블 단위로 만들어지기 때문에 어플리케이션 하나에 여러 개의 DAO가 만들어짐 +- JdbcTemplate, SimpleJdbcTemplate, SimpleJdbcInsert 등은 멀티스레드 환경에서도 안전하게 공유해서 사용 가능 + - 싱글톤 빈으로 관리 가능 +- but, 테이블이나 저장 프로시저 단위로 오브젝트를 만들어야 하기 때문에 DAO마다 다른 템플릿 사용 +- 따라서, DataSource만 DI 받고 필요한 템플릿과 JDBC 오브젝트는 직접 생성 \ No newline at end of file diff --git "a/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.4 JPA.md" "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.4 JPA.md" new file mode 100644 index 0000000..ae2342a --- /dev/null +++ "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.4 JPA.md" @@ -0,0 +1,87 @@ +2.4 JPA += +- Java Persistent API의 약자 +- 영속성 관리와 ORM을 위한 표준 기술 +- ORM + - 오브젝트와 RDB 사이에 존재하는 개념과 접근 방법, 성격의 차이 때문에 불편함을 제거 + - 필요한 SQL을 자동으로 작성하고 DB에 작업한 뒤 결과를 다시 오브젝트로 돌려줌 +- 따라서, ORM 사용 개발자는 모든 데이터를 오브젝트 관점으로만 볼 수 있음 + +## 2.4.1 EntityManagerFactory 등록 +- 스프링에서는 세 가지 방법을 사용해 EntityManagerFactory 타입의 빈을 등록 가능 + +### 1. LocalEntityManagerFactoryBean +- 제약사항이 많아 잘 사용하지 않음 + +### 2. JavaEE5 서버가 제공하는 EntityManagerFactory +- 스프링 애플리케이션에서는 JNDI를 통해 서버가 제공하는 EntityManager와 EntityManagerFactory를 제공받을 수 있음 +- JPA를 지원하는 JavaEE5 이상의 서버에 배치하고 JPA 프로바이더와 서버가 요구하는 설정을 해뒀다고 전제할 경우 사용 가능한 방식 +- 스프링에서는 특별히 관여할 수 있는 작업은 거의 없음 +- 모든 JPA 기능은 서버와 JPA의 퍼시스턴스 유닛 설정에 따름 + +### 3. LocalContainerEntityManagerFactoryBean +- 스프링이 직접 제공하는 컨테이너 관리 EntityManager를 위한 EntityManagerFactory를 만들어줌 +- JavaEE 서버에 배치하지 않아도 JPA 기능 활용 가능 +- 빈으로 등록, 필수 프로퍼티로 DataSource 지정 +- 매핑에 사용될 자바 오브젝트는 JPA 어노테이션을 이용해 정의 가능 + +```java +@Entity +public class Member { + @Id + int id; + + @Column(length=100) + String name; + + @Column(nullable=false) + double point; +} +``` + +#### 추가 가능한 프로퍼티 +- persistenceUnitName + - persistence.xml에 하나 이상의 퍼시스턴스 유닛이 정의되는 경우 사용 +- persistenceXmlLocation + - 일부 WAS에서 EntityManagerFactory를 만들어버리는 경우가 있기 때문에 디폴트 위치 말고 다른 곳에 놓는게 안전함 + - 이 경우에 위치를 지정해주어야 함 +- jpaProperties, jpaPropertyMap + - EntityManagerFactory를 위한 프로퍼티를 지정할 때 사용 + +### 트랜잭션 매니저 +- 컨테이너가 관리하는 EntityManager 방식에는 컨테이너가 제공하는 트랜잭션 매니저가 반드시 필요함 +- 애플리케이션 관리 EntityManager에서는 코드에서 직접 트랜잭션을 제어할 수 있지만, 이 방법은 테스트 목적이 아니라면 권장 x +- 따라서, EntityManager를 사용하려면 트랜잭션 매니저 등록해야 +- JpaTransactionManager를 등록하고 EntityManagerFactory 빈을 프로퍼티에 등록 + +## 2.4.2 EntityManager를 사용하는 방법 +### JpaTemplate +### 애플리케이션 관리 EntityManager와 @PersistenceUnit +- EntityManagerFactory를 통해 생성 (번거로워서 잘 안) +- JavaEE 환경과 JavaSE 환경에서 모두 사용 가능 +- `EntityManager em = entityManagerFactory.createEntityManager();` +- EntityManagerFactory 을 D씀 받는 방법 + - @Autowired + - @PersistenceUnit : JPA 표준 스팩, 스프링 사용 x, xml 설정 필요(``) +### 컨테이너 관리 EntityManager와 @PersistenceContext +- 컨테이너가 제공하는 EntityManager를 직접 제공받는 법 +- JPA의 @PersistenceContext 어노테이션 사용 +- JavaEE 컨테이너가 관리하는 EntityManager를 주입받는 방법 사용 +- 이렇게 주입받은 EntityManager는 실제 EntityManager가 아니라 현재 진행 중인 트랜잭션에 연결되는 퍼시스턴스 컨텍스트를 갖는 프록시 (EntityManager는 사용자의 요청에 따라 만들어지는 스레드별로 독립적으로 사용되어야 하기 때문) +- 실제로는 트랜잭션마다 다른 EntityManager 오브젝트를 사용 +- `@PersistenceContext(type=PersistenceContextType.TRANSACTION) EntityManager em;` + +### @PersistenceContext와 확장된 퍼시스턴스 컨텍스트 +- 세 번째 방법과 동일하게 @PersistenceContext 어노테이션 사용하여 EntityManager를 주입 +- type 값이 PersistenceContextType.EXTENDED +- 상태유지 세션빈에 바인딩 +- 사용자별로 독립적이며 장기간 보존되는 오브젝트로 만들어짐 + +### JPA 예외 변환 +- JPA의 API를 사용하면 런타임 예외를 발생 -> 예외 처리 필요 x + +### JPA 예외 변환 AOP +- JPA 예외를 스프링의 DataAccessException 예외로 전환시키는 법 -> 스프링 AOP 사용 + 1. 변환이 필요한 DAO 클래스에 @Repository 붙이기 + 2. PersistenceExceptionTranslationPostProcessor를 빈으로 등록하면 @Repository 붙은 빈을 찾아서 예외 변환을 해주는 AOP 어드바이스를 적용해주는 후처릿기 생성됨 +- 단, DataAccessException의 서브 클래스로 매핑되서 던져지지는 않음 \ No newline at end of file diff --git "a/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.5 \355\225\230\354\235\264\353\262\204\353\204\244\354\235\264\355\212\270.md" "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.5 \355\225\230\354\235\264\353\262\204\353\204\244\354\235\264\355\212\270.md" new file mode 100644 index 0000000..bc38aaf --- /dev/null +++ "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.5 \355\225\230\354\235\264\353\262\204\353\204\244\354\235\264\355\212\270.md" @@ -0,0 +1,41 @@ +2.5 하이버네이트 += +- 가장 크게 성공한 오픈소스 ORM vmfpdladnjzm +- 스프링 : POJO를 내세워 가볍고 단순한 엔터프라이즈 시스템을 개발 +- 하이버네이트 : POJO로 SQL을 직접 사용하여 빠르면서도 편리한 ORM 방식의 개발 + +## 2.5.1 SessionFactory 등록 +- 하이버네이트의 핵심 엔진 +- 스프링은 이를 빈으로 등록하고 초기화할 수 있도록 두 가지 팩토리 빈을 제공 + +### LocalSessionFactoryBean +- 빈으로 등록된 DataSource를 이용해서 스프링이 제공하는 트랜잭션 매니저와 연동할 수 있도록 설정된 SessionFactory를 만들어주는 팩토리 빈 + +### AnnotationSessionFactoryBean +- JPA처럼 엔티티 클래스에 애노테이션을 부여하고 이를 매핑정보로 사용하는 방법을 제공 +- 기본적으로 JPA에 정의된 매핑용 애노테이션을 그대로 사용 가능, 추가로 확장 애노테이션을 사용해 하이버네이트의 고급 매핑 정보를 정의 가능 + +### 트랜잭션 매니저 +- 스프링의 트랜잭션 경계설정을 적용하려면 스프링이 제공하는 트랜잭션 매니저를 이용해야 함 + +#### 1. HibernateTransactionManager +- 단일 DB 사용하는 경우 +#### 2. JtaTransactionManager +- 여러 개의 DB에 대한 작업을 하나의 트랜잭션으로 묶는 경우 + +## 2.6.2 Session과 HibernateTemplate +- Session은 하이버네이트의 핵심 API +- SessionFactory로부터 만들어지며 보통 트랜잭션과 동일한 스코프로 만들어짐 +- 하이버네이트도 트랜잭션과 동기화된 Session을 가져와 사용 +- 스프링이 제공하는 두 가지 방법 + +### 1. HibernateTemplate +- 템플릿/콜백 패턴 적용 +- 예외 변환 기능을 제공 +- 트랜잭션 매니저를 통해 트랜잭션이 시작되지 않았어도 자동 커밋 +- 3.0.2 부터는 하이버네이트 API를 직접 사용해도 스프링 트랜잭션 동기화 기능 적용 가능해짐 + +### 2. SessionFactory.getCurrentSession() +- 현재 트랜잭션에 연결되어 있는 하이버네이트 Session을 돌려준다. + + diff --git "a/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.6 \355\212\270\353\236\234\354\236\255\354\205\230.md" "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.6 \355\212\270\353\236\234\354\236\255\354\205\230.md" new file mode 100644 index 0000000..7e8dd63 --- /dev/null +++ "b/\354\230\244\354\210\230\354\247\204/Vol2/2\354\236\245_\353\215\260\354\235\264\355\204\260_\354\227\221\354\204\270\354\212\244_\352\270\260\354\210\240/2.6 \355\212\270\353\236\234\354\236\255\354\205\230.md" @@ -0,0 +1,150 @@ +2.6 트랜잭션 += +- 트랜잭션 스크립트 : 하나의 트랜잭션 안에서 동작해야 하는 코드를 한 군데 모아서 만드는 방식 +- 선언적 트랜잭션을 이용하여 트랜잭션 스크립트를 탈피 +- 전파 기능을 사용하면 의미 있는 작은 단위로 만들어진 오브젝트와 메소드를 적절한 순서대로 조합해서 호출하기만 하면 된다. +- 스프링은 이러한 선언적 트랜잭션 기능을 평범한 POJO 코드에 적용하게 해준다. + +## 2.6.1 트랜잭션 추상화와 동기화 + +### 트랜잭션 추상화 +- 스프링은 다양한 트랜잭션 서비스의 종류와 데이터 액세스 기술 사이의 종속성을 제거 +- 트랜잭션 추상 계층을 이용하여 트랜잭션 기능 활용하도록 해줌 + +### 트랜잭션 동기화 +- 트랜잭션을 일정 범위 안에서 유지, 어디서든 자유롭게 접근 가능하도록 해줌 +- 선언적 트랜잭션을 가능하게 하는 핵심 기능 + +### PlatformTransactionManager +- 추상화 핵심 인터페이스 +- 모든 스프링 트랜잭션 기능과 코드는 이 인터페이스를 통해 로우레벨의 트랜잭션 서비스를 이용할 수 있다. +- 트랜잭션 경계를 지정하는 데 사용됨 + - getTransaction(), commit(), rollback() +- TransactionDefinition : 트랜잭션의 네 가지 속성ㅇ + +### 트랜잭션 매니저의 종류 : PlatformTransactionManager의 구현 클래스 +- DataTransactionManager : Connection의 트랜잭션 API 사용 +- JpaTransactionManager +- HibernateTransactionManager +- JmsTransactionManager, CciTransactionManager +- JtaTransactionManager + +## 2.6.2 트랜잭션 경계설정 전략 +- 경계는 보통 서비스 계층 오브젝트의 메소드 +- 방법 1. 코드에 의한 프로그램적인 방법 : 트랜잭션 코드를 직접 만들기 +- 방법 2. AOP를 이용한 선언적인 방법 : 기존 코드에 경계설정 기능 부여 + +### 코드에 의한 트랜잭션 경계설정 + +### 선언적 트랜잭션 경계설정 +- 트랜잭션 프록시 빈 사용 +#### AOP와 tx네임스페이스 +#### @Transactional +- 트랜잭션이 적용될 타깃 인터페이스나 클래스, 메소드 등에 부여 +- 클래스, 인터페이스, 메소드 동시 부여 가능 + - 메소드 > 클래스 > 인터페이스 메소드 > 인터페이스 + +### 프록시 모드 : 인터페이스와 클래스 +- 스프링의 AOP는 기본적으로 다이내믹 프록시 적용 +- 다이내믹 프록시는 인터페이스가 필요 +- 특별한 경우, 인터페이스를 구현하지 않은 클래스에 트랜잭션 적용하는 경우는?? +- 스프링이 지원하는 클래스 프록시 모드 사용! +- GGLib 라이브러리가 제공해주는 클래스 레벨의 프록시 +- final에 적용 불가 (클래스 프록시는 타깃 클래스를 상속해서 프록시를 만들기 때문) +- 불필요한 메소드에 트랜잭션 적용되기도 함 (클래스의 모든 메소드에 트랜잭션 적용, 리소스 낭비) + +### AOP 방식 : 프록시와 AspectJ +- 스프링의 AOP 는 보통 프록시 방식 +- AspectJ는 프록시를 두지 않고, 타깃 오브젝트 자체를 조작해서 부가기능을 직접 넣는 방식 +- 다양한 조인 포인트, 고급 기능 사용 가능 +- 별도의 빌드 과정이나 바이트코드 조작을 위한 로드타입 위버 설정과 같은 부가 작업이 필요 +- 클래스와 클래스 메소드 레벨에 부여해야 함 + + +#### 타깃 오브젝트가 자기 자신의 다른 메소드를 호출할 때 +- AopContext.currentProxy() + - 프록시 설정에서 현재 진행 중인 프록시를 노출하도록 설정 + - 스프링 API가 등장하기 때문에 권장 x +- AspectJ AOP + - 클래스 바이트 코드 변경 + +## 2.6.3 트랜잭션 속성 +### 1. 트랜잭션 전파 : propagation +- 트랜잭션을 시작하거나 기존 트랜잭션에 참여하는 방법을 결정하는 속성 +#### REQUIRED +- 디폴트 +- 있으면 참여, 없으면 생성 +#### SUPPORTS +- 있으면 참여, 없으면 없이 진행 +#### MANDATORY +- 있으면 참여, 없으면 예외 +#### REQUIRES_NEW +- 항상 새로운 트랜잭션 +- 이미 진행 중인 것은 보류시킴 +#### NOT_SUPPORTED +- 트랜잭션 사용 x +- 이미 진행 중인 것이 있으면 보류 +#### NEVER +- 트랜잭션 사용하지 않도록 강제 +- 이미 진행 중인 것이 있으면 예외 +#### NESTED +- 이미 진행 중인 것이 있으면 중첩 트랜잭션 시작 +- 부모 트랜잭션의 커밋과 롤백에는 영향을 받지만 자신의 커밋과 롤백은 부모에게 영향x + +### 2. 트랜잭션 격리 수준 : isolation +#### DEFAULT +- 사용하는 데이터 접근 기술이나 DB 드라이버를 따름 + +#### READ_UNCOMMITED +- 가장 낮은 격리 수준 +- 커밋 되기 전의 값을 읽을 수 있음 +- 빠름 + +#### READ_COMMITED +- 가장 많이 사용 +- 트랜잭션이 커밋한 결과만 읽을 수 있음 +- 같은 로우를 다시 읽을 때 다른 내용을 읽을 수 있음 + +#### REPEATABLE_READ +- 하나의 트랜잭션이 읽은 로우를 다른 트랜잭션이 수정하는 것을 막는다. +- 하지만 새로운 로우를 추가하는 것은 제한하지 않음 + +#### SERIALIZABLE +- 가장 강한 트랜잭션 격리수준 +- 여러 트랜잭션이 동시에 같은 테이블 접근 불가 +- 성능이 떨어짐 + +### 3. 트랜잭션 제한시간 : timeout +### 4. 읽기전용 트랜잭션 : read-only, ReadOnly +- 성능 최적화 +- 쓰기 작업을 방지 +### 5. 트랜잭션 롤백 예외 : rollback-for, rollbackFor, rollbackForClassName +- 런타임 예외 발생 시 롤백 +- 예외x or 체크 예외 발생 시 커밋 + - 체크 예외는 비즈니스적 의미를 담은 결과를 돌려주는 용도로 많이 사용되기 때문 +- 체크 예외를 롤백하고 싶을 때 사용 +### 6. 트랜잭션 커밋 예외 : no-rollback-for, noRollbackFor, noRollbackForClassName +- 런타임 예외를 커밋하고 싶을 때 사용 + +## 2.6.4 데이터 액세스 기술 트랜잭션의 통합 +- 스프링은 DB 당 하나의 트랜잭션 매니저 사용 +- DB는 하나인데 두 가지 이상의 데이터 접근 기술을 사용하는 경우는?? + +### 트랜잭션 매니저별 조합 가능 기술 +- 알고 있으면 적절한 기술과 트랜잭션 매니저의 선택이 가능할 것 +- DataSourceTransactionManager : JDBC + iBatis +- JpaTransactionManager : JPA + DataSource(JDBC / iBatis) + - 기본적으로 JPA의 트랜잭션은 JPA API를 이용해 처리되지만 스프링에서는 JPA의 EntityManagerFactoryy가 스프링 빈으로 등록된 DataSource 사용할 수 있고, 이를 JDBC나 iBatis가 사용할 수 있다. +- HibernateTransactionManager : Hibernate + DataSource(JDBC / iBatis) + - 위와 동일 +- JtaTransactionManager : 모든 종류의 데이터 액세스 기술을 같은 트랜잭션 안에서 동작하게 만들 수 있음 + +### ORM과 비 ORM DAO를 함께 사용할 때의 주의사항 +- JPA나 하이버네이트는 단순히 JDBC API를 간접적으로 실행해주는 방식이 아님 +- persist()나 save() 메소드 호출 시 바로 INSERT 쿼리 실행되는 것x, 엔티티 매니저나 세션에만 저장해둠 + - 쿼리하는 것을 최대한 지연 (캐싱) + - 트랜잭션이 끝나기 전에 변경될 수 있기 때문 +- 하지만 JDBC는 이를 모름 -> 같은 트랜잭션에서 count()를 실행하면 바로 쿼리를 보내서 조회. 따라서 개수 업데이트 안됨 +- 따라서, flush()를 사용해 캐시를 DB에 반영하고 JDBC DAO의 쿼리를 사용해야 함 + - flush()는 캐시의 내용을 즉시 DB에 반영 +- AOP를 사용해 JDBC API를 사용하는 곳에 flush() 하는 부가기능을 추가하는 방법도 사용 가능 diff --git "a/\354\230\244\354\210\230\354\247\204/Vol2/5\354\236\245 AOP\354\231\200 LTW/5.1 \354\225\240\354\212\244\355\216\231\355\212\270 AOP.md" "b/\354\230\244\354\210\230\354\247\204/Vol2/5\354\236\245 AOP\354\231\200 LTW/5.1 \354\225\240\354\212\244\355\216\231\355\212\270 AOP.md" new file mode 100644 index 0000000..773777d --- /dev/null +++ "b/\354\230\244\354\210\230\354\247\204/Vol2/5\354\236\245 AOP\354\231\200 LTW/5.1 \354\225\240\354\212\244\355\216\231\355\212\270 AOP.md" @@ -0,0 +1,176 @@ +5.1 애스펙트 AOP += +## 5.1.1 프록시 기반 AOP +- AOP는 모듈화된 부가기능(어드바이스)와 적용 대상(포인트컷)의 조합을 통해 여러 오브젝트에 나타나는 공통적인 능을 손쉽게 개발하고 관리하는 기술 +- 스프링은 다이내믹 프록시 기술을 사용 + +### 스프링이 지원하는 프록시 기반 AOP 개발 방법 +#### AOP 인터페이스 구현과 등록을 이용하는 방법 +- 클래스로 개발하고 이를 빈으로 등록해서 적용 + - 인터페이스를 구현하는 방식으로 어드바이스와 포인트컷 개발 + - 이를 빈으로 등록 + - 이 조합을 어드바이저로 구성 + - 다양한 조합 가능 + - 어드바이저를 빈으로 등록 + - 어드바이저를 프록시로 만들어 빈으로 등록 + - +#### AOP 인터페이스 구현과 aop 네임스페이스의 태그를 이용하는 방법 +#### 임의의 자바 클래스와 aop 네임스페이스의 를 이용하는 방법 +#### @AspectJ 애노테이션을 이용한 애스펙트 개발 방법 +- 애스펙트라는 개념을 사용 + - 독립적인 AOP 모듈. 평범한 자바 클래스를 애노테이션을 사용하여 애스펙트로 정의 +- AspectJ AOP 프레임워크에서 정의된 @AspectJ 문법과 애스팩트 정의 방법을 차용 +- AspectJ AOP를 사용하는 것은 아님 (프록시 기반 AOP 사용) +- 유연하고 편리함 + +### 자동 프록시 생성기와 프록시 빈 +- 프록시 방식을 적용하기 위해선 클라이언트는 타겟에 인터페이스로 접근해야 함 -> DI 준수 +- 같은 타입의 Proxy를 정의하고 Client -> Proxy -> Targer 순서로 의존관계를 맺게하여 부가기능 제공 +- 그런데 이런 방식으로 하면, 같은 타입의 빈이 여러개 생성되므로 자동 주입 불가 +- 스프링의 자동 프록시 생성 + - 포인트컷과 애노테이션 사용 +- 자동 프록시는 target을 감싸서 대체 + +![image](https://github.com/user-attachments/assets/a25eb026-adf4-427d-bc46-a31d23b35e81) + +- 자동 프록시 생성기의 틀징 + - AOP 적용은 @Autowired의 타입에 의한 의존관계 설정에 문제를 일으키지 않는다. + - AOP 적용은 다른 빈들이 Target 오브젝트에 직접 의존하지 못하게 한다. + - Interface 타입이므로 Target을 직접 의존하고 있던 빈에는 프록시를 적용하지 못하고 오류가 난다. + +![image](https://github.com/user-attachments/assets/0622a279-8fca-464d-8392-b5b7c37e6d5b) + +### 프록시의 종류 +- 클래스를 직접 참조하는 경우에도 프록시 적용 가능 +- 타깃 클래스를 상속한 서브클래스 사용 +- 두 가지 제약 + - final 클래스와 메소드에 사용 불가 + - CGLib라는 바이트코드 생성 라이브러리 필요 + +## 5.1.2 @AspectJ AOP +### 애스펙트 +- 많은 오브젝트에 걸쳐서 필요한 부가기능을 추상화해놓은 것 +- 하나 이상의 포인트컷과 어드바이스로 구성 +- @AspectJ는 애스펙트를 자바 클래스와 메소드, 애노테이션을 이용해서 정의하는 방법을 가르킴 + +### @Aspect 클래스와 구성요소 +- 자바 플래스에 @Aspect를 붙여 애스펙트를 만듦 +- 어드바이스는 오브젝트로 만들어 실행 가능 + +#### 포인트컷: @Pointcut +- 메소드에 붙여 선언하고, 선택 로직은 @Pointcut 안에 포인트컷 표현식을 넣어서 정의 +- 하나의 애스펙트 안에 여러개의 포인트컷 선언 가능 + +```java +@Pointcut("execution(* hello(..))") +private void all() {} +``` + +#### 어드바이스: @Before, @AfterReturning @AfterThrowing, @After, @Around +- 메소드에 붙이고 메소드 내의 자바 코드로 어드바이스 로직 작성 +- 하나이상 적용 가능 + +```java +@Around("all()") // 포인트컷 지정 +public Object xxx(ProceedingJoinPoint pjp) throws Throwable { + ... + Object ret = pjp.proceed(); + ... + return ret; +} +``` + +``` java +@Aspect("execution(* *(..))") // 표현식을 사용하여 직접 지정도 가능 +``` + +- 포인트컷과 어드바이스 메소드 외에도 일반 필드나 단순 메소드 포함하거나 상속과 인터페이스 구현, DI 가능 -> POJO 클래스처럼 사용 가능 + +### 포인트컷 메소드와 애노테이션 +![image](https://github.com/user-attachments/assets/af5ff96a-d238-49e6-b633-ff156f3bd9fb) +- 포인트컷 메소드의 리턴타입은 항상 void 형 +```java +@Pointcut("execution(* sayHello(..))") private void hello(); +``` +- 포인트컷의 조인포인트는 메소드 + +#### execution : 가장 정교한 포인트컷 지시자, 메소드 단위까지 선택 가능 +#### within : 클래스 타입 패턴만을 이용해 조인 포인트 메소드 선택. 모든 메소드에 적용. 패키지 선택 가능 +#### this, target: 오브젝트로 선별 +#### args: 메소드의 파라미터 타입을 이용 +#### @target, @within: 특정 애노테이션이 부여된 오브젝트를 선정 +#### @args: 특정 애노테이션이 붙은 파라미터 +#### @annotation: 특정 애노테이션이 부여된 메소드를 선정 +#### bean: 빈 이름이나 아이디 +#### &&: AND , ||: or, !: 제외 + +### 어드바이스 메소드와 애노테이션 +![image](https://github.com/user-attachments/assets/fbd3e9ae-4428-4a8b-9789-df97ddb4d72f) + +#### @Around +- ProceedingJoinPoint 를 매개변수로 받아서 타킷 오브젝트 실행하고 return 값 받아올 수 있다. +```java +@Around("myPointcut()") +public Object doNothing(ProceedingJoinPoint pjp) throws Throwable{ + Object ret = pjp.proceed(); + return ret; +} +``` + +#### Before +- 리턴값에 관심이 없을 때 사용 +```java +@Around("myPointcut()") +public Object doNothing(JoinPoint jp) throws Throwable{ + System.out.println(jp.getSignature().getDeclaringTypeName); + System.out.println(jp.getSignature().getName); + for (Object arg : jp.getArgs()) { + System.out.println(arg); + } +} +``` + +#### AfterReturning +- 정상 종료 시 사용 +- 리턴값 참조 가능 + +```java +@Around(pointcut = "myPointcut()", returning = "ret") +public Object doNothing(Object ret){ + +} +``` + +#### @AfterThrowing +- 예외 발생 시 실행 + +#### @After +- 결과에 상관없이 실행 +- 로그 남길 때 사용 + +### 파라미터 선언과 바인딩 +- 포인트컷 표현식의 타입 정보를 파라미터와 연결 가능 + - 표현식 간결해짐 +```java +@Before("@target(bj)") +public void beforeBatch(BatchJob bj) {...} +``` + +- 포인트컷 정의에도 사용 가능 +```java +@Pointcut("@target(bj)") +public void batchJob(BatchJob bj) {...} + +@Before("batchJob(bj)") +public void beforeBatch(BatchJob bj) {...} +``` +- 포인트컷 내 파라미터의 이름은 파라미터 이름과 일치해야 함 + +### @AspectJ를 이용한 AOP의 학습 방법과 적용 전략 +- 직관적으로 선정 대상을 파악할 수 있도록 포인트컷에 의미 있는 이름을 부여하고 조합하여 사용하자 +- 단계를 거쳐 점진적으로 도입하자 +- 부가 기능에 사용 하는 것 -> AOP를 제거해도 핵심기능과 도메인 로직 자체는 문제 없이 동작해야 한다. +- 충분한 테스트를 작성해야 한다. + - POJO로 만든 메소드이므로 단위 테스트 작성 가능 +- 포인트컷 선정 대상ㅇ 확인 필요 +- 레퍼런스 문서 참고하자 \ No newline at end of file diff --git "a/\354\230\244\354\210\230\354\247\204/Vol2/5\354\236\245 AOP\354\231\200 LTW/5.2 AspectJ\354\231\200 @Configurable.md" "b/\354\230\244\354\210\230\354\247\204/Vol2/5\354\236\245 AOP\354\231\200 LTW/5.2 AspectJ\354\231\200 @Configurable.md" new file mode 100644 index 0000000..5c4b4a1 --- /dev/null +++ "b/\354\230\244\354\210\230\354\247\204/Vol2/5\354\236\245 AOP\354\231\200 LTW/5.2 AspectJ\354\231\200 @Configurable.md" @@ -0,0 +1,17 @@ +5.2 AspectJ와 @Configurable += +- 프록시를 사용하지 않고 자바 클래스의 바이트코드 조작을 통해 애스펙트 적용 +- why? + - 프록시로는 어드바이스를 적용할 수 없는 조인 포인트와 포인트컷 지시자 지원을 위함 + - 필드 읽기와 쓰기, 스태틱 초기화, 인스턴스 생성, 인스턴스 초기화 지원 + +## 5.2.2 빈이 아닌 오브젝트에 DI 적용하기 +- DDD에서 빈으로 등록되지 않은 도메인에서 오브젝트를 DI 받고 싶다면? +- 오브젝트 생성 시점에 DI 기능을 가진 어드바이스 적용 +- but, 오브젝트 생성시점은 스프링 AOP가 지원하는 조인포인트가 아니므로 AspectJ 가 필요 + + +### DI 애스펙트 +- DependencyInjectionAspect 제공 +- Configurable이라는 애노테이션이 부여된 클래스의 생성자가 호출되어 오브젝트가 만들어질 때를 가리키는 포인트컷 +- 또는, 빈의 초기화 메소드가 정의되어 있다면 실행