본문 바로가기

스프링

[spring] xml_springOxm_jaxen_추상화_응집도결합도_2

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이 편리한 포맷

 

 

XML을 읽어서 Map에 보관하고 필요할 때 제공하는 역할을 하는 OxmSqlService 기능을 구현해보자. 그 중 읽어서 보관하는 역할 중 SqlReaderOXM 언마샬러를 이용하도록 OxmSqlService 내에 고정시켜야한다. SQL을 읽는 방법을 OXM으로 제한해서 사용성을 극대화 하는 것이 목적(OXM에서만 읽는 방법이기 때문)이다. 하지만 OxmSqlServiceOXM 코드를 직접 넣어도 될까? 물론 그럴 수도 있다. 하지만 OxmSqlServiceOXM 기술에 의존적이라고 해서 꼭 OXM 코드를 직접 갖고 있을 필요는 없다. 이미 SqlReaderSqlRegistry라는 두 개의 전략을 활용하는 구조를 적용해봤으므로, 그대로 유지하되 SqlReader 구현 오브젝트에 대한 의존관계를 고정시켜버리는 방법을 생각해 볼 수 있다. 이 왕이면 구현 클래스를 OxmSqlService가 내장하게 하는 것도 좋겠다.

 

* 내부적으로 낮은 결합도(역할에 따른 관심사 분리 서로 알 필요가 없다.)를 유지한 채로 응집도가 높은 구현을 만들 때 유용하게 사용하는 방법

 

Spring에서 제공하는 Marshaller 사용하여 OXM 방식이 변경되어도 OxmSqlService의 코드의 수정없이, XML 설정만 교체해서 사용하기 위함.

 

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

 

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

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

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

 

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

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

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

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

 

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

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

 

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

 

* OXM 템플릿 클래스

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

* 자바 멤버 클래스

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

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

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

 

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

 

위처럼 OxmSqlReader는 외부에 노출되지 않고 OxmSqlService에 의해 만들어지고 스스로 빈으로 등록될 수 없다. 따라서 OxmSqlService에 의해 간접적으로 DI 받아야한다.

 이렇게 하나의 빈을 등록하는 것으로 충분할 때 사용됨. SqlRegistry는 필요에 따라 다른 구현으로 교체가능. OxmSqlService의 프로퍼티 중 unmarshallersqlmapFile은 내부의 OxmSqlReader가 사용.

 

앞서 배운 UserDao에서 JdbcTemplate를 직접 만들어 사용했던것과 비슷하다. 하지만 JdbcTemplate는 여러 DAO에서 사용 가능한 최상위 레벨 클래스이지만, OxmSqlReaderOxmSqlService에서만 사용하도록 제한한 멤버 클래스라는 점에 차이가 있다.

 

 

 

 

 

 

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

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

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

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

 

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

 

 

타겟인터페이스 구현

 

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

 

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

 

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

 

 

트랜잭션 테스트

 

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

 

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

 

 

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

 

이렇게 위임구조를 이용하면 OxmSqlService에 있던 중복 코드를 깔끔하게 제거할 수 있다. SqlReaderSqlRegistry를 활용해 SqlService를 제공하는 코드는 BaseSqlService에만 유일하게 존재한다. 이와 관련된 로직이 변경되면 BaseSqlService만 수정해주면 된다.

 

하지만, OxmSqlReaderXmlSqlReader에는 공통적인 문제점이 있다. XML 파일이 UserDao와 같은 클래스 패스에 존재하는 파일로 제한된다는 점이다. 루트 등에 있는 XML 파일을 읽으려면 어떻게 해야할까? 더 나아가서 http, ftp 프로토콜로 접근할 수 있는 웹상 리소스 파일을 가져올 수는 없을까? 다음편 리소스편 참조