본문 바로가기

스프링

[spring] xml_springOxm_jaxen_리소스추상화_3

1.     Com.springsource.javax.activation : Spring java-mail

2.     Com.springsource.javax.mail : Spring java-mail

3.     Com.springsource.org.aopalliance : Spring ProxyFactoryBean

4.     Com.springsource.org.aspectj.tools : AspectJExpressionPointcut 포인트컷 표현식 지원

5.     Com.springsource.org.castor : OXM castor Framework

6.     Com.springsource.junit : junit

7.     Commons-logging : Spring-context가 사용

8.     Mail : java-mail

9.     Mockito : 목 프레임워크 중 Mockito

10.   Mysql-connector : Mysql JDBC

11.   Org.springframework.context.support : Spring java-mail

12.   Org.springframework.oxm : Spring OXM(Object XML Mapping) Jaxb .

13.   Spring-aop : 스프링 기능 자체의 aop, Spring ProxyFactoryBean

14.   Spring-bean : 스프링 코어와 함께 의존성 주입 제공 (Core Container)

15.   Spring-context : 스프링 코어, BeanFactory를 확장한 어플리케이션 컨텍스트 구현, 리소스 로드 및 국제화 지원(Core Container)

16.   Spring-core : 다른 스프링 모듈이 사용하는 유틸리티(Core Container)

17.   Spring-expression : EL 확장 Bean속성(배열, 컬렉션 포함).(Core Container)

18.   Spring-jdbc : 스프링이 지원하는 jdbc.

19.   Spring-test.jar

-       @RunWith : Junit 프레임워크의 테스트 실행방법을 확장시 사용.

-       SpringJUnit4ClassRunner : 어플리케이션컨텍스트를 만들고 관리하는 확장 클래스

-       @ContextConfiguration(경로) : 자동으로 만들어줄 어플리케이션 컨텍스트 설정파일

20.   Spring-tx : DuplicateKeyException.class 파일 존재 및 스프링 트랜잭션

 

 

레벨의 역할(다음레벨 셋팅 및 전달)은 레벨에게 위임

 

User의 역할은 User에게 User 레벨셋팅

 

JAXB sql 엘리먼트 domain

 

Jaxb Sqlmap 엘리먼트 domain

 

 

SpringUnmarshaller을 통해 마샬링/언마샬링을 하기 위해서는 해당 domain ObjectFactory를 생성해줘야 한다. 직접 생성해줘도 되고, Jaxb 컴파일러로 생성해도 됨.

 

 

JPA, Hibernate, JDBC, JDO 와 같이 변할 수 있으므로 인터페이스

 

 

JDBC 구현

 

쿼리 DAO와 분리 DBA에 의한 SQL 리뷰나 튜닝 필요시 해당 파일만 전달. SQL 내용을 변경하더라도 어플리케이션 코드나 DI 설정은 전혀 수정할 필요가 없어짐.

독립적인 파일 XML이 편리한 포맷

 

리소스 로더(ApplicationContext)가 인식할 수 있는 문자열로 표현해주면 된다.

Classpath는 디폴트이므로 생략 가능하다. 클래스 패스 루트부터 절대위치를 적어야함.

 

 

File: 을 사용하면 파일 시스템의 루트 디렉토리로부터 시작하는 파일의 위치를 나타냄.

 

HTTP 프로토콜로 접근 가능한 웹 리소스를 가져올 수도 있다.

 

* XML을 읽어서 Map에 보관하고 필요할 때 제공 (템플릿 인터페이스) 할 일이 2가지 이상일 때는 관심사의 분리가 필요함.

 

* XML을 읽어서 Map에 보관하고 필요할 때 제공 관심사의 구분 중 읽어서 보관하기 위한 인터페이스(전략)

- 리턴타입 void : map으로 리턴해줘도 되지만 범용적인 인터페이스가 map을 사용안한다고 한다면?? 바로 Registry에 저장해 주는 방법 채택

- 파일이 없는 경우 등 대부분 복구가 불가능한 예외로 판단하여 예외 없음.

 

> 위처럼 직접 구현해줘도 되지만, Spring unmarshaller를 사용하고, OxmService에서 직접 Reader 설정을 주입받는 장점이 있기에 미사용함

> OxmService와 하나의 클래스로 만들어 두기 때문에 빈의 등록과 설정은 단순해지고 쉽게 사용.

> OXM이 변경될 경우 자꾸 늘어나는 빈의 개수와 반복되는 DI구조가 불편해진다.

> 디폴트의존오브젝트로 생성하는 것도 방법이지만 OXM 중 하나에 종속되므로 좀더 유연한 설계를 위해 필요하다.

 

 

