DB 테이블과 필드정보를 고스란히 담고 있는 SQL 문장을 분리해보자
이 두정보는 완벽하게 하면 변경될 일이 없지만
변화가 있을 수 있다 어쩔수 없이..
이렇게 하면 스프링 설정을 통해 빈 값에 주입해줄수 있다.
이렇게 해두면 XML 파일만 변경해 주면 된다.
SQL 맵 프로퍼티 방식
SQL 이 많아지면? DAO 에 DI 용 프로퍼티를 계속 추가해줘서 곤란해짐
그래서 하나의 컬렉션 안에 담아두자.
이런식으로 하면 편리해지지만 SQL 을 가져올때 문자열로된 키값이 오타와 같은
실수가있으면 실행 되기전 오류 확인이 힘듬..
위에도 단점이 존재하기에
DAO가 사용할 SQL을 제공해주는 기능을 독립 시킬 필요가 있다
DI 해주고..
SqlService는 모든 DAO 에서 서비스 빈을 사용하게 만들자..
SqlService 인터페이스에는 어떤 기술적인 조건이나 제약사항도 담겨 있지 않다.
SqlService 구현방법 고민
XML에 SQL 정보를 넣어두는건 좋은 방법이 아님.
SQL 전용 포맷을 가진 독립적인 파일을 이용하는편이 바람직
XML에 담긴 정보를 읽어오기
장점: XML 문서정보를 거의 동일한 구조의 오브젝트로 직접 매핑
XML을 오브젝트 처럼 다룰 수 있다.
위에 XML 문서의 구조를 정의하는 스키마를 만들면..
sqlmap.xsd 파일로 저장 되고 JAXB컴파일러로 컴파일해보기
sqlmap.xml 이라는 이름으로 저장
Xml 문서를 읽어서 자아 오브젝트로 변환하는것을 언마샬링 이라 함.
오브젝트를 xml 로 마꾸는걸 마샬링
자바 오브젝트를 바이트 스트림으로 바꾸는걸 직렬화 라고 함.
생성자에서 XML파일을 읽어서 맵에 저장,
개선점
생성자에서 예외가 발생할 수도 있는 복잡한 초기화 작업은 다루지 안흔ㄴ게 좋음
-> 초기 상태를 가진 오브젝트를 만들고 별도의 초기화 메소드를 사용하자
읽어들일 파일의 위치와 이름을 코드에 고정하는건 좋지 않음
XmlSqlService 오브젝트는 빈이므로 스프링에 제어권이 있따.
생성 초기화는 스프링에게 맡겨야됨.
빈후처리기 -> 스프링컨테이너가 빈을 생성한뒤 부가적인 작업을 수행할 수 있게 해줌.
애노테이션을 이용한 빈 설정 지원해주는 몇 가지 빈 후처리기
편리한방법 context 스키마의 annotation-config 태크를 사용하면 편리함
XML 대신 다른 포맷의 파일에서 SQL을 읽어오게 하려면 어떻게 해야 할까?
XmlSqlService 단일책임원칙 위반.
분리 가능한 관심사 구분
XmlSqlService
첫째는 sQL 정보를 외부 리소스로부터 읽어오는것.
SQLdl 담겨 있는 리소스가 어떤 것이든 상관엇ㅂ이 애플리케이션에서 활용 가능하도록 메모리에 읽어들이는 것을 하나의 책임
읽어온 SQL을 보관해 뒀다가 필요할 때 제공
...
서비스를 위해서 한번 가져온 sql 을 필요에 따라 수정할 수 있게하자
SqlReader / SqlRegistry 는 각각 SQL 정보를 읽어 가져오는 방법과 이를 저장해두는 방법의 구체적인 기술과 구현으로 독립적으로 사용 하도록 만든 인터페이스.
인터페이스를 통해서만 의존 오브젝트에 접근해야 됨.
인터페이스는? 한 개 이상을 상속하는 것이 허용 됨.
인터페이스 구현은 타입을 상속 하는 것.
이런 구조도 좋지만 빈을 여러개 등록해줘야되는 불편함이 존재..
디폴트 의존관계는 자동으로 적용되는 의존관계
에러가남. sqlmapFile 프로퍼티가 비어있기때문에..
JaxXmlSqlReader 개선
자바에는 JAXB 외에도 다양한 XML 과 자바오브젝트를 매핑하느 ㄴ기술이 있따
XML 파일을 좀 더 다양한 소스에서 가져올 수 있게 만든다 현재는 UserDAo 클래스와 같은 클래스패스 안에서만
XML을 읽을수 있다 이것을 임의의 클래스나 파일 스스템상의 절대위치 또는 HTTP 프로토콜을 통해
원격에서 가져오도록 확장할 수는 없는가 하는 점이다.
XML과 자바오브젝트를 매핑해서 상호 변환해 주는 기술을 OXM 이라 함.
OxmSqlService 는 SqlReader 타입의 의존 오브젝트를 사용하되
이를 스태틱 멤버 클래스로 내장하고 자신만이 사용할 수 있도록 만들자
의존 오브젝트를 자신만이 사용하도록 독점하는 구조다
이런식으로 확장 변경을 제한두는 이유?
OXM을 이용하는 서비스 구조로 최적화하기 위해서다.
빈 의 등록과 설정은 단순해지고 쉽게 사용함
OxmlSqlService 외형적인 틀을 유지한채 SqlService 구현은 BaseSqlService로 위임하자
프록시 이용 해도 되지만 빈을 여러개 등록해야되서 또 복잡해짐
여러가지 종류의 리소스를 어떻게 단일 인터페이스 메소드로 추상화 할까?
리소스 접근 방법이 통일되면 좋지 않을까?
애플리케이션 컨텍스트가 사용할 설정정보 파일을 지정하는 것부터 시작해서
스프링의 거의 모든 API는 외부의 리소스 정보가 필요할 때는 항 상 이 Resource 추상화를 이용
리소스 인터페이스는 빈이 아니라 값으로 취급
단순한 정보를 가진 값
리로스로더의 대표적인 예 : 스프링의 애플리케이션 컨텍스트
리소스 인터페이스 상속 따라서 모든 애플리케이션 컨텍스트는 리소스 로더임
리소스를 사용할 때는 리소스오브젝트가 실제 리소스가 아님
단지 리소스에 접근할 수 있는 추상화된 핸들러.
리소스 오브젝트가 만들어져도 실제로 리소스가 존재하지 않을 수 있음.
서버 가동중에 SQL 변경??
재시작을해야되는 문제점..
DI의 적합한 오브젝트 설계가 필요 가치를 얻으려면..
인터페이스 사용하는 이유 : 다형성 때문에
인터페이스 분리 원칙을 통해 클라이언트와 의존 오브젝트 사이의 관계를 명확하게 해줄수 있기 때문.
인터페이스는 하나의 오브젝트가 여러 개를 구현할 수 있으므로 하나의 오브젝트를 바라보는 창이
여러 가지 일수 있다는 뜻.
인터페이스 분리 원칙
하나의 오브젝트가 구현하는 인터페이스를 여러 개 만들어 구분 하는이유?
오브젝트의 기능이 발전하는 과정에서 다른 종류의 클라이언트가 나오기 때문
인터페이스 여러개 만드는것도 좋지만 상속을 통해 확장도 있따.
베이스 서비스는 기존 조회랑 등록역할만 하면되기때문에
업데이트는 새로운 클라이언트를 제공해서
만들어주자 정리하면 각각 자신의 필요에 맞는 인터페이스에만 의존..?
각각 관심과 필요에 따라서 다른 인터페이스를 통해 접근
실시간으로 변경 작업을 만들때 고려해야 될 사항은 동시성 문제.
자바에서 제공하는 주요 기술을 이용해 간단한 방식으로
안전한 업데이트가 가능한 SQL 레지스트리를 구성해 보자.
기존 HASHMAP은 동시성 처리하기 적합하지 않다.
ConcurrentHashMap 대신 내장형 DB를 이용해 쿼리를 저장하고
수정하자
ConcurrentHashMap이 멀티스레드 환경에서 최소한의 동시성을 보장해주고 성능도 그리 나쁜편은 아니지만 , 저장되는 데이터양이 많아지고 잦은 조회와 변경이 일어나는 환경일때라는 한계가 있음
Derby ,HJSQL, H2
내장형 디비 이기때문에 초기 테이블과 데이터를 필요에 의해 셋팅해준다.
빈으로 등록 될 수 없고 초기화 코드가 필요하면 팩토리 빈으로
EmbeddedDatabaseBuilder을 만들어 줘야됨.
DataSource 대체하기 힘들다 그래서 EmbeddedDbSqlRegistry 도 뒤의 내장형 DB 까지 연동돼서 테스트 하는게 더 간편함
추상업데이트 테스트를 만들어주고
이전에 만들어주었던 해쉬맵 테스트와 임베디드 테스트를 상속받아서
처리했다 각 데이터베이스 생성?? 연결을 분리하기위해.
테스트에서도 이렇게 인터페이스를 분리해서 중복되기 쉬운
코드를 따로 정리해준다.
하나 이상의 SQL 맵을 받아 수정할때 문제가 발생할 수 있음
Hash맵은 트랜잭션 개념 하기 어려움
내장형 디비는 트랜잭션 적용이 쉬움.
트랜잭션 적용 전이라 테스트가 실패한다.
자바 소스는 컴파일된후 클래스 파일에 저장됬다 JVM에 의해 메모리로 로딩되어 실행됨.
애노테이션 활용이 늘어남
핵심 로직을 담은 자바 코드와 이를 지원하는 IoC 방식의 프레임워크
그리고 프레임워크가 참조하는 메타정보라는 세가지 방식과 잘어울리기 때문
메타정보를 활용하는데 애노테이션이 적절함.
애노테이션 같은 메타정보 활용하는 프로그래밍이
미리 약속한 규칙 관례를 따라서 프로그램이 동작하도록 만드는 프로그래밍 스타일을 적극적으로 포용하게 만들어옴.
애노테이션에 많은 정보함축 우선순위도 매기고..
애노테이션에 메타정보로 이용하면서 명시적으로 정보를 넣도록 하지 않았다.
코드는 간결해짐. 그러나 정책을 기억 못하거나 잘못 알고 있을 경우 의도한대로 동작하지 않는 코드가 만들어 짐..
XML 없애기
XML -> 자바 코드로 변경하도록
XML 을 최종적으로 사용하지 않게하는게 목적
@ContedxtConfiguration 스프링 테스트가 테스트용 DI 정보를 어디서 가져와야 하는지 지정할때 사용하는 애노테이션
스프링 시작점을 만들어주고..
테스트 클래스에 위치를 위에와 같이 변경해준다.
아직은 빈정보가 없기때문에 XML을 임포트해서 사용한다.
본격적으로 자바코드로 이동 .
이제까지 작성해왔던 DI XML
<context:annotation-config> 는 @PostConstruct를 붙인 메소드가 빈이 초기화 된후에 자동으로 실행되도록 사용함
@Configuration 사용하면 이제 필요가 없음
빈후처리기를 등록해주기 때문
@bean 으로 만들어주고 Id 는 메소드 명으로
@Autowired 가 붙은 필드의 타입과 같은 빈이 있으면 해당 빈을
필드에 자동으로 주입해줌
@Resource 태그 사용
필드 이름 기준으로 주입받고 Autowird datasource 와 혼동을
일으킬수 잇어서 리소스 어노테이션으로 받는다.
@EnableTransactionManagement
@Autowired를 이용한 자동와이어링
자동와이어링을 이용하면 컨테이너가 이름이나 타입을 기준으로
주입될 빈을 찾아줌. 빈의 프로퍼티 설정을 직접 해주는 자바코드나
XML의 양을 대폭 줄일 수 있다.
직접 주입 대상 지정 방식.
이방식을 자동와이어링 방식으로..
여기서는
DataSource 타입의 빈을 모두 찾는다. 두 개이상이면?
동일한 이름이 있는지 .. 빈은 2개 존재함 dataSource / embeddedDatabase 빈
그중 dataSource 빈이 수정자 메소드 프로퍼티와 이름이 일치해 넣어줌
자동 와이어링으로 sqlService 를 받으니
밑에 함수는 생략해도되고
UserDao에서는 그냥 가져오면됨
첫번째는 그림은 JdbcTemplate를 생성해서 주입해주기때문에
삭제하면 안됌
오토와이어드는 DI 코드 폭을 대폭 줄일 수있지만
빈 설정정보를 보고 다른 빈과 의존관계가 어떻게 되 어 있는가
파악하기 힘들다.
@Component 클래스는 클래스가 자동 빈 등록 대상이 된다..
일종의 마커라고 보면 됨..
@Component 사용하려면 빈 스캔 기능을 사용하겠다는 애노테이션 필요함
@Component가 붙은 클래스가 발견되면 새로운 빈을 추가함
빈 아이디를 따로 지정하지않으면 클래스 이름 첫 글자를 소문자로 바꿔서 사용한다.
스프링은 컴포넌트 애노테이션 외에 자동으로 빈등록이 가능하다 다른 애노테이션들로
여러 개의 애노테이션에 공통적인 속성을 부여하려면? 메타 애노테이션을 사용한다.
오토와이어드는 타입기준으로 적용할 빈을찾지만
두 개이상 발견되면 이름 기준으로 다시 찾는다.
컴포넌트 애노테이션 에 빈아이디를 지정해주자.
성격이 다른 DI 분리
TestUserSerivce 는 UserServiceImpl 를 상속해서 만들어서
UserDa는 자동와이어링 적용 대상이라 생략해도 된다.
테스트용 빈과 실제 사용할 빈 설정정보를 딸 ㅗ분리하기때문에 컴포넌트애노테이션을 붙이지 않는다.
SQL 서비스 는 그자체로 독립적인 모듈로 취급하는게 나아보임.
테스트용 설정정보는 애플리케이션 핵심 설정정보와 또 분리하는 편이 낫지만
SQL 서비스는 애플리케이션이 동작할 때 항상 필요한 정보다..
실제 운영서버에서 작동할 메일샌더를 넣어준다.
그러나 실제 메일샌더랑 TestAppContext에 정의해둔 메일샌더랑
충돌이 일어난다.
운영 테스트 에 공통으로 사용되는 빈은 따로 컨텍스트를 분리해준다
환경에 따라서 빈 설정정보가 달라져야 하는 경우에는
파일을 여러 개로 쪼개고 조합하는 방법 대신.!
간단히 설정정보를 구성하는 기능을 제공한다.
test 프로파일의 빈 설정정보를 담은 클래스
@Profile 붙은 설정 클래스는 임포트로 가져오든 콘텍스트컨피규레이션에 직접
명시하든 상관없이 현재 컨테이너의 활성 프로파일 목록에 자신의 프로파일이름이
들어 있지 않으면 무시된다.
@ActiveProfiles 로 활성 프로파일을 지정해주자
프로파일에 따라 분리했던 설정정보를 하나의 파일로 모아보자
스태틱 중첩 클래스 이용
지정했던 클래스를 내부로 이동시킨 클래스로 변경해주자.
datasource DB 연결정보가 종속 되있음..
운영 개발 로컬에 같은 디비 환경은 같다 정햇을때
연결정보는 환경에 다라 다르게설정해주자
컨테이너가 프로퍼티 값을 가져오는 대상을 프로퍼티 소스라고 함.
@Vlue 애노테이션 이용.
이름 그대로 값을 주입받을때 ..
값을 주입받을 수 있게 치환자를 이용해주자
빈 팩토리 후처리기로 사용되는 빈을 정의해주는 것인데
이 빈 설정 메소드는 반드시 스태틱 메소드로 선언해야 한다.
결론적으로 SQL 매핑 리소스는 빈 클래스 외부에서 설정할 수 있어야 한다.
굳이 위처럼 AppContext내 구현 빈을 만드는 방법이
DI를 따라 여러클래스 인터페이스를 생성하는것보다 나을수 있다.
SQL 서비스가 필요한 애플리케이션은 메인 설정 클래스에서 @Import로
SqlServiceContext 빈 설정을 추가하고 SqlMapConfig을 구현해 SQL 매핑파일의 위치를 지정해 주면 됨.
@Import를 대체 할 애노테이션을 공부해보자
직접 애노테이션을 설정해 좀더 명확하게 어떤 서비스를 이용하는지
코드상에 노출 시킬 수 있다.