* XML을 읽어서 Map에 보관하고 필요할 때 제공 관심사의 구분 중 보관되어 있는 Map 중 요청한 쿼리를 제공해주기 위한 인터페이스(전략)

- 레지스트리에서 검색 실패 등으로 다른 레지스트리에 검색 시도 등 복구할 여지가 있어 예외를 던짐.

 

HashMap에 저장하여 키를 받으면 쿼리를 주는 HashMapSqlRegistry

 

프로퍼티 설정을 통한 초기화 작업을 제외하면 BaseSqlServiceOxmSqlService에 중복된다. 무시할 수도 있겠지만 만약 BaseSqlService의 코드를 재사용한다고 이를 상속해서 OxmSqlService를 만들면 멤버 클래스로 통합시킨 OxmSqlReader를 생성하는 코드를 넣기가 애매하다. 또는 중복을 제거하기 위해 loadSql()getSql() 메소드를 추출해서 슈퍼클래스로 분리하는 방법도 있겠지만, 이 정도 코드로는 복잡한 계층구조로 만들기도 부담스럽다. 그래서 이런 경우에는 그냥 간단한 코드의 중복쯤은 허용하고 BaseSqlService와는 독립적으로 OxmSqlService를 관리해나가도 크게 문제될 것은 없어 보인다. 그런데 이 두 개의 중복되는 코드를 가진 loadSql()getSql()의 수정이 필요할 때마다 양쪽을 함께 변경해야하니 부담되고 실수할 가능성도 높아진다. 이런 경우 위임 구조를 이용해 코드의 중복을 제거할 수 있다.

 

loadSql()getSql()의 구현 로직은 BaseSqlService에만 두고, OxmSqlService는 일종의 설정과 기본 구성을 변경해주기 위한 어댑터 같은 개념으로 BaseSqlService의 앞에 두는 설계가 가능하다. OxmSqlService의 외형적인 틀은 유지한 채로 SqlService의 기능 구현은 BaseSqlService로 위임하는 것이다. 위임을 위해서는 두 개의 빈을 등록할 수도 있다. 하지만 OxmSqlServiceBaseSqlService를 위임구조로 만들기 위해 두 개의 빈을 등록하는 것은 불편한 일이다. 부가기능 프록시처럼 많은 타깃에 적용할 것도 아니고, 특화된 서비스를 위해 한번만 사용할 것이므로 유연한 DI방식은 포기하고 OxmSqlServiceBaseSqlService를 한 클래스로 묶는 방법을 생각해보자.

 

 

기존 OxmSqlReader는 클래스패스로부터 리소스를 가져오기 위해 ClassLoadergetResourceAsStream() 메소드를 사용했다. 이를 파일 시스템이나 웹상의 HTTP를 통해 접근 가능한 파일로 바꾸려면 URL 클래스를 사용하도록 변경하거나, 또한 서블릿 컨텍스트 내의 리소스를 가져오려면 ServletContextgetResourceAsStream()을 사용해야 한다. 리소스의 위치나 종류에 따라서 다른 클래스와 메소드를 사용해야 한다는 점이 불편하다.

 

* 안타깝게도 자바에는 이렇게 다양한 위치에 존재하는 리소스에 대해 단일화된 접근 인터페이스를 제공해주는 클래스가 존재하지 않는다. 그나마 URL을 이용해 웹상의 리소스에 접근할 때 사용할 수 있는 java.net.URL 클래스가 있을 뿐이다. 하지만 자바의 클래스패스 안에 존재하는 리소스나 서블릿 컨텍스트의 리소스 또는 임의의 스트림으로 가져올 수 있는 리소스를 지정하는 방법이 없다는 점과 리소스 파일의 존재여부를 미리 확인할 수 있는 기능이 없다는 단점이 있다.

 

스프링은 자바에 존재하는 일관성 없는 리소스 접근 API를 추상화해서 Resource라는 추상화 인터페이스를 정의했다. 스프링은 거의 모든 API는 외부의 리소스 정보가 필요할 때는 항상 이 Resource 추상화를 이용한다.

 Resource는 빈으로 등록해야할까? 물론 그렇지 않다. 다른 서비스 추상화의 오브젝트와는 달리, Resource는 값으로 취급된다. 리소스는 OXM이나 트랜잭션 처럼 서비스를 제공해주는 것이 아니라 단순한 정보를 가진 값으로 지정된다. 빈으로 등록하는 것이 아닌 해봐야 <property>value 애트리뷰트에 넣는 방법밖에 없으며, 넣을 수 있는 건 단순한 문자열뿐이다.

 

스프링에는 URL 클래스와 유사하게 접두어를 이용해 Resource 오브젝트를 선언하는 방법이 있다. 문자열로 정의된 리소스를 실제 Resource 타입 오브젝트로 변환해주는 ResourceLoader를 제공한다. ResourceLoader도 구현이 다양할 수 있으므로 인터페이스로 정의해뒀다.

 

ResourceLoader의 대표적인 예는 바로 스프링의 ApplicationContext 이다. ApplicationContext ResourceLoader 인터페이스를 상속하고 있다. 따라서 모든 ApplicationContext는 리소스 로더이기도 하다.

 예를 들어 ApplicationContext가 사용할 스프링 설정정보가 담긴 XML 파일도 리소스 로더를 이용해 Resource 형태로 읽어온다. 그 밖에도 ApplicationContext가 외부에서 읽어오는 모든 정보는 리소스 로더를 사용하게 되어 있다. 또한 빈의 프로퍼티 값을 변환할 때도 리소스 로더가 자주 사용된다. 스프링이 제공하는 빈으로 등록 가능한 클래스에 파일을 지정해주는 프로퍼티가 존재한다면 거의 모두 Resource 타입이다. 따라서, 실제로 어디에 존재하는 어떤 종류인지 상관없이 동일한 방법으로 리소스의 내용을 읽어올 수 있다.

 

스프링 애플리케이션에서 파일을 읽거나 참조하는 기능을 만들 때는 Resource 타입의 추상화 기능을 이용하자.

 

48. SQL 매핑정보 소스의 타입을 Resource로 받는다.

58. 리소스의 종류에 상관없이 스트림으로 가져올 수 있다.

* Resource는 단지 리소스에 접근할 수 있는 추상화된 핸들러일 뿐이다. 실제 리소스가 아니다.

 

* OXM 템플릿 클래스

: SQL을 읽는 방법을 OXM으로 제한해서 사용성을 극대화 하는 것이 목적(OXM에서만 읽는 방법이기 때문) 전략은 구분하되 이를 스태틱 멤버 클래스로 내장하고 자신만이 사용. 밖에서 볼 때는 OxmSqlService 하나의 오브젝트로 보이지만 내부에서는 의존관계를 가진 두 개의 오브젝트가 깔끔하게 결합돼서 사용된다. 유연성은 조금 손해를 보더라도 내부적으로 낮은 결합도를 유지한 채로 응집도가 높은 구현을 만들 때 유용하게 사용할 수 있다.

* 자바 멤버 클래스

> 하나의 클래스로 만들어 두기 때문에 빈의 등록과 설정은 단순해지고 쉽게 사용.

> OXM이 변경될 경우 자꾸 늘어나는 빈의 개수와 반복되는 DI구조가 불편해진다.

> 디폴트의존오브젝트로 생성하는 것도 방법이지만 OXM 중 하나에 종속되므로 좀더 유연한 설계를 위해 필요하다.

 

OxmSqlServiceSqlReader를 멤버 클래스로 고정시켜서 OXM에 특화된 형태로 재구성했기 때문에 설정은 간결해지고 의도되지 않은 방식으로 확장될 위험이 없다. 그렇지만 BaseSqlServiceloadSql() getSql() 이라는 핵심 메소드 구현코드가 동일하다는 점이 꺼림직하므로 BaseSqlService 사용.

 

 

 

 

데코레이터패턴, 프록시, 트랜잭션을 위한 인터페이스.

트랜잭션  경계설정의 일원화(하나로 만듬)

비즈니스 로직을 담고 있는 서비스 계층 오브젝트에 트랜잭션 경계를 부여하기에 가장 적절한 대상이다.

예를 들어 UserService가 아니라면 UserDao를 직접 사용하지 않고, UserService를 사용하는 것이 바람직하다. 단순 조회나 간단한 수정이라면 직접 UserService외의 서비스 계층 오브젝트에서 UserDao를 사용해도 상관없다. 하지만 등록이나 수정, 삭제가 포함된 작업이라면 다른 모듈의 DAO를 직접 이용할 때 신중을 기해야 한다. 안전하게 사용하려면 다른 모듈의 서비스 계층을 통해 접근하는 방법이 좋다.(트랜잭션 모두 취소 혹은 모두 성공)

 

단순히 레코드 개수를 리턴하는 getCount()를 제외하면 나머지는 독자적인 트랜잭션을 가지고 사용될 가능성이 높다. 따라서 이 4개의 메소드를 추가함.

 

 

타겟인터페이스 구현

 

메일서버 과부하를 막기위한 테스트 스텁

 

DB 접속불가를 위한 목오브젝트를 이용한 테스트

 

 

목 오브젝트 생성과 add 테스트

 

 

트랜잭션 테스트

 

번거롭게 목, 스텁 오브젝트를 생성하지 않고, 목 프레임웍을 활용한 테스트