'FRAMEWORK'에 해당되는 글 126건

  1. 2014.09.29 [Spring 레퍼런스] 1장 스프링 프레임워크 소개 #2
  2. 2014.09.29 [Spring 레퍼런스] 1장 스프링 프레임워크 소개 #1
  3. 2014.09.29 log4j.properties 설정
  4. 2014.09.29 Spring - 뷰 리졸버(View resolver)
  5. 2014.09.29 Spring - Transaction2 (선언적 트랜잭션 처리)
  6. 2014.09.29 Spring - Transaction1 (스프링의 트랜잭션 지원)
  7. 2014.09.29 Spring - POJO
  8. 2014.09.29 Spring - Bean 초기화 및 생명주기
  9. 2014.09.29 Spring - Bean Scope
  10. 2014.09.29 Spring - IoC & DI

[Spring 레퍼런스] 1장 스프링 프레임워크 소개 #2

FRAMEWORK/SPRING REF 2014. 9. 29. 14:08

이 포스팅은 다음의 스프링소스 라이센스를 따릅니다.

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.



1.3 사용 시나리오

앞에서 설정한 블락을 만드는 것은 많은 시나리오에서 스프링이 논리적인 선택을 하도록 한다. 여기서 많은 시나리오는 애플릿부터 스프링의 트랜잭션 관리 기능과 웹 프레임워크 통합을 사용하는 완전한 엔터프라이즈 어플리케이션까지를 포함한다.



대표적인 완전한 형태의 스프링 웹 어플리케이션


스 프링의 선언적 트랜잭션 관리 기능은 웹 어플리케이션이 EJB 컨테이너가 관리하는 트랜잭션처럼 완전한 트랙잭션 기능을 가지게 한다. 모든 커스텀 비즈니스 로직은 간단한 POJO로 구현할 수 있고 스프링의 IoC 컨테이너로 관리할 수 있다. 추가적인 서비스는 이메일을 보내는 기능을 지원하고 어디서 유효성 확인 규칙을 실행할 것인지 선택하는 웹 계층과는 독립적인 유효성 확인에 대한 지원을 포함한다. 스프링의 ORM 지원은 JPA, Hibernate, JDO, iBatis와 통합되었다. 예를 들면 하이버네이트를 사용할 때 이미 존재하는 매핑 파일과 표준 하이버네이트 SessionFactory 설정을 계속 사용할 수 있다. 폼(Form) 컨트롤러는 도메인 모델에 대한 값으로 HTTP 파라미터를 전달하는 ActionForms나 다른 클래스에 대한 필요성을 제거함으로써 도메인 모델과 웹 계층을 자연스럽게 통합한다.



서드파티 웹 프레임워크를 사용하는 스프링 미들-티어


종 종 다른 프레임워크로 완전히 전환하는 것이 어려울 수 있다. 스프링 프레임워크는 스프링 프레임워크안에서만 모든 일을 하도록 강제하지 않다. 스프링 프레임워크는 스프링 프레임워크만 쓰던가 아예 쓰지 않던가 해야 하는 솔루션이 아니다. 이미 만들어진 프레임워크가 WebWork, Struts, Tapestry나 다른 UI 프레임워크로 만들어졌더라도 스프링의 트랜잭션 기능을 사용하는 스프링 기반의 미들-티어와 통합할 수 있다. 간단하게 ApplicationContext를 사용하는 비즈니스 로직을 연결하고 웹 계층과 통합하기 위해 WebApplicationContext 를 사용하면 된다.



원격 사용 시나리오


이 미 존재하는 코드에 웹서비스로 접근할 필요가 있으면 스프링의 Hessian-, Burlap-, Rmi-나 JaxRpcProxyFactory 클래스를 사용할 수 있다. 존재하는 어플리케이션으로의 원격접근을 허용하는 것은 어렵지 않다.



EJB - 존재하는 POJO의 랩핑(Wrapping)


또 한, 스프링 프레임워크는 엔터프라이즈 자바빈, 존재하는 POJO의 재사용 허용, 선언적인 보안이 필요한 확장성 있고 안전한(fail-safe) 웹 어플리케이션에서 사용하는 무상태 세션빈을 감싸기 위해 접근과 추상화 계층을 제공한다.


1.3.1 의존성 관리와 작명 관례

의 존성 관리과 의존성 주입은 다르다. 스프링의 좋은 기능(의존성 주입 같은)을 어플리케이션에서 사용하려면 필요한 모든 라이브러리(jar 파일)를 조립하고 런타임시에, 혹은 가능하다면 컴파일할 클래스패스에 둘 필요가 있다. 이러한 의존성은 주입되는 가상 컴포넌트가 아니라 (전통적으로) 파일시스템에 존재하는 물리적 리소스이다. 의존성 관리의 프로세스는 이러한 리소스를 필요한 위치에 두고 저장하고 클래스 패스에 추가하는 작업이 포함한다. 의존성은 직접적일 수도(예를 들어 런타임시에 스프링에 의존하는 어플리케이션) 간적접일 수도(예를 들어 commons-pool기반의 commons-dbcp에 의존하는 어플리케이션) 있다. 간적인 의존성은 보통 과도기적이라고 부르기도 하는데 이러한 의존성은 구별하고 관리하기가 몹 어렵다.

스프링을 사용할 계획이라면 필요한 스프링 일부를 포함하는 jar 라이브러리의 복사본이 필요하다. 스프링은 의존성이 최대한 분리되도록 모듈이 패키징되어 있으므로 이 작업을 쉽게 할 수 있다. 그래서 예를 들어 웹 어플리케이션을 작성할 것이 아니라면 스프링-웹 모듈은 필요 없다. 이 가이드의 스프링 라이브러리 모듈을 참조하기 위해 spring-*나 spring-*.jar같은 짧은 작명 컨벤션을 사용한다. *은 모듈의 약칭을 의미한다. (예를 들어 spring-core, spring-webmvc, spring-jms등이다.) 실제 jar 파일명은 이 형식일 수도 있고(아래에서 볼 것이다) 아닐 수도 있으며 보통 파일명에 버전번호가 포함되어 있다.(예를 들면 spring-core-3.0.0.RELEASE.jar 와 같은 방식이다.)

보통 스프링은 결과물(Artifact)을 다음 네 곳에 배포한다.

  • 커뮤니티 다운로드 사이트http://www.springsource.org/downloads/community 다운로드받기 쉽도록 zip 파일로 묶인 모든 스프링 jar파일이 있다. 3.0 버전부터 이곳의 jar파일명은 org.springframework.*-<version>.jar 의 형식을 따른다.
  • 메이븐 센트럴(Maven Central). 메이븐 센트럴은 메이븐의 기본 저장소이므로 사용하기 위해서 특별한 설정을 할 필요는 없다. 스프링이 의존하는 공통 라이브러리 중 다수는 메이슨 센트럴에서 이용할 수 있고 스프링 커뮤니티의 많은 부분은 의존성 관리에 메이븐을 사용하므로 편리하다. 메이븐 센트럴의 jar 파일명은 spring-*-<version>.jar의 형식을 따르고 메이븐 groupId는 org.springframework이다.
  • SpringSource에서 운영하는 Enterprise Bundle Repository (EBR). EBR는 스프링에 통합하는 모든 라이브러리를 제공한다. 모든 스프링 jar와 의존성 라이브러리를 포함해서 그 외 스프링으로 어플리케이션을 개발할 때 사용하는 공통 라이브러리에 대한 메이븐 저장소와 아이비(Iby) 저장소를 모두 이용할 수 있다. 릴리즈 버전과 마일스톤을 포함해서 개발 스냅샷까지 이곳에 배포된다. jar 파일명은 커뮤니티 다운로드와 같은 형식(org.springframework.*-<version>.jar )을 사용하고 외부 라이브러리(스프링소스가 만들지 않은)는 com.springsource 접두사가 있는 긴 형식이다. 더 자세한 정보는 FAQ를 참고해라.
  • 개발 스냅샵과 마일스톤 릴리즈를 위해 아마존 S2에서 운영되는 공개 메이븐 저장소를 사용한다.(최종 릴리즈의 복사본도 배포된다.) jar 파일명은 메이븐 센트럴과 같은 형식이므로 메이븐 센트럴에 배포된 다른 라이브러리와 함께 스프링의 개발 버전을 사용할 때 유용하다.


그래서 어떻게 의존성을 관리할 것인지를 먼저 결정해야 한다. 대부분은 메이븐이나 아이비같은 자동화된 시스템을 사용한다. 하지만 수동으로 모든 jar를 다운받아서 관리할 수도 있다. 메이븐이나 아이비로 스프링을 다운받으려면 어디서 다운받을 지를 결정해야 한다. 보통 OSGi를 고려한다면 Hibernate나 Freemarker같은 스프링 의존성의 OSGi 호환 결과물이 제공되는 EBR을 사용한다. OSGi를 쓸 계획이 없다면 저장소마다 장단점이 있지만, 어느 곳을 사용하던 큰 문제는 없다. 대신 섞어서 사용하지는 마라. EBR은 메이븐 센트럴과는 작명 관례를 사용하므로 특히 주의해야 한다.

Table 1.1. 메이븐 센트럴과 스프링소스 EBR 저장소의 비교

기능Maven CentralEBR
OSGi 호환명확하지 않음호환
Artifact의 수모든 종류로 수천 가지스프링 통합과 관련된 것들로 수백 가지
작명 관례의 일관성일관성 없음일관성 있음
GroupId의 작명 관례다양함. 새로운 artifact는 종종 도메인 명을 사용한다. 예를 들면 org.slf4j. 오래된 것들은 그냥 artifact명을 사용한다. 예를 들면 log4j출처나 메인 패키지 루트의 도메인명. 예를 들어 org.springframework
ArtifactId의 작명 관례다양함. 보통 프로젝트나 모듈명으로 구분자로 하이픈("-")을 사용한다. 예를 들면 spring-core, logj4메 인 패키지 루트에서 유래된 번들 심볼릭명. 예를 들면 org.springframework.beans. jar가 OSGi 호환성 패치가 되었다면 com.springsource가 추가된다. 예를 들면 com.springsource.org.apache.log4j
버전의 작명관례다 양함. 새로운 artifact는 m.m.m이나 m.m.m.X를 사용한다. (m은 숫자고 X는 문자다.) 오래된 artifact는 m.m을 사용하거나 아예 다른 방식을 사용한다. 순서는 정의되어 있지만, 종종 따르지 않는 경우도 있으므로 너무 신뢰하지 않는 것이 좋다.OSGi 버전번호인 m.m.m.X를 사용한다. 예를 들면 3.0.0.RC3. 텍스트는 숫처럼 버전의 순서가 지켜지는 알파벳만 사용할 수 있다.
배포보통 rsync나 소스제어 업데이트를 통해 자동으로 이루어진다. 프로젝트 저자는 JIRA로 각 jar를 업로드 할 수 있다.수동 (스프링소스가 처리하는 JIRA)
품질 보증정책에 의해서 이루어짐. 정확도에 대한 책임은 프로젝트 저자한테 있다.상세한 OSGi manifest, 메이븐 POM, 아이비 metadata를 따름. QA는 스프링 팀이 진행한다.
호스팅Contegix. Sonatyle이 여러 미러사이트를 함께 펀딩함.스프링 소스가 펀딩하는 S3.
검색 유틸리티다양함http://www.springsource.com/repository
스프링소스 도구의 통합메이븐 의존성 관리로 STS에 통합됨메이븐, Roo, 클라우드 파운드리와 함께 STS로 광범위하게 통합됨



1.3.1.1 스프링 의존성과 스프링에 기반하는 의존성

스프링이 엔터프라이즈와 다른 외부 도구들에 대한 광범위한 통합과 지원을 제공하더라도 의도적으로 의존성을 최소한으로 유지해야 한다. 간단한 사용을 위해서라면 많은 jar 라이브러리를 다운받아 설치하지 말아야 한다.(자동으로 한다고 하더라도) 기본적인 의존성 주입을 사용하려면 오직 하나의 외부 의존성만 필요하고 이는 로깅을 위한 것이다.(로깅 옵션에 대한 더 자세한 내용을 아래를 봐라.)

다음은 스프링에 의존하는 애플리케이션 설정하는 필요한 기본적인 과정을 설명할 차례다. 먼저 메이븐과 아이비를 사용한다. 모든 경우에서 명확한 것이 없다면 의존성 관리 시스템의 문서를 참고하거나 예제코드를 봐라. - 스프링은 개발과정에는 의존성 관리를 위해 아이비를 사용하고 예제는 대부분 메이븐을 사용한다.


1.3.1.2 메이븐 의존성 관리

메이븐으로 의존성을 관리한다면 로깅 의존성을 명시할 필요도 없다. 예를 들어 어플리케이션 컨텍스트을 생성하고 어플리케이션 설정에 의존성 주입을 사용하려면 메이븐 의존성은 다음과 같을 것이다.

1
2
3
4
5
6
7
8
<dependencies>
  <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>3.0.0.RELEASE</version>
     <scope>runtime</scope>
  </dependency>
</dependencies>


이것이 전부다. 스프링 API를 거슬러서 컴파일할 필요가 없다면 scope를 런타임으로 선언할 수 있다. 이는 기본적인 의존성 주입의 유즈케이스다.

위의 예제에서 메이븐 센트럴의 작명 관례를 사용했으므로 메이븐 센트럴이나 스프링소스의 S3 메이븐 저장소에서 동작한다. S3 메이븐 저장소를 사용하려면(예를 들어 마일스톤이나 개발 스냅샷을 사용하기 위해) 메이븐 설정에 저장소의 위치를 명시해야 한다. 전체 릴리즈를 사용하려면 다음과 같이 작성한다.

1
2
3
4
5
6
7
<repositories>
  <repository>
     <id>com.springsource.repository.maven.release</id>
     <snapshots><enabled>false</enabled></snapshots>
  </repository>
</repositories>


마일스톤을 사용하려면 다음과 같이 작성한다.

1
2
3
4
5
6
7
<repositories>
  <repository>
     <id>com.springsource.repository.maven.milestone</id>
     <snapshots><enabled>false</enabled></snapshots>
  </repository>
</repositories>


개발 스냅샷을 사용하려면 다음과 같이 작성한다.

1
2
3
4
5
6
7
<repositories>
  <repository>
     <id>com.springsource.repository.maven.snapshot</id>
     <snapshots><enabled>true</enabled></snapshots>
  </repository>
</repositories>


스프링소스의 EBR을 사용하려면 의존성에 대해서 앞에서와는 다른 작명 관례를 사용해야 한다. 이름은 예하기 쉽다. 예를 들면 다음과 같다.

1
2
3
4
5
6
7
8
<dependencies>
  <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>org.springframework.context</artifactId>
     <version>3.0.0.RELEASE</version>
     <scope>runtime</scope>
  </dependency>
</dependencies>


저장소의 위치도 명시적으로 선언해야 한다.(URL부분만 중요하다).

1
2
3
4
5
6
<repositories>
  <repository>
     <id>com.springsource.repository.bundles.release</id>
  </repository>
</repositories>


직접 의존성을 관리한다면 위에서 선언한 저장소 URL은 검색하기에 좋지 않다. 하지만 http://www.springsource.com/repository에서 의존성을 검색하고 다운로드 받을 수 있는 화면을 제공한다. 메이븐이나 아이비를 사용한다면 쉽게 복사해서 붙일 수 있도록 메이븐과 아이비 설정 스니펫을 제공한다.


1.3.1.3 아이비(Ivy) 의존성 관리

의존성 관리를 Ivy로 하길 좋아하더라도 라이브러리 이름과 설정옵션은 비슷하다.

아이비를 설정하려면 ivysettings.xml에 스프링소스 EBR을 나타내는 다음 resolver를 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<resolvers>
   
  <url name="com.springsource.repository.bundles.release">
 
   <ivy pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
   <artifact pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
 
  </url>
 
  <url name="com.springsource.repository.bundles.external">
 
   <ivy pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
   <artifact pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
 
  </url>
 
</resolvers>


위의 XML은 라인이 너무 길어서 유효한 XML이 아니다. - 복사해서 붙다면 URL 가운데 줄 바꿈을 삭제해라

아이비가 EBR로 설정되면 의존성 추가는 쉽다. 저장소 검색페이지에서 원하는 라이브러리의 상세페이지에 들어가서 의존성 부분에 추가할 아이비 스니펫을 찾을 수 있다. 예를 들어 ivy.xml에 다음과 같이 추가한다.

1
<dependency org="org.springframework" name="org.springframework.core" rev="3.0.0.RELEASE" conf="compile->runtime"/>



1.3.2 로깅

스프링에서 로깅은 아주 중요한 의존성이다. 그 이유는 a) 로깅은 유일하게 강제적인 외부 의존성이고, b) 모든 개발자는 사용하는 도구에서 어떤 출력을 보기 원하고 c) 스프링은 로깅 의존성의 선택이 있는 다른 많은 툴과 통합되기 때문이다. 어플리케이션 개발자의 목표 중 하나는 외부 컴포넌트를 포함한 전체 어플리케이션의 중심에서 통일된 로깅을 설정하는 것이다. 이는 로깅 프레임워크가 많이 존재하기 때문에 쉽지 않다.

스프링의 강제적인 로깅 의존성은 Jakarta Commons Logging API (JCL)이다. JCL을 컴파일하고 스프링 프레임워크를 상속받은 클래스에서 사용할 수 있는 Log객체로 만든다. 이는 같은 로깅 라이브러리를 사용하는 모든 버전의 스프링 사용자에게 중요하다. 스프링은 스프링을 상속받은 어플리케이션에서조차 하위호환성이 유지되므로 마이그레이션이 쉽다. 마이그레이션을 하는 방법은 스프링의 모듈 중 하나를 명시적으로 commons-logging(JCL의 정식 구현체)에 의존하게 하고 다른 모듈은 컴파일 시에 의존하게 하는 것이다. 예를 들어 메이븐을 사용하는데 commons-logging의 의존성이 어디에 있는지 모른다면 스프링의 핵심모듈인 spring-core에 있다.

commons-logging의 좋은 점은 당신이 어플리케이션을 동작하게 하는 것에만 신경 쓰면 된다는 것이다. commons-logging에는 잘 알려진 클래스패스나 적절한 위치(필요하다면 지적할 수 있다.)에서 다른 로깅 프레임워크를 찾는 런타임 디스커버리 알고리즘이 있다. 이용할 수 있는 로깅 프레임워크가 없다면 JDK(정확히는 java.util.logging이나 JUL이다.)로 꽤 괜찮은 로그를 남긴다. 대부분 상황에서 로그가 잘 나오는 것을 확인해서 스프링이 잘 동작한다는 것을 알 수 있다. 이는 중요하다.


1.3.2.1 Commons Logging 사용하지 않기

안타깝게도 commons-logging의 런타임 디스커버리 알고리즘은 엔드유저에게는 편리하지만, 문제의 소지가 있다. 시간을 거꾸로 돌릴 수 있거나 스프링을 새로운 프로젝트로 다시 시작한다면 런타임 디스커버리 알고리즘은 다른 로깅 의존성을 사용할 것이다. 첫 번째 선택은 아마 Simple Logging Facade for Java (SLF4J)일 것이다. 어플리케이션에서 스프링과 함께 사용하는 많은 툴은 SLF4J를 사용한다.

commons-logging는 쉽게 사용하지 않게 할 수 있다. 그냥 런타임시에 클래스패스에 존재하지 않으면 된다. 스프링 의존성이 선언되어 있으므로 메이븐에서 의존성을 제외 처리하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependencies>
  <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>3.0.0.RELEASE</version>
     <scope>runtime</scope>
     <exclusions>
        <exclusion>
           <groupId>commons-logging</groupId>
           <artifactId>commons-logging</artifactId>
        </exclusion>
     </exclusions>
  </dependency>
</dependencies>


위 코드를 추가하면 어플리케이션의 빌드가 깨질 것이다. 클래스패스에 JCL API의 구현체가 없기 때문이다. 새로운 구현체를 추가하면 이 문제를 해결할 수 있다. 다음 섹션에서 예제를 통해 SLF4J를 사용하는 JCL 구현체를 새로 추가하는 방법을 살펴볼 것이다.


1.3.2.2 SLF4J의 사용

SLF4J는 의존성이 깔끔하고 commons-logging보다 런타임에서 더 효율적이다. 다른 로깅 프레임워크의 런타임 디스커버리 대신 컴파일할 때 바인딩하기 때문이다. 이는 런타임에서 벌어질 일을 더 명시적으로 지정해야 한다는 의미이다. 따라서 의존성을 선언하고 적절하게 설정해야 한다. SLF4J는 대부분의 로깅 프레임워크에 대한 바인딩을 지원하므로 기존에 사용하던 로깅 프레임워크를 계속 사용할 수 있다. 사용하는 로깅 프레임워크를 설정과 관리에 바인딩하면 된다.

SLF4J는 JCL을 포함한 많은 로깅 프레임워크에 대한 바인딩을 지원한다. 반대로 다른 로깅 프레임워크가 SLF4J로 바인딩할 수도 있다. 즉 다른 로깅프레임워크와 SLF4J사이에 브릿지로 사용할 수도 있다. 그래서 스프링과 SLF4J를 함께 사용하려면 commons-logging의존성을 SLF4J-JCL 브릿지로 바꿔야 한다. 설정을 바꾸었으면 스프링에서 호출된 로깅은 SLF4J API를 호출하는 로깅으로 전환된다. 그래서 어플리케이션의 다른 라이브러리가 같은 API의 로깅을 사용한다면 한 곳에서 로깅을 설정하고 관리할 수 있다.

스프링을 SLF4J로 브릿지하고 명시적으로 SLF4J를 Log4J로 바인딩하는 것이 가장 일반적이다. 브릿지, SLF4J API, Log4J로의 바인딩, Log4J구현체의 4가지 의존성을 추가해야 한다.(commons-logging는 제외 처리해야 한다.) 메이븐이라면 다음과 같이 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<dependencies>
  <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>3.0.0.RELEASE</version>
     <scope>runtime</scope>
     <exclusions>
        <exclusion>
           <groupId>commons-logging</groupId>
           <artifactId>commons-logging</artifactId>
        </exclusion>
     </exclusions>
  </dependency>
  <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>jcl-over-slf4j</artifactId>
     <version>1.5.8</version>
     <scope>runtime</scope>
  </dependency>
  <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>1.5.8</version>
     <scope>runtime</scope>
  </dependency>
  <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-log4j12</artifactId>
     <version>1.5.8</version>
     <scope>runtime</scope>
  </dependency>
  <dependency>
     <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.14</version>
     <scope>runtime</scope>
  </dependency>
</dependencies>


단순히 로깅을 사용하기 위해 너무 많은 의존성을 추가한 것처럼 보일 수 있다. 하지만 이는 선택사항이고 평범한 commons-logging보다 나은 동작을 한다. 특히 commons-logging는 OSGi 플랫폼 같은 엄격한 컨테이너를 사용한다면 클래스로더 이슈도 고려해야 한다. 또한, 바인딩이 런타임이 아닌 컴파일타임에 이뤄지기 때문에 성능적인 이점도 있다고 알려졌다.

더 적은 과정과 의존성을 사용하는 SLF4J 사용자들은 보통 Logback로 직접 바인딩한다. Logback은 SLF4J를 직접 구현했기 때문에 추가적인 바인딩 과정을 줄여준다. 그래서 4개가 아닌 2개의 라이브러리에만 의존할 수 있다.(jcl-over-slf4j와 logback) Logback으로 직접 바인딩하는 방법을 사용했다면 (스프링이 아닌) 다른 외부 의존성에서 slf4j-api에 대한 의존성을 제외처리 해야 한다. 이는 클래스패스에 오직 한가지 버전의 API만 존재하기를 바라기 때문이다.


1.3.2.3 Log4J의 사용

많은 사람들이 설정과 관리 때문에 로깅 프레임워크로 Log4j를 사용한다. Log4j는 사용하기 편하고 잘 구성되어 있다. 사실 스프링을 만들고 테스트할 때 런타임에서 Log4j를 사용했다. 스프링은 Log4j의 설정과 초기화를 위한 몇 가지 유틸리티도 제공한다. 그래서 스프링의 몇몇 모듈에는 Log4j에 대한 의존성이 컴파일타임에 선택적으로 의존성이 존재한다.

기본 JCL 의존성(commons-logging)과 함께 Log4j가 동작하게 하려면 클래스패스에 Log4j를 두고 클래스패스의 루트에 설정파일 (log4j.properties나 log4j.xml)을 두면 된다. 메이븐 사용자라면 의존성 선언에 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependencies>
  <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>3.0.0.RELEASE</version>
     <scope>runtime</scope>
  </dependency>
  <dependency>
     <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.14</version>
     <scope>runtime</scope>
  </dependency>
</dependencies>


다음은 콘솔에 로그를 출력하는 log4j.properties의 예제이다.

1
2
3
4
5
6
7
log4j.rootCategory=INFO, stdout
 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
 
log4j.category.org.springframework.beans.factory=DEBUG



네이티브 JCL를 사용하는 런타임 컨테이너

많은 사람들이 JCL의 구현체를 제공하는 컨테이너에서 스프링 어플리케이션을 구동한다. IBM 웹스피어(Websphere) 어플리케이션 서버(WAS)가 대표적이다. 웹스피어는 종종 문제를 일으키는데 불행히도 거기에는 완벽한 해결책이 없다. 대부분의 상황에서 단순히 어플리케이션에서 commons-logging를 제외 처리하는 것으로는 해결되지 않는다.

이 문제를 좀 더 명확히 보자. 알려진 문제점은 JCL이나 commons-logging자체의 문제는 아니다. 더 정확히 말하면 commons-logging를 다른 프레임워크(대개 Log4J)로 바인딩해서 발생한 문제다. 바인딩이 실패하는 이유는 몇몇 컨테이너가 사용하는 commons-logging의 과거 버전(1.0)과 대부분이 사용하는 최신 버전(1.1)사이에서 런타임 디스커버리 방법이 달라졌기 때문이다. 스프링은 JCL API에서 특이한 API는 전혀 사용하지 않으므로 문제가 발생하지 않는다. 하지만 스프링이나 어플리케이션이 로깅을 시도하자마자 Log4J로의 바인딩이 동작하지 않는 것을 발견할 수 있을 것이다.

WAS에서 이러한 상황을 겪을 가장 쉬운 해결책은 컨테이너 대신 어플리케이션이 JCL 의존성을 제어하도록 클래스 로더의 계층을 거꾸로 하는 것이다.(IBM은 이를 "parent last"라고 부른다.) 이 옵션은 항상 열려 있는 것은 아니지만, 대안적인 방법을 위한 공개된 영역에서 다수의 다른 제안이 존재한다. 아마 컨테이너의 정확한 버전과 기능에 의존하도록 바꿀 것이다.


관련 글

  1. 스프링 프레임워크 - 위키백과, 우리 모두의 백과사전
  2. 자바지기 Spring 프레임워크 강의 - Spring Framework - Confluence
  3. Play framework에서 Long Polling 구현하기 - Outsider's Dev Story
  4. SLF4J
  5. ivy, 330 George Street, Sydney - Merivale


:

[Spring 레퍼런스] 1장 스프링 프레임워크 소개 #1

FRAMEWORK/SPRING REF 2014. 9. 29. 14:05

오랫동안 자바와 스프링에 대해서 공부해야지 하면서도 사실 다른 기술들에 비해서 많은 시간은 투자하지 못했습니다. 어쨌든 작년부터는 스프링으로 업무를 하고 있기 때문에 올해는 맘먹고 스프링을 공부하기로 했습니다. 어떻게 스프링을 공부하는게 좋을까 고민을 많이 했습니다. 그래도 수많은 기술들을 만져봤지만 스프링 프레임워크자체가 꽤 크기 때문인지 전체적인 감을 잡기가 좀 어려웠습니다. 아무래도 시간을 많이 투자하지 못했기 때문이라고 생각합니다. 

예전과는 다르게 이제는 토비의 스프링 3라는 좋은 책이 생겼기 때문에 공부하기는 훨씬 용이해 졌다고 생각합니다. 토비의 스프링 3를 한번 보기는 했지만 제대로 스프링을 써보기 전에 공부했기 때문에 제대로 이해못한 부분도 있었는데 그래도 6개월 이상 스프링을 사용하고 있기 때문에 올해 스프링을 공부하면 이해도가 좀 더 높아질 꺼라 생각하고 있습니다. 토비님이 토비의 스프링 3에서 계속 러닝테스트를 강조하셨기 때문에 코딩량도 부족한 저에게는 좋은 학습방법이 될꺼라고 생각했습니다. 하지만 러닝테스트는 공부를 하면서 설명에 나온 기능을 직접 테스트로 구현하면서 파악해 보는 것이기 때문에 러닝테스트 작성외에 기능의 설명이 나열된 어떤 교재가 필요했습니다. 처음에는 토비의 스프링 3을 생각했지만 책도 아는 만큼 보이기 때문에 좀 더 스프링을 이해하고 보면 더 이해도가 높을꺼라고 생각해서 한번 더 보기전에 좀 더 스프링에 대한 감을 잡고 싶었습니다.

그래서 스프링 프레임워크 레퍼런스 가이드를 선택했습니다.  레퍼런스 가이드는 모든 기술에서 기본이 된다고 생각하고 있었기 때문에 조급해하지 말고 기분부터 다져가자는 생각으로 레퍼런스 가이드를 보기로 했습니다. 사실 700 페이지나 되는 분량에 좀 압도되기는 했지만 끝까지 다 보지 못한다고 하더라도 일단 해보기로 했습니다. 저는 영어문서를 읽을 때 가능하면 번역을 하면서 보기 때문에 번역한 내용을 블로그에 올립니다. 이 내용은 스프링을 공부하려는 목적으로 하는 번역이기 때문에 번역의 품질은 그다지 좋지 못합니다. 왠만한 기술 용어는 영문 용어를 그대로 사용했고  문장은 내용을 이해할 수 있는 정도 수준이면 크게 다듬지 않았기 때문에 상당수 번역문체가 존재할 수 있습니다. 그리고 스프링에 대한 이해도가 높지 않기 때문에 오역이 있을 수도 있습니다.

이 문서는 Github 저장소에서 관리합니다. 현재 레퍼런스 가이드는 3.1버전이지만 3.1의 내용이 전부 적용되어 있지는 않은것 같습니다. 나중에 레퍼런스 가이드가 업데이트되었을 때 비교해서 적용도 해야하기 때문에(가능하다면) 저장소로 관리하기로 했습니다. 이왕 하는거 저장소로 관리하기 시작했지만 레퍼런스 가이드의 분량이 엄청나기 때문에 끝까지 할 수 있을지 어느정도까지 하다가 멈출지는 저도 알 수 없습니다. 하다가 지쳐서 그만 둘 수도 있고 스프링 학습이 목적이기 때문에 학습에 별로 도움이 안된다고 느껴지거나 더 좋은 학습방법을 깨닫게 되면 언제든지 멈출 수 있습니다. 저는 git-flow의 브랜칭 모델을 따르기로 했기 때문에 번역한 내용은 master브랜치가 아닌 develop브랜치에 존재합니다. master 브랜치를 사용합니다.




Spring Framework Reference Documentation

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.



Part I. Spring Framework 개요

스프링 프레임워크는 엔터프라이즈급 애플리케이션을 만들기 위한 경량솔루션이며 많은 기능을 제공하고 있다. 하지만 스프링은 필요한 부분만 가져다 사용할 수 있도록 모듈화되어 있다. Struts를 IoC 컨테이너와 함께 사용할 수 있고 하이버네이트 통합 코드나 JDBC 추상화 계층만 사용할 수도 있다. 스프링 프레임워크는 RMI나 웹서비스로 로직에 원격접근하는 선언적 트랜잭션 관리와 데이터를 유지하는 다양한 옵션을 지원한다. 모든 기능을 갖춘 MVC 프레임워크를 제공하고 소프트웨어에 투명하게 AOP를 합할 수 있다.

스프링은 비침투적이다. 이 말은 도메인 로직 코드가 일반적으로 프레임워크에 의존적이지 않다는 의미이다. (데이터 접근 계층 같은)로 통합 계층에서 데이터 접근 기술과 스프링 라이브러리에 대한 의존성이 존재한다. 하지만 당신의 다른 코드들로부터 이러한 의존성을 쉽게 제거할 수 있을 것이다.

이 문서는 스프링 프레임워크에 대한 레퍼런스 가이드이다. 이 문서에 대한 어떤 요청이나 의견, 질문이 있다면 사용자 메일링 리스트나 http://forum.springsource.org/의 지원 포럼에 글을 올리면 된다.



1. 스프링 프레임워크 소개

스프링 프레임워크는 자바 애플리케이션 개발을 위한 포괄적인 인프라스트럭쳐를 제공하는 자바 플랫폼이다. 스프링은 당신이 애플리케이션에 집중할 수 있도록 인프라스트럭쳐를 다룬다.

스프링을 사용하면 "plain old Java objects"(POJOs)로 어플리케이션을 만들고 엔터프라이즈 서비스를 비침투적으로 POJO에 적용할 수 있다. 이 능력은 Java SE 프로그래밍에 적용되고 전제 혹은 부분적으로 Java EE에 적용된다.

어플리케이션 개발자인 당신이 어떻게 스프링 플랫폿의 이점을 사용할 수 있는가에 대한 예제가 있다.

  • 트랜잭션 API를 사용하지 않고도 데이터베이스 트랜잭션에서 자바메서드를 실행하도록 만든다.
  • 원격 API를 사용하지 않고도 로컬 자바메서드를 원격 프로시저로 만든다.
  • JMX API를 사용하지 않고도 로컬 자바메서드를 관리작업으로 만든다.
  • JMS API를 사용하지 않고도 로컬 자바메서드를 메시지 핸들러로 만든다.


1.1 의존성 주입(Dependency Injection)과 제어의 역전(Inversion of Control)

자바 어플리케이션은 -- 제한적인 에플릿부터 n티어 서버사이드 엔터프라이즈 어플리케이션까지 포함하는 의미다 -- 보통 어플리케이션에 적합한 형식으로 협력하는 객체로 이루어져 있다. 그래서 어플리케이션의 객체는 서로 의존성이 있다.

자바 플랫폼이 어플리케이션 개발에 관련된 풍부한 기능을 제공하더라도 기본적으로 구축된 블락들을 서로 밀착된 하나의 큰 구조로 만드는 기능은 부족하다. 아키텍처와 개발자가 이 작업을 해야 한다. 사실, 애플리케이션을 만드는 여러 가지 클래스와 오브젝트 인스턴스를 구성하가 위해 팩토리, 추상 팩토리, 빌더, 데코레이터, 서비스 로케이터같은 디자인 패턴을 사용할 수 있다. 하지만 이러한 디자인 패턴은 단순히 베스트 프티스에 이름을 주어 패턴이 무슨 일을 하고 어디에 적용할 수 있고 어떤 문제에 대한 것인지 등을 설명한 것뿐이다. 패턴은 어플리케이션에서 스스로 구현해야 하는 베스트 프렉티스를 형식화한 것이다.

스프링 프레임워크 제어의 역전 (IoC) 컴포넌트는 이러한 관심사에 접근한다. 즉, 서로 다른 컴포넌트들을 사용할 준비가 된 완전히 동작하는 애플리케이션을 구성하는 형식적인 의미를 제공한다.스프링 프레임워크는 자신의 어플리케이션으로 통합할 수 있는 퍼스트 클래스 객체를 형식화된 디자인 패턴으로 만들었다.수많은 조직과 협회는 신뢰할 수 있고 유지보수 가능한 어플리케이션을 만들려고 이 방법으로 스프링 프레임워크를 사용한다.

배경

“제어의 어떤 관점이 역전되었는가?” 마틴 파울러(Martin Fowler)는 2004년 자신의 사이트에서 제어의 역전(IoC)에 대한 의견을 말했다. 파울러는 원리가 더 명확하게 드러나도록 이름을 의존성 주입(Dependency Injection)으로 바꾸어야 한다고 제한했다.

IoC와 DI를 더 알고 싶다면 http://martinfowler.com/articles/injection.html에 올라온 파울러의 글을 참고해라.




1.2 모듈

스프링 프레임워크는 약 20개의 모듈로 구조화된 특징으로 이루어져 있다. 이러한 모듈들은 다음 다이어그램에 보이듯 코어 컨테이너, 데이터 접근/통합, 웹, AOP(관점지향 프로그래밍), 인스트루멘테이션(Instrumentation), 테스트로 그룹을 나눌 수 있다.



스프링 프로그램의 개요



1.2.1 코어 컨테이너

코어 컨테이너 는 코어(Core), 빈즈(Beans), 컨텍스트(Context), 표현언어 (Expression Language) 모듈로 이루어졌다.

코어와 빈즈 모듈은 IoC와 의존성 주입을 포함하는 프레임워크의 기본이 되는 부분을 제공한다. BeanFactory는 팩토리 패턴을 세련되게 구현했다. BeanFactory을 사용하면 프로그래밍 적으로 싱글톤을 구현할 필요가 없고 실제 프로그램 로직에서 의존성에 대한 설정과 명세를 분리할 수 있다.

컨텍스트 모듈은 코어와 빈즈 모듈에서 제공하는 단단한 기반위에 구성되었다. 이는 JNDI 등록과 유사한 프레임워크 스타일로 객체에 접근한다는 의미다. 컨텐스트 모듈은 빈즈 모듈의 특징을 상속받고 국제화, 이벤트 전파, 리소스-로딩, 서블릿 컨테이너 컨텍스트의 투명한 생성에 대한 지원을 추가한다. 또한 컨텍스트 모듈은 EJB, JMX, 기본적인 원격 같은 Java EE의 기능을 지원한다. ApplicationContext 인터페이스는 컨텍스트 모듈에서 중요하다.

표현언어 모듈은 런타임에서 객체 그래프를 조회하고 조작하는 강령한 표현언어이다. 표현언어 모듈은 JSP 2.1 명세에 명시된 것처럼 통합된 표현언어(unified EL)의 확장이다. 언어는 속성값을 설정하고 가져오는 기능과 속성 할당, 메서드 호출, 배열과 컬렉션과 인덱서의 컨텍스트 접근, 논리적/산술적 오퍼레이터, 이름있는 변수, 스프링의 IoC 컨테이너에서 이름으로 객체를 획득하는 기능을 지원한다. 일반적인 리스트 집합뿐 아니라 리스트의 투영과 선택도 지원한다.


1.2.2 데이터 접근/통합

데이터 접근/통합 계층은 JDBC, ORM, OXM, JMC, 트랜잭션 모듈로 이루어졌다.

JDBC모듈은 JDBC 추상화계층을 제공한다. 그래서 지루한 JDBC 코딩과 데이터베이스 벤더에 따라 다른 오류코드를 파싱할 필요가 없다.

ORM 모듈은 JPA, JDO , Hibernate, iBatis를 포함하는 인기 있는 객체-관계 매핑 API에 대한 통합계층을 제공한다. ORM 패키지를 사용하면 이러한 O/R매핑 프레임워크들을 앞에서 언급했던 선언적 트랜젝션 관리 같은 스프링의 다른 기능들과 함께 사용할 수 있다.

OXM 모듈은 JAXB, Castor, XMLBeans, JiBX, XStream에 대한 객체/XML매핑 구현을 지원하는 계층이다.

자바 메시징 서비스 (JMS) 모듈은 메시지를 생산하고 소비하는 기능을 포함한다.

트랜잭션 모듈은 특별한 인터페이스와 모든 POJO (plain old Java objects)의 클래스에 대한 트랜잭션 관리를 지원한다. 트랜잭션 관리는 프로그래밍 적으로 하거나 선언적으로 할 수 있다.


1.2.3 웹

웹 계층은 웹, 웹-서블릿, 웹-스트러츠, 웹-포틀릿 모듈로 이루어졌다.

스프링의 웹 모듈은 기본적인 웹-지향적인 통합기능을 제공한다. 웹-지향적인 통합기능은 멀티파트 파일 업로드 기능, 서블릿 리스너와 웹 지향적인 애플리케이션 컨텍스트를 사용한 IoC 컨테이너의 초기화를 말한다. 또한, 스프링의 원격기능에서 웹과 관련된 부분을 포함한다.

웹-서블릿 모듈은 웹 어플리케이션을 위한 스프링의 모델-뷰-컨트롤러 (MVC ) 구현을 포함한다. 스프링의 MVC 프레임워크는 도메인 모델코드와 웹 폼을 깔끔하게 분리할 수 있도록 하고 스프링 프레임워크의 다른 모든 기능과 통합할 수 있게 한다.

웹-스트러츠(Web-Struts) 모듈은 스프링 어플리케이션에서 전통적인 스트러츠 웹티어를 통합을 지원하는 클래스를 포함한다. 이 지원은 스프링 3.0에서는 폐기되었다. 어플리케이션을 스트러츠 2.0이나 스트링 통합으로 마이그레이션 하던가 스프링 MVC를 사용하는 것을 고려해라.

웹-포틀릿(Web-Portlet) 모듈은 포틀릿 환경에서 사용되는 MVC 구현과 웹-서블릿 모듈 기능의 미러 기능을 제공한다.


1.2.4 AOP와 인스트루멘테이션(Instrumentation)

스프링의 AOP 모듈은 AOP Alliance를 따르는 관점지향 프로그래밍의 구현체다. 예를 들어 기능적으로 분리되어야 하는 코드를 깔끔하게 분리하는 메서드-인터셉터와 포인트컷을 정의할 수 있다. 소스레벨의 메타데이터 기능을 사용하면 .NET 어트리뷰트와 유사한 방법으로 행동에 관한 정보를 코드로 구현할 수 있다.

분리된 관점(Aspects) 모듈은 AspectJ와의 통합을 제공한다.

인스트루멘테이션(Instrumentation) 모듈은 인스트루멘테이션을 지원하는 클래스와 특정 어플리케이션 서버에서 사용되는 클래스로더 구현체를 제공한다.


1.2.5 테스트

테스트 모듈은 JUnit이나 TestNG로 스프링 컴포넌트의 테스트를 지원한다. 테스트 모듈은 스프링 어플리케이션 컨텍스트의 안정된 로딩과 이러한 컨텍스트의 캐싱을 제공한다. 또한, 코드를 격리된 상태로 테스트하기 위해 사용할 수 있는 모의 객체를 제공한다.



관련 글

  1. 스프링 프레임워크 강좌, 프로그래밍 추천강의 교육안내 - YouTube
  2. 사악미소의 현대마법의 공방 :: [Spring] 스프링 프레임 워크란?
  3. 훈군 :: 토비의 스프링3 - 5장 요약
  4. [한빛미디어] Spring 프레임워크 워크북
  5. OKJSP: 스프링 프레임워크 샘플 가장 쉬운 시작


:

log4j.properties 설정

FRAMEWORK/SPRING MVC 2014. 9. 29. 13:37

* 설정 준비

- log4j-1.x.xx.jar를 복사해서 eclipse/workspace/<프로젝트명>/WEB-INF/lib에 붙여넣기

- eclipse/workspace/<프로젝트명>/WEB-INF/src에 log4j.properties를 작성

- eclipse/workspace/<프로젝트명>/WEB-INF/web.xml에 아래 부분 추가

<init-param>

      <param-name>log4j-configuration</param-name>

      <param-value>/WEB-INF/src/log4j.properties</param-value>

</init-param>


-> 굳이 /WEB-INF/src에 log4j.properties를 작성하지 않아도 되는데, 이 properties 파일 위치를 바꿀 경우 web.xml의

param-value의 값도 바꾼 위치에 맞게 설정해준다.


자바 프로그램의 로깅에서 사실상 표준의 위치에 있는 Log4J의 설정 파일은 프로퍼티 형태의 log4j.properties와 XML 형태의 log4j.xml이 있다. 

XML 버전이 조금 늦게 나왔지만 향후에는 XML 설정파일만 지원하게 될 것이라는 얘기도 있지만, 프로퍼티 설정파일이 Java의 deprecated 메소드가 여전히 남아있는 것처럼 아주 없어질 것 같지는 않다.

한 가지 유의할 점은 Log4J가 자동 설정파일을 찾을 때 클래스패스에서 log4j.xml 파일을 먼저 찾고, 이 xml 파일이 없을 경우에만 log4j.properties를 찾는다는 것이다. 


* log4j.properties의 사용 예제

#로그설정(로그레벨 DEBUG ~ FATAL, OFF일경우 로그사용 안함), 사용 로그 이름
log4j.rootLogger=DEBUG, stdout, rolling


#stdout 로그 레벨 설정

log4j.appender.stdout.Threshold=WARN

Log Level
TRACE : 가장 상세한 정보를 나타낼 때 사용한다.
DEBUG : 일반 정보를 상세히 나타낼 때 사용한다.
INFO  : 일반 정보를 나타낼 때 사용한다.
WARN  : 에러는 아니지만 주의할 필요가 있을 때 사용한다.
ERROR : 일반 에러가 일어 났을 때 사용한다.
FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용한다.


  FATAL > ERROR > WARN > INFO > DEBUG > TRACE


#stdout 콘솔 어펜더로 사용
log4j.appender.stdout=org.apache.log4j.ConsoleAppender


Appender

 Appender 설 명
 org.apache.log4j.AsyncAppender 비동기 출력 - 네트워크 전송등 조금 특수한 용도에 사용된다. 로그 이벤트를 queue에 모은후 다른 쓰레드에서 스케줄로 로그를 출력해주는것이다. 다른 Appender와 결합해서 사용한다.
 org.apache.log4j.ConsoleAppender

 stdout, stderr 출력 - Console

 org.apache.log4j.DailyRollingFileAppender 지정한 시간단위로 파일 출력- File
 org.apache.log4j.varia.ExternallyRolledFileAppender 외부 Roller로 출력
 org.apache.log4j.FileAppender

 파일 출력 - File

 org.apache.log4j.jdbc.JDBCAppender 데이터베이스로 출력
 org.apache.log4j.net.JMSAppender JMS로 출력
 org.apache.log4j.lf5.LF5Appender LogFactor5라는 스윙 로그뷰어로 출력
 org.apache.log4j.nt.NTEventLogAppender Windows 이벤트 로그로 출력
 org.apache.log4j.varia.NullAppender 아무것도 안함
 org.apache.log4j.RollingFileAppender 파일 크기단위로 파일 출력 - File
 org.apache.log4j.net.SMTPAppender 메일로 출력
 org.apache.log4j.net.SocketAppender 외부 서버에 Socket으로 출력
 org.apache.log4j.net.SocketHubAppende SocketServer로서 출력
 org.apache.log4j.net.SyslogAppender Unix Syslog로 출력
 org.apache.log4j.net.TelnetAppender#FAE0D4


#stdout 패턴 레이아웃 사용
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout


Layout - 로그 출력을 어떤 형식으로 할지 구체적으로 지정해주는 부분

  org.apache.log4j.SimpleLayout

 기본 레이아웃

  org.apache.log4j.TTCCLayout 

 시간 출력에 특화된 레이아웃

  org.apache.log4j.HTMLLayout

 HTML형식으로 출력. 테이블 형식으로 각각의 로그를 출력한다.

  org.apache.log4j.XMLLayout

 XML형식으로 출력

  org.apache.log4j.PatternLayout

 사용자 마음대로 패턴을 지정하여 출력


#stdout 레이아웃 패턴 정의
log4j.appender.stdout.layout.ConversionPattern=%t> [%d{yyyy-MM-dd HH:mm:ss}] [%c{1}] [%L] [%p] %m %n


Options
%d : 로깅 이벤트가 일어난 날자(date)
%p : 로깅 이벤트의 priority
%t : 로깅 이벤트를 생성한 스레드 이름
%c : 로깅 이벤트의 category
%F : 로깅요청을 일으킨 파일 이름
%L : 로깅요청을 일으킨 파일의 행번호
%x : 로깅이벤트를 발생시킨 스레드에 관련된 내포검사항목  (Nested Diagnostic Context : NDC)을 출력
%C : 로깅요청을 일으킨 호출자의 완전한 클래스이름
%M : 로깅요청을 일으킨 메소드
%m : 메세지
%n : 플랫폼 독립적인 개행문자
%l : 소스코드의 위치정보를 출력한다. %C. %M(%F:%L) 의 축약형


#롤링파일 Appender 사용
log4j.appender.rolling=org.apache.log4j.RollingFileAppender
#롤링파일 로그에 대한 파일명 (경로 설정 가능 함)
log4j.appender.rolling.File=output.log

# Log File 뒤에 날짜 패턴 추가
log4j.appender.rolling.DatePattern='.'yyyyMMdd

# Tomcat Restart시 새로 작성 여부 (true - 기존파일에 추가, false - 새로 작성)
log4j.appender.rolling.Append=true

#로그파일 최대 크기
log4j.appender.rolling.MaxFileSize=1KB
#최대파일이 넘어갔을 경우 백업
log4j.appender.rolling.MaxBackupIndex=1
#롤링파일 패턴 레이아웃 사용
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
#롤링파일 패턴 정의
log4j.appender.rolling.layout.ConversionPattern=%t> [%d{yyyy-MM-dd HH:mm:ss}] [%c{1}] [%L] [%p] %m %n



:

Spring - 뷰 리졸버(View resolver)

FRAMEWORK/SPRING MVC 2014. 9. 29. 13:36

뷰 리졸빙(View resolving)

사용자에게 결과를 랜더링하여 보여주기 위하여 사용


뷰 리졸버 구현체

 뷰 리졸버

 설명

 BeannameViewResolver

 논리적 뷰 이름과 동일한 ID를 갖는 <bean> 으로 등록된 View의 구현체를 찾는다.

 ContentNegotiatingViewResolver

 요청되는 콘텐츠 형식에 기반을 두어 선택한 하나 이상의 다른 뷰 리졸버에 위임한다.

 FreeMarkerViewResolver

 FreeMarker 기반의 템플릿을 찾는다.

 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성

 InternalResourceViewResolver

 웹 어플리케이션의 WAR 파일 내에 포함된 뷰 템플릿을 찾는다.

 뷰 템플릿의 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성

 JasperReportsViewResolver

 Jasper Reports 리포트 파일로 정의된 뷰를 찾는다.

 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성

 TilesViewResolver

 Tiles 템플릿으로 정의된 뷰를 찾는다.

 템플릿 이름은 논리적 뷰 이름에 접두어와 접미어를 붙여 구성

 UrlBasedViewResolver

 ViewResolver의 구현체로 특별한 맵핑 정보 없이 view 이름을 URL로 사용

 View 이름과 실제 view 자원과의 이름이 같을 때 사용할 수 있다.

 ResourceBundleViewResolver

 ViewResolver 의 구현체로 리소스 파일을 사용합니다.

 views.properties 를 기본 리소스 파일로 사용합니다.

 VelocityLayoutViewResolver

 VelocityViewResolver의 서브클래스로, 스프링의 VelocityLayoutView를 통해 페이지 구성을 지원

 VelocityViewResolver

 Velocity 기반의 뷰를 찾는다.
 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성

 XmlViewResolver

 ViewResolver 의 구현체로 XML파일을 사용합니다.

 /WEB-INF/views.xml 을 기본 설정파일로 사용합니다.

 XsltViewResolver

 XSLT기반의 뷰를 찾는다. 

 XSLT스타일시트의 경로는 논리적 뷰 이름에 접두어와 접미어를 붙여 구성






ViewResolver들의 상위에 있는 AbstractCachingViewResolver 이 클레스가 캐슁 기능을 제공하기 때문에 이 클레스의 하위 클레스들은 엄청난 성능 향상을 맛볼 수 있다. 캐슁 기능을 끄고 싶을 때는 cache 속성을 false로 하면 된다.
런타임시 특정 view를 다시 읽어야 한다면 removeFromCache(String viewName, Locale loc) 메소드를 사용할수 있다.


Resolver 사용 예제

1. UrlBasedViewResolver

ViewResolver 의 구현체로 특별한 맵핑 정보 없이 의미상 view 이름을 URL로 사용

View 이름과 실제 view 자원과의 이름이 같을 때 사용

<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">

    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>

viewResolver는 사용자에게 보여줄 view를 생성할 때, prefix와 suffix를 지정해 줄 수 있다. 

만약 controller에서 넘겨준 modelAndView 값이 index이라면, 위의 정의에 따라 viewResolver는 "/WEB-INF/index.jsp"를 찾는다.


2. InternalResourceViewResolver

UrlBasedViewResolver 를 상속 받았으며 InternalResourceView(Servlet, JSP)를 사용

<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
  <property name="prefix" value="/WEB-INF/jsp/"/>
  <property name="suffix" value=".jsp"/>
</bean>

UrlBasedViewResolver를 상속받아 사용은 유사하다. 

참고적으로 JSTL을 사용하려면 위의 viewClass를 정의해주면 된다.


3. ResourceBundleViewResolver

ViewResolver 의 구현체로 리소스 파일 사용(views.properties 를 기본 리소스 파일로 사용)

다른 View 리졸버는 항상 논리적인 View 이름을 사용하여 단일한 View 구현 객체를 결정하는 반면, ResourceBundleViewResolver는 사용자의 Locale을 기초로 하여 동일한 논리적인 뷰 이름으로 서로 다른 View 구현 객체를 리턴 할 수 있다.


.properties 의 문법

viewname.class = class-name

viewname.url = view-url


viewReslover 정의

<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename" value="views"/>       // view.properties에 기술 
    <property name="defaultParentView" value="parentView"/>

    // views.properties 파일에 정의되지 않을 경우에는 parentView 에 정의된 사항을 따른다.
</bean>


views.properties 리소스 파일

bookView.class = org.springframework.web.servlet.view.JstlView
bookView.url = WEB-INF/jsp/book/bookView.jsp

bookEdit.class = org.springframework.web.servlet.view.JstlView
bookEdit.url = WEB-INF/jsp/book/bookEdit.jsp


Controller 작성

public class TestController{

public ModelAndView index(HttpServletRequest request, HttpServletResponse response){

Map model = new HashMap();


return new ModelAndView("bookview", "model", model);

}

}

viewResolver는 사용자에게 보여줄 view를 생성할 때, views.properties에서 정의된 URL을 참조한다.

만약 views.properties파일에 원하는 정보가 없다면 parentView.properties파일을 참조하게 된다.


4. XmlViewResolver

ViewResolver 의 구현체로 XML파일 사용(/WEB-INF/views.xml 을 기본 설정파일로 사용)

BeanNameViewResolver와 마찬가지로  뷰 이름과 동일한 이름을 갖는 빈을 뷰 객체로 사용


<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>


<bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="order" value="1"/>
    <property name="location" value="/WEB-INF/simpleviews.xml"/>
</bean>


simpleviews.xml

<beans>
    <bean name="report" class="org.springframework.example.ReportExcelView"/>
</beans>


5. TilesViewResolver

런타임 시 전체 페이지에 조립되는 페이지의 조각을 배치하기 위한 템플릿 프레임워크


Tiles 2를 이용하기 위해서는 UrlBasedViewResolver를 정의한 후 viewClass를 org.springframework.web.servlet.view.tiles2.TilesView로 정의

<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesVIew"/>
</bean>


Tiles 매핑 관련 정보가 작성되어 있는 tiles definition 파일의 위치를 정의

TilesViewResolver자체는 타일즈 정의에 대하여 아무런 정보를 가지지 않는다. 대신 해당 정보를 추적하는 TileConfigurer에 의존한다. 따라서 tiles definition의 정보를 아래와 같이 정의한다.

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TIlesConfigurer">
    <property name="definitions">

        <list>

            <value>/WEB-INF/view.xml</value>

        </list>

</bean>


view.xml

Tiles를 사용하기 위해서는 실제 Controller에서 리턴된 view 이름을 토대로 페이지에 출력해줄 tiles attribute를 정의해주는 tiles definition을 정의

<definition name="template" template="/sample/layouts/main_template.jsp">  // 공통 레이아웃 정의

    <put-attribute name="header" value="/sample/layouts/top.jsp" />

    <put-attribute name="body" value="/sample/layouts/welcome.jsp" />

    <put-attribute name="footer" value="/sample/layouts/left.jsp" />

</definition>

<definition name="home" extends="template">                             // home 타일 정의

    <put-attribute name="body" value="/sample/view/home.jsp" />

</definition>


먼저 Layout을 정의한 jsp 페이지를 정의한다. 

해당 main_template.jsp 페이지에서 기본적으로 사용할 페이지 구성 요소(위의 예에선 header, body, footer)들을 정의한 후 다른 view들은 미리 정의된 template이라는 definition을 extends하여 body만 설정하여 사용할 수 있다. 

위의 예에서 home라는 이름의 view가 리턴될 경우 "/sample/view/home.jsp" 페이지의 레이아웃으로 

header에는 "/sample/layouts/top.jsp" body는 "/sample/view/home.jsp", footer는 "/sample/layouts/left.jsp"이 될 것이다. 


JSP에서 tiles 구성 요소를 넣을 때는 아래와 같이 tiles taglib을 정의한 후 <tiles:insertAttribute> 태그를 이용하여 사용한다.


6. 다수의 ViewResolver 설정

하나의 DispatcherServlet은 한 개 이상의 ViewResolver 설정 가능

order 프로퍼티를 이용하여 뷰 이름을 검사할 ViewResolver의 순서를 결정 (order프로퍼티의 값이 작은 것이 우선)

우선순위를 명시하지 않으면 Integer.MAX_VALUE를 order 프로퍼티의 값으로 가진다.

우선순위가 높은 ViewReslover가 null을 리턴하면 그 다음 우선순위를 갖는 ViewResolver에 뷰를 요청


<bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="order" value="0"/>
    <property name="location" value="/WEB-INF/simpleviews.xml"/>
</bean>


<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedXmlViewResolver">

    <property name="order" value="1"/>

    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>


우선순위 결정 시 주의점

InternalResourceViewResolver는 항상 뷰 이름이 매핑되는 뷰 객체를 리턴하므로 마지막 순위를 갖도록 지정 (null은 리턴하지 않는다.)

VelocityViewResolver와 VelocityLayoutViewResolver의 경우 둘 다 뷰 이름에 매필되는 Velocity 템플릿 파일이 존재 하지 않을 경우 null을 리턴하지 않고 예외를 발생 시킴 (VelocityViewResolver 역시 가장 낮은 순위를 갖도록 한다.)


참조 :

http://whiteship.tistory.com/819

http://linuxp.tistory.com/entry/Spring-Framework%EC%9D%98-ViewResolver

http://dev.anyframejava.org/docs/anyframe/4.1.0/reference/html/ch11.html

http://ingenuity.egloos.com/m/3108539

http://blog.naver.com/PostView.nhn?blogId=apchima&logNo=80159530985

:

Spring - Transaction2 (선언적 트랜잭션 처리)

FRAMEWORK/SPRING MVC 2014. 9. 29. 13:36

선언적 트랜잭션

Transaction Template와 달리 트랜잭션 처리를 코드에서 직접적으로 수행하지 않음

설정 파일이나 어노테이션을 이용하여 트랜잭션의 법위, 롤백 규칙 등을 정의


선언적 트랜잭션 정의 방식

1. <tx:advice> 태그를 이용하여 트랜잭션 처리

2. @Transaction 어노테이션을 이용한 트랜잭션 설정

3. TransactionProxyFactoryBean 태그를 이용한 트랜잭션 처리 

(2.0 이전 빈마다 프록시를 만들어서 사용하였으나 현재는 많이 사용되지 않는다.)


트랜잭션 특성

선언적 트랜잭션은 트랜잭션 특성으로 정의된다.

트랜잭션 특성이란?  특정 메소드에 어떻게 적용되어야 하는지에 대한 정책을 기술한 것

전파방식, 격리수준, 읽기전용힌트, 타임아웃, 롤백규칙으로 정의된다.


<tx:advice> 태그를 이용한 트랜잭션 처리

<tx:advice> 태그를 이용해서 트랜잭션 속성을 정의하기 위해서는 먼저 tx 네임스페이스를 추가.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema.beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource" />

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED" />

            <tx:method name="*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>
    ...
 </beans>


tx네임스페이스를 추가하여 <tx:advice>태그, <tx:attributes>태그, <tx:method>태그 이용으로 트랜잭션 속성 정의


<tx:advice> 태그

트랜잭션을 적용할 때 사용될 Advisor를 생성

id속성 - 생성 될 트랜잭션 Advisor의 식별 값 입력

transaction-manager속성 - 스프링의 PlatformTransactionManager 빈을 설정


<tx:method> 태그

트랜잭션을 설정할 메소드 및 트랜잭션 속성을 설정

<tx:attributes>태그의 자식 태그로 설정

name 속성 

- 트랜잭션이 적용될 메소드의 이름을 명시.  

- "*"을 사용한 설정이 가능 ("get*" 으로 설정한 경우 이름이 get으로 시작하는 메소드을 의미)

propagation 속성 - 트랜잭션 전파 규칙을 설정

isolation 속성 - 트랜잭션 격리 레벨을 설정

read-only 속성 - 읽기 전용 여부를 설정

no-rollback-for 속성 - 트랜잭션을 롤백하지 않을 예외 타입을 설정

rollback-for 속성 - 트랜잭션을 롤백할 예외 타입을 설정

timeout 속성 - 트랜잭션의 타임 아웃 시간을 초 단위로 설정


트랜잭션 적용

<tx:advice> 태그는 Advisor만 생성하는 것일뿐, 실제로 트랜잭션을 적용하는 것은 아니다.

트랜잭션의 적용은 AOP를 통하여 이루어짐

 <aop:config>
    <aop:advisor 

        pointcut="execution(* *...service.*(..))"

        advice-ref="txAdvice" />
 </aop:config>

- pointcut 어트리뷰트는 AspectJ 포인트 컷 표현식을 이용하여 service의 모든 메소드를 어드바이스 해야 한다고 정의

- 어떤 트랜잭션 특성이 어떤 메소드에 적용되는지는 트랜잭션 어드바이스에 정의되어 있으며, 이는 advice-ref 어트리뷰트에 txAdvice라는 이름으로 참조하고 있다는 것을 의미한다.



1. 전파방식(propagation behavior)

- 호출되는 메소드와 클라이언트에 대하여 트랜잭션의 경계를 정의

  전파방식

 의미

  MANDATORY

 호출 전에 반드시 진행 중인 트랜잭션이 존재해야 한다.
 진행 중인 트랜잭션이 존재하지 않을 경우 예외 발생

  NESTED

 이미 진행 중인 트랜잭션이 존재하면 중첩된 트랜잭션에서 실행되어야 함을 나타낸다.

 중첩된 트랜잭션은 본 트랜잭션과 독립적으로 커밋되거나 롤백될 수 있다. 만약 본 트랜잭션이 없는 상황이라면 이는 PROPAGATION_REQUIRED와 동일하게 작동한다.

 그러나 이 전파방식은 벤더 의존적이며, 지원이 안되는 경우도 많다.

  NEVER

 트랜잭션 진행 상황에서 실행 될 수 없다.

 만약 이미 진행 중인 트랜잭션이 존재하면 예외 발생

  SUPPORTED

 트랜잭션이 없는 상황에서 실행

 만약 진행 중인 트랜잭션이 있다면 해당 메소드가 반환되기 전까지 잠시 중단한다.

  REQUIRED

 트랜잭션 상황에서 실행되어야 한다.

 진행 중인 트랜잭션이 있다면 이 트랜잭션에서 실행된다.

 없는 경우에는 트랜잭션이 새로 시작된다.

  REQUIRED_NEW

 자신만의 트랜잭션 상황에서 실행되어야 한다. 

 이미 진행 중인 트랜잭션이 있으면 그 트랜잭션은 해당 메소드가 반환되기 전에 잠시 중단된다.

  SUPPORTS

 진행 중인 트랜잭션이 없더라도 실행 가능하고, 트랜잭션이 있는 경우에는 이 트랜잭션 상황에서 실행된다.


2. 격리수준(isolation level)

- 한 트랜잭션이 동시 진행 중인 다른 트랜잭션의 영향을 얼마나 받는지 정의

  격리수준

  의미

  DEFAULT

  하부 데이터 저장소의 기본 격리 수준을 이용

  이는 데이터 저장소마다 다르다.

  READ_UNCOMMITTED

  커밋되지 않은 데이터 변경사항을 읽을 수 있다.
  더티읽기, 반복할 수 없는 읽기, 팬텀읽기를 허용

  READ_COMMITTED

  다른 트랜잭션에 커밋된 변경사항만 읽을 수 있다.
  더티읽기는 방지되지만, 반복할 수 없는 읽기, 팬텀읽기는 허용

  REPEATABLE_READ

  같은 데이터 필드는 여러 번 반복해서 읽더라도 동일한 값을 읽도록 한다.

  자신이 변경한 필드는 변경된 값을 읽는다.

  더티읽기와 반복할 수 없는 읽기는 방지되지만, 팬텀읽기는 허용

  SERIALIZABLE

  완전한 ACID를 보장하는 격리 수준
  보통 해당 트랜잭션에 관련된 모든 테이블을 잠그기 때문에 성능이 가장 비효율적인 격리 수준이다.


더티읽기 - 한 트랜잭션에서 다른 트랜잭션에 의해 변경될 수 있지만 아직 변경되지 않은 데이터를 읽게 되는 문제

   - 데이터가 커밋되지 않고 롤백되어 버린다면, 첫 번째 트랜잭션에서 읽은 이 데이터는 유효하지 않은 데이터가 된다.

반복할 수 없는 읽기 - 트랜잭션이 같은 질의를 두 번 이상 수행할 때 서로 다른 데이터를 얻게되는 문제

   - 각 질의 수행 사이에 동시 진행 중인 다른 트랜잭션에서 이 데이터를 변경하는 경우 발생

팬텀읽기 - 반복할 수 없는 읽기와 유사하지만 차이가 있따.

   - 팸텀읽기는 어떤 트랜잭션(T1)이 둘 이상의 데이터 행을 읽은 다음, 동시 진행 중인 다른 트랜잭션(T2)이 추가 행을

     삽입할 때 발생한다. T1에서의 동일한 질의를 다시 수행하면, T1은 이전에는 없던 데이터 행까지 읽게 된다.


3. 읽기전용

- 트랜잭션이 하부의 데이터 저장소에 대해 오직 읽기 작업만 수행하고, 수정등의 작업은 수행하지 않는 경우 사용.

- 읽기전용으로 설정 할 경우 데이터 저장소에서는 트랜잭션의 읽기 전용 특성을 활용한 몇 가지의 최적화를 수행 할 여지가 생긴다. 따라서 트랜잭션을 읽기 전용으로 선언함으로써 데이터 저장소에게 최적화가 적절하다고 판단하고 최적화를 적용할 수 있는 기회를 줄 수 있다.

<tx:advice id="txAdvice" transaction-manager="transactionManager">

<tx:attributes>

<tx:method name="get*" propagation="REQUIRES" read-only="true" />

</tx:attributes>

</tx:advice>

데이터 저장소는 트랜잭션이 시작할 때 읽기 전용 최적화를 수행하므로 새로운 트랜잭션을 시작 할 수 있는 전파방식(REQUIRES, REQUIRES_NEW, NESTED)을 가진 메소드에 선언해야 의미가 있다.


4. 타임아웃

- 트랜잭션이 일정 시간이 지나버리면 롤백하도록 선언하기 위해 사용

- 새로운 트랜잭션을 시작 할 수 있는 전파방식(REQUIRES, REQUIRES_NEW, NESTED)을 가진 메소드에 선언해야 의미가 있다.


5. 롤백규칙

어떤 예외가 발생하였을 때 롤백하고, 어떤 경우에는 롤백하지 않는지 정의

기본적으로 런타임 예외(Runtime Exception)에 대해서는 롤백하고, 검사형 예외(Checked Exception)에 대해서는 롤백하지 않는다.

롤백규칙을 지정해 놓으면 검사형 예외(Checked Exception)에 대해서도 트랜잭션 롤백 처리를 할 수 있다.


rollback-for 속성 - 예외 발생 시 롤백 작업을 수행할 예외 타입을 설정

no-rollback-for 속성 - 예외가 발생하더라도 롤백을 하지 않는 예외 타입을 설정


명시할 예외 타입이 한 개 이상인 경우 각각의 예외는 콤마로 구분

예외 클래스는 완전한 이름을 입력하거나, 패키지 이름을 제외한 클래스 이름만 입력 가능

<tx:method name="get*" read-only="true" rollback-for="Exception"/>

<tx:method name="get*" read-only="true" no-rollback-for="Exception"/>

rollback-for와 no-rollback-for를 동시에 정의하면 가장 상위의 정의가 적용된다.



어노테이션 기반 트랜잭션 설정

@Transactional 어노테이션 사용

메소드나 클래스에 적용

관련 트랜잭션 속성을 설정


 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;

 public class PlaceOrderServiceAnnotationImpl implements PlaceOrderService {
    @Transactional(propagation=Propagation.REQUIRED, readOnly=true)
    public PurchaseOrderResult order(PurchaseOrderRequest orderRequest)
        throws ItemNotFoundException {

        Item item = itemDao.findById(orderRequest.getItemId());

        if(item == null) {
            throw new ItemNotFoundException(orderRequest.getItemId()):
        }

        PaymentInfo paymentInfo = new PaymentInfo(item.getPrice());
        paymentInfoDao.insert(paymentInfo);
        ...
    }
 }


@Transaction 어노테이션 속성

 속성

 설

 propagation

 트랜잭션 전파 규칙 설정, Propagation 열거형 타입에 값이 정의됨

 기본 값 - Propagation.REQUIRED

 isolation

 트랜잭션 격릴 레벨 설정

 readOnly

 읽기 전용 여부 설정

 rollbackFor

 트랜잭션을 롤백할 예외 타입 설정 ex)rollbackFor={Exception.class}

 noRollbackFor

 트랜잭션을 롤백하지 않을 예외 타입 설정 ex)noRollbackFor={Exception.class}

 timeout

 트랜잭션의 타임아웃을 초단위로 설정


@Transactional 어노테이션이 적용된 스프링 빈에 트랜잭션 적용

<tx:annotation-driven>태그를 설정

 <tx:annotation-driven transaction-manager="transactionManager" />

 <bean id="placeOrderService"
    class="kame.spring.store.domain.PlaceOrderServiceAnnotationImpl"
    ... />


<tx:annoation-driven> 태그 대신 PersistenceAnnotationBeanPostProcessor 클래스를 빈으로 등록해도 된다.

 <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />



참조 : 

http://blog.naver.com/PostView.nhn?blogId=chocolleto&logNo=30087623408&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView

http://tedwon.com/display/dev/Spring+Transactions

'FRAMEWORK > SPRING MVC' 카테고리의 다른 글

log4j.properties 설정  (0) 2014.09.29
Spring - 뷰 리졸버(View resolver)  (0) 2014.09.29
Spring - Transaction1 (스프링의 트랜잭션 지원)  (0) 2014.09.29
Spring - POJO  (0) 2014.09.29
Spring - Bean 초기화 및 생명주기  (0) 2014.09.29
:

Spring - Transaction1 (스프링의 트랜잭션 지원)

FRAMEWORK/SPRING MVC 2014. 9. 29. 13:35

트랜잭션 (Transaction)

트랜잭션은 현대의 웹 보안에 있어서 매우 중요한 역활을 차지하며 DB와 JAVA언어가 데이터를 주고 받는 과정에 원자성을 부여하는 수단을 일컫는다.


간단하게 설명하자면 !!

어떤 작업 프로세스를 하나로 묶어 실행 중 하나의 작업이라도 실패하면 모두 실패 처리를 해주고, 

전체 작업이 성공하면 성공 처리를 해주는 것이다.



스프링 프레임워크의 트랜잭션 

 - Java Transaction API(JTA), JDBC, Hivernate, Java Persistence API(JPA), Java Data Objects(JDO)같은 여러 가지 트랜잭션 API간에 일관성있는 프로그래밍 모델

 - 선언적인 트랜잭션 관리 지원

 - 프로그래밍적인 트랜잭션 관리에 대해 JTA같은 복잡한 트랜잭션 API보다 간단한 API

 - 스프링의 데이터 접근 추상화와의 뛰어난 통합


스프링의 트랜잭션 기술을 이용하면, 단 몇 줄의 코드만으로 다이나믹 프록시와 AOP라는 기술을 통해 

크게는 인터페이스 단위에서 클래스, 작게는 메서드까지 세분화하여 트랜잭션을 컨트롤 할 수 있다. 

거기다 애노테이션 기술로 @Transactional을 설정하는 것으로도 트랜잭션 설정이 가능하게 해준다.



스프링의 트랜잭션 관리 지원

1. 프로그래밍 처리 방식 

- 코드에서 트랜잭션의 범위를 정교하게 정의 할 수 있다. 

  하지만 트랜잭션이 스프링의 종속적인 코드가 될 가능성이 크다.


2. 선언적인 트랜잭션

- 실제 작업과 트랜잭션 규칙을 분리 할 수 있다. 

- 격리 수준이나 타임아웃같은 추가적인 애트리뷰트를 선언 할 수 있다.


트랜잭션의 동작 원리



1. Caller가 target 메소드를 호출하면 먼저 AOP Proxy가 호출된다.

2. Transaction Advisor는 새로운 트랜잭션을 생성한다.

3. Custom interceptor들이 transaction advisor 전 후로 호출된다.

4. Target 비즈니스 메소드가 호출된다.

5. Transaction Advisor는 커밋 또는 롤백을 결정하고 호출자에게 넘긴다.

6. AOP Proxy는 결과를 받아서 Caller에서 넘긴다


스프링의 PlatformTransactionManager 설정

스프링은 트랜잭션을 직접 관리하지 않고, 스프링에 포함된 일련의 트랜잭션 관리자를 이용한다.




트랜잭션 관리자는 트랜잭션 관리 책임을 JTA나 퍼시스턴스 메커니즘이 제공하는 플랫폼 종속적인 트랜잭션 구현체에 위임한다.


1. JDBC 트랜잭션

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />

<property name="url" value="jdbc:oracle:thin:@" />

<property name="username" value="" />

<property name="password" value="" />


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource" />

</bean>


DataSourceTransactionManager는 내부적으로 DataSource에서 획득하게 될 java.sql.Connection 객체를 이용하여 트랜잭션을 관리한다.

예를 들어, 모든 작업이 성공한 성공적인 트랜잭션은 이 커넥션 객체의 commit()메소드 호출을 통해 Commit되고, 실패한 트랜잭션은 rollback()메소드를 통해 RollBack된다.


2. 하이버네이트 트랜잭션

<bean id="sessionFactory" class="org.springframeworkd.orm.hibernate3.LocalSessionFactoryBean">

<property name="dataSource" ref="dataSource" />

<property name="mappingResources">

.....

</property>

<property name="hibernateProperties">

.....

</property>

</bean>


<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager>

<property name="dataSource" ref="dataSource" />

</bean>


sessionFactory 프로퍼티를 통해서 전달받은 하이버네이트 Session으로부터 하이버네이트 Transaction을 생성한 뒤, Transaction을 이용해서 트랜잭션을 관리.


3. JPA 트랜잭션

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

.....

</bean>


<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

<property name="entityManagerFactory" ref="entityManagerFactory" />

</bean>


entityManagerFactory 프로퍼티를 통해 전달받은 EntityManagerFactory를 이용해서 트랜잭션을 관리.


4. JTA 트랜잭션

JTA를 사용한다면 트랜잭션 관리자 정의는 JDBC, Hibernate, JPA나 다른 지원 기술 등 어떤 데이터접근 기술을 사용한지와 상관없이 같을 것이다. 이는 JTA 트랜잭션이 트랜잭션이 적용된 어떤 리소스와도 동작하는 전역 트랜잭션이기 때문이다.



참조 : 

http://demon92.tistory.com/67

http://blog.outsider.ne.kr/869

http://springmvc.egloos.com/498979

http://blog.naver.com/PostView.nhn?blogId=chocolleto&logNo=30087623217&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView

'FRAMEWORK > SPRING MVC' 카테고리의 다른 글

Spring - 뷰 리졸버(View resolver)  (0) 2014.09.29
Spring - Transaction2 (선언적 트랜잭션 처리)  (0) 2014.09.29
Spring - POJO  (0) 2014.09.29
Spring - Bean 초기화 및 생명주기  (0) 2014.09.29
Spring - Bean Scope  (0) 2014.09.29
:

Spring - POJO

FRAMEWORK/SPRING MVC 2014. 9. 29. 13:34
지난 몇 년간 EJB3를 포함한 많은 자바의 새로운 기술과 제품들은 저마다 POJO 프로그래밍의 지원을 주요 장점으로 내세우며 등장했다. 그러나 POJO 프로그래밍이 과연 무엇이고 어떤 이득을 안겨줄 수 있는지에 대해 명확히 설명하는 것은 사실 쉽지 않다. 이 글에서는 POJO 프로그래밍이 왜 중요한지 살펴보고 그것을 통해 기존의 무겁고 지저분한 코드를 어떻게 깔끔하고 군살 없는 코드로 만들 수 있는지를 알아보자.

 

POJO는 ‘Plain Old Java Object’의 약자이다. 이를 번역하면 ‘평범한 구식 자바 오브젝트(객체)’라고 할 수 있겠다. 도대체 평범하고 구식인 자바 오브젝트가 뭐가 다르고 특별해서 POJO라고 부르는 것일까? 그럼 평범하지 않은 최신의 자바 오브젝트는 또 무엇인가?

POJO란 무엇인가?

POJO를 이해하려면 POJO라는 단어가 만들어진 역사적 배경을 살펴볼 필요가 있다. POJO는 마틴 파울러가 2000년 가을에 열렸던 어느 컨퍼런스의 발표를 준비하면서 처음 만들어낸 말이다. 마틴 파울러는 EJB(Enterprise JavaBean)보다는 단순한 자바 오브젝트에 도메인 로직을 넣어 사용하는 것이 여러 가지 장점이 있는데도 왜 사람들이 그 EJB가 아닌 ‘평범한 자바 오브젝트’를 사용하기를 꺼려하는지에 대해 의문을 가졌다. 그리고 그는 단순한 오브젝트에는 EJB와 같은 그럴듯한 이름이 없어서 그 사용을 주저하는 것이라고 결론 내렸다. 그래서 만든 단어가 POJO라는 용어인 것이다. POJO 기반의 기술을 사용한다고 말하면 왠지 첨단 기술을 사용하는 앞선 개발자인 듯한 인상을 주기 때문이다. 
이 POJO라는 용어를 만들어낸 전략은 매우 성공적이었다. EJB의 문제점과 한계를 느낀 많은 개발자들은 새로운 기술처럼 보이는 POJO 프로그래밍이라는 것에 관심을 가지게 되었고, POJO 기반의 프로그래밍 기술이 EJB의 강력한 대안으로 등장하기 시작했다. POJO 기반의 프레임워크, POJO 애플리케이션을 위한 플랫폼 등이 점점 인기를 끌게 되었고, 결국 POJO가 배제하려고 했던 EJB는 POJO 기반의 기술에 밀려 이제 레거시 기술로 사라질 위기에 처했다. 그렇다면 단지 EJB를 사용하지 않으면 모두 POJO라고 할 수 있을까? 그렇지는 않다. POJO 프로그래밍이라는 개념은 단지 ‘EJB가 아닌 자바’ 이상의 특징을 가지고 있는 프로그래밍 모델이다. POJO 기반의 개발은 생각보다 단순하지 않다. 
POJO를 좀더 이해하려면 EJB의 장단점을 함께 이해해야 한다. 그것은 POJO 프로그래밍이 다시 EJB 시대 이전으로 돌아가자는 것이 아니고, EJB를 넘어 그보다 더 앞으로 나아가자는 것이기 때문이다.

 

EJB와 엔터프라이즈 서비스

자바에서 EJB 기술의 등장은 필연적인 것이었다. 기업의 IT 시스템은 점점 그 중요성이 증대되고 그에 따라 점점 복잡한 기술이 요구되었으며 자바의 기초적인 JDK만으로는 그것을 충족시킬 수 없었다. 서버 기반의 자바 기술인 J2EE(Java2 Enter pise Edition)가 등장했지만 Servlet, JSP 레벨의 최소한의 서버 프로그래밍 인터페이스만 가지고는 복잡한 엔터프라이즈 애플리케이션을 제작하는 데 부담이 적지 않았다.


엔터프라이즈 시스템의 복잡도의 증가 영역

 - 기업업무처리의 IT시스템에 대한 의존도가 높아지면서 시스템이 다뤄야 하는 비즈니스로직 자체가 점차 복잡해진 것 애플리케이션 로직의 복잡도와 상세 기술의 복잡함을 개발자들이 한 번에 다룬다는 것은 쉬운 일이 아니었다. 한 개발자가 보험업무와 관련된 계산 로직을 자바로 어떻게 구현해야 하는지에 집중하면서 동시에 시스템 레벨에서 멀티 DB로 확장 가능한 트랜잭션 처리와 보안 기능을 멀티스레드 세이프하게 만드는 것에 신경 써야 한다면 여간 부담되는 게 아닐 것이다.

 - 많은 사용자의 처리요구를 빠르고 안정적이면서 확장 가능한 형태로 유지하기 위해 필요한 로우레벨의 기술적인 처리 요구 ( 대표적으로 트랜잭션 처리, 상태 관리, 멀티스레딩, 리소스 풀링, 보안 등 )

 

EJB는 이런 문제를 다루기 위해 등장했다. EJB 1.0의 스펙이 제시한 EJB의 비전은 ‘EJB는 애플리케이션 개발을 쉽게 만들어 준다. 애플리케이션 개발자는 로우레벨의 기술들에 관심을 가질 필요도 없다’였다. 애플리케이션 개발자들은 다뤄야 하는 해당 도메인과 비즈니스 로직에만 집중하면 된다는 것이었다. 게다가 EJB는 독립적으로 개발한 컴포넌트들을 서버에 자유롭게 배포하고 서로 연동해 사용하게 하는 컴포넌트 기반의 개발 모델을 제시할 뿐더러, 여러 대의 서버에 분산되어 있는 모듈간의 리모팅 처리도 개발자들이 거의 신경 쓰지 않고 개발할 수 있게 했다. 더 나아가 벤더별로 제각각 발전시켜 혼란에 빠지기 쉬운 자바의 서버 기술을 일관성 있게 구현하게 지원하므로 특정 서버에 종속되지 않고 서버간의 이동성(portability)을 보장해 준다고 약속했다

 

하지만 EJB는 불필요할 만큼 과도한 엔지니어링으로 실패한 대표적인 케이스였다.

EJB에서는 현실에서 1% 미만의 애플리케이션에서만 필요한 멀티 DB를 위한 분산 트랜잭션을 위해 나머지 99%의 애플리케이션도 무거운 JTA 기반의 글로벌 트랜잭션 관리 기능을 사용해야 했다. EJB의 혜택을 얻기 위해 모든 기능이 다 필요하지도 않은 고가의 WAS(CPU 당 몇 백에서 몇 천만 원씩 하는)를 구입해야 했고, 고급 IDE의 도움 없이는 손쉽게 다룰 수 없는 복잡한 설정 파일 속에서 허우적대야 했다. EJB 컴포넌트는 컨테이너 밖에서는 정상적으로 동작할 수 없으므로 개발자들은 끝도 없이 반복되는 수정-빌드-배포-테스트의 지루한 과정으로 많은 시간을 낭비해야 했고, 간단한 기능에 대해서조차 자동화된 테스트를 만드는 것은 거의 불가능에 가까웠다. 테스트는 서버에 배치 후에 대부분 수동으로 해야 했고, 느린 배포 작업 탓에 그나마도 자주 반복되기 힘들게 만들었다. 특별한 경우가 아니라면 그다지 장점이 없는 EJB의 원격분산 모델은 성능을 떨어뜨리고 서버의 복잡도만 증가시켰다. 가장 최악의 문제점은 EJB 스펙을 따르는 비즈니스 오브젝트들은 객체지향적인 특징과 장점을 포기해야 했다는 것이다. EJB빈은 상속과 다형성 등의 혜택을 제대로 누릴 수 없었다. 간단한 기능 하나를 위해서도 많은 인터페이스와 EJB 의존적인 상속 등을 사용해야 했다. EJB 옹호자들 사이에서조차 외면 받아온 엔티티빈에 대해 말하는 것은 시간 낭비일 것이다.

 

그럼에도 EJB가 계속 사용되었던 이유는,

엔터프라이즈 애플리케이션에서 반드시 필요로 하는 주요한 엔터프라이즈 서비스들을 애플리케이션 코드와 분리해서 독립적인 서비스로 사용할 수 있게 만들어줬다는 점이다. 비록 불완전하고 불필요한 복잡도가 남아있긴 했지만 선언적인 트랜잭션 관리(Declarative Transaction Management)나 역할 기반의 보안(Role based Security) 기능들을 제공했다. 비즈니스 오브젝트를 배포하고 관리하는 컨테이너를 제공하고 기본적인 스레드 관리, 인스턴스/리소스 풀링을 제공하는 등의 것들이다. 한편으로는 ‘개발자들이 로우레벨의 기술적인 문제에 신경 쓰지 않고 비즈니스 로직에 충실히 개발하게 함으로써 애플리케이션 개발을 손쉽게 만들어 준다’는 처음 약속을 어느 정도 지켰다고 볼 수 있다. 하지만 EJB의 문제는 앞서 지적한 것처럼 한편으로는 애플리케이션 개발의 복잡도를 제거하면서 다른 한편으로는 더 많은 문제와 복잡성을 가지고 왔다는 것이다.

 

결국 EJB는 형편없는 생산성과 느린 성능, 불필요한 기술적인 복잡도, 벤더 사이의 알력으로 과도하게 높아진 스펙 등으로 인해 자바의 엔터프라이즈 개발에 대한 불신을 가중시켰다. 마침내 마틴 파울러를 비롯한 많은 오피니언 리더들은 EJB와 같은 잘못 설계된 과도한 기술을 피하고, 객체지향 원리에 따라 만들어진 자바 언어의 기본에 충실하게 비즈니스 로직을 구현하는 일명 POJO 방식으로 돌아서야 한다고 지적하고 나섰다. POJO 방식의 개발은 EJB가 잃어버린 소중한 가치인 객체지향적인 설계와 자동화된 테스트의 편의성, 개발생산성 등을 회복시켜 줄 수 있는 길이기 때문이다.

 

결국 POJO를 정리하자면,

1. 특정 규약(contract)에 종속되지 않는다.

Java 언어와 꼭 필요한 API 외에 종속되지 않는다. EJB2 의 EntityBean 상속이나 Struts 1 의 ActionForm 상속등 규약에 종속되지 않아야 한다.

2. 특정 환경에 종속되지 않는다.

EJB3 의 JNDI 서버 서비스의 의존이나 특정 벤더의 서버나 기업프레임워크안에서만 동작 가능한 코드가 아니다.

3. 객체지향원리에 충실해야 한다.

특정 기술규약과 환경에 종속되지 않은 Java Object 가 모두 POJO라 할수는 없다. 객체지향 개념이 녹아있지 않은 것만 POJO 이다.

 

POJO를 사용하는 이유

1. 코드의 간결함

비즈니스 로직과 특정 환경/low 레벨 종속적인 코드를 분리하므로 단순하다.

2. 자동화 테스트에 유리

환경 종속적인 코드는 자동화 테스트가 어렵지만, POJO 는 테스트가 매우 유연하다.

3. 객체지향적 설계의 자유로운 사용

특정 규약 종속적인 객체의 경우 특정 상속을 미리 지정해서, 단일상속만 제공하는 JAVA 언어로서는 더이상 객체지향적 설계를 하는데 제약을 가져오는 경우가 있으나, POJO 는 아무런 규약이나 규제가 없으므로 OO 설계에 매우 자유롭다.

 

 

POJO 프레임워크

앞에서 강조한 것처럼 EJB를 사용하지 말고 POJO를 쓰자는 것이 EJB 이전의 방식으로 돌아가는 것을 의미한다면 또 다른 문제가 발생할 수밖에 없다. 여전히 복잡한 로우레벨의 API를 이용해 코드를 작성해야 하고, 많은 기술적인 문제를 애플리케이션 코드에 그대로 노출시켜 개발해야 한다면 기껏 POJO로의 복귀 덕분에 얻은 많은 장점들을 놓칠 수밖에 없다. 그래서 등장한 것이 바로 POJO 기반의 프레임워크이다.

 

POJO 프레임워크 - POJO를 이용한 애플리케이션 개발이 가진 특징과 장점을 그대로 살리면서 EJB에서 제공하는 엔터프라이즈 서비스와 기술을 그대로 사용할 수 있도록 도와주는 프레임워크, 나아가 이는 기존의 EJB에서보다 훨씬 더 세련되고 나은 방법

많은 POJO 프레임워크가 있지만 그 중에서 가장 대표적인 것을 꼽으라면 하이버네이트와 스프링을 들 수 있다.


하이버네이트 - EJB의 엔티티빈이 제시했던 컨테이너가 관리하는 퍼시스턴스 기술(Container Managed Persistence)과 오브젝트-관계형 DB 매핑(Object Relational Mapping) 기술을 순수한 POJO를 이용해 사용할 수 있게 하는 POJO 기반의 퍼시스턴스 프레임워크

하이버네이트는 사실 엔티티빈이 제공했던 것과는 비교할 수 없을 만큼 더 편리하고 세련된 방식으로 퍼시스턴스 기능을 제공한다. 굳이 컨테이너 위에서 동작시킬 필요도 없고, 이상적인 매핑을 위해 포기했던 많은 기능과 성능을 객체지향 DB에 적합하게 최적화한 기능을 통해 JDBC API를 직접 사용해 개발하는 것 못지않은 성능과 복잡한 퍼시스턴스 로직을 개발 가능하게 해줬다. 가장 중요한 점은 하이버네이트가 사용하는 POJO 엔티티들은 EJB의 엔티티빈과 달리 객체지향적인 다양한 설계와 구현이 가능하다는 점이다. 엔티티의 상속, 다형성, 밸류 오브젝트, 사용자정의 타입 등을 어떠한 기술적인 손해 없이도 그대로 퍼시스턴스 매핑용 오브젝트로 사용할 수 있게 한다.


스프링 - 세션빈이 제공하던 중요한 엔터프라이즈 서비스들을 POJO 기반으로 만든 비즈니스 오브젝트에서 사용할 수 있게 한다. 대표적인 것이 선언적인 트랜잭션 서비스와 보안이다. 또한 EJB와 마찬가지로 오브젝트 컨테이너를 제공해서 인스턴스의 라이프사이클을 관리하고 필요에 따라 스레딩, 풀링 및 서비스 인젝션 등의 기능을 제공한다. 또한 OOP를 더 OOP답게 사용할 수 있게 하는 AOP 기술을 적용해서 POJO 개발을 더 쉽게 만든다.

 

POJO 프로그래밍을 손쉽게   있도록 지원하는 세가지 가능기술(Enabling technology)

(1) IoC/DI (Inversion of Control / Dependency Injection)

IoC/DI  스프링의 가장 기본의 되는 기술이자 스프링의 핵심 개발원칙이다나머지 두가지 기술인 AOP  PSA IoC/DI 바탕을 두고 있으며, 3대기술은 아니지만자주 등장하는 템플릿/콜백 패턴의 적용도 IoC/DI 핵심원리다.


(2) AOP (Aspect Oriented Programming)
핵심 관심사를 분리하여 프로그래매 모듈화를 향상시키는 프로그래밍 스타일이다. AOP는 객체를 핵심 관심사와 횡단 관심사로 분리하고, 횡단 관심사를 관점(Aspect)라는 모듈로 정의하고, 핵심 관심사와 엮어서 처리할 수 있는 방법을 제공한다. 스프링은 자체적으로 프록시 기반의 AOP를 지원하며, 로깅, 트랜잭션, 보안 이런것들이 전반적으로 처리하는데 사용됨.

(3) PSA (Portable Service Abstraction)
인터페이스가 다른 다양한 구현을 같은 방식으로 사용하도록 중간에 인터페이스 어댑터 역할을 해주는 레이어를 하나 추가하는 방법


POJO vs. 짝퉁 POJO

POJO 프로그래밍의 진정한 가치는 자바의 객체지향적인 특징을 살려 비즈니스 로직에 충실한 개발이 가능하도록 하는 것이다. 그러면서 복잡한 요구조건을 가진 엔터프라이즈 개발의 필요조건을 충족시킬 수 있도록 POJO 기반의 프레임워크를 적절히 사용하는 것이 요구된다. 문제는 단지 POJO 프레임워크로 잘 알려진 제품을 사용하기만 하면 자동으로 POJO 개발을 하고 있다고 생각하는 경우가 많다는 것이다.


스프링과 하이버네이트가 한창 인기를 끌기 시작할 때 EJB 기반으로 구현된 애플리케이션에 스프링과 하이버네이트를 적용했다고 해서 주목받던 어느 오픈소스 제품이 있었다. POJO 기반 서버 애플리케이션의 좋은 샘플이라고 생각한 필자와 동료들은 그 소스를 가져다가 한번 살펴봤는데, 정작 그 소스를 보고는 놀라지 않을 수 없었다. EJB를 제거하고 POJO 기반의 프레임워크를 적용했다고는 하지만 사라진 것은 단지 EJB 관련 클래스와 인터페이스 정도이고 그 외에 EJB로 개발할 때 가졌던 모든 코드와 스타일을 그대로 가져다가 억지로 POJO 프레임워크에 구겨 넣은 것을 발견했기 때문이다. 그들이 말하는 POJO 프레임워크 기반의 개발이란 단지 POJO 프레임워크 위에서 동작하기만 하면 그 오브젝트의 설계와 구현은 어찌되든 상관없는 형태였다. 이는 자바가 처음 등장했을 때 자바로 개발한다고 하면서, 실제로는 C에서 쓰던 절차적인 방식(객체지향적인 모든 특성 무시)으로 구현했던 경우와 다를 게 없었다.


필자는 그런 POJO 애플리케이션을 짝퉁 POJO라고 부르고 싶다. 결과적으로 그런 개발은 POJO 프로그래밍이 주는 장점을 제대로 살릴 수 없고 오히려 배제되어야 할 또 다른 복잡함을 낳기 때문이다. <리스트 1>은 최근에 어느 프로젝트에서 스프링을 이용해 개발했다고 소개한 코드를 간략하게 옮겨놓은 것이다.

 

<리스트 1> 잘못된 POJO 기반 코드의 예

class MyService {
    private MyDAO myDAO;
    private XXDao xxDAO;
     
    public MyVo foo(UrVO urVo) throws MyException {
        Connection con = null; 
        try {
            con = DBUtil.getConnection();
            con.setAutoCommit(false);

            if (xxDAO.check(urVo))
                return myDAO.foo(con, urVo);

            else
                return myDAO.boo(con, urVo);
        }
        catch (MyException e) {
            con.rollback();
            throw e;
        }
        catch (Exception e) {
            throw new MyException(e, "XXX00001");
            con.rollback();
        }
        finally {
            con.commit();
            DBUtil.close(con);
        }
    }
    
}


class MyDAO { 
    public void foo(Connection con, UrVO urVO) throws MyException {
        PreparedStatement pstmt = null;
        try {
            pstmt = new LoggablePreparedStatement(con, QueryFactory.getInstance().getQuery("FOO"));
            pstmt.setInt(1, urVO.getSeq());
            if (pstmt.executeUpdate() != 1) {
                throw new MyException("SVM00009");
            }
        } catch (MyException e) {
            throw kble;
        } catch (Exception e) {
            throw new MyException(e);
        } finally {
            DBUtil.close(pstmt);
        }
    }
    
}

 

이 코드는 물론 스프링의 IoC/DI 컨테이너에 올라가서 빈(bean)으로 등록되어 Setter 인젝션 방식으로 사용되기는 할 것이다. 그럼 과연 이 코드가 POJO라고 말할 수 있을까? 물론 EJB를 사용하지 않았고 특정 환경에 종속적인 것처럼 보이지도 않는다. 또한 POJO 프레임워크의 대표격인 스프링 프레임워크를 사용해 만들어졌다. 하지만 이 코드는 POJO 프로그래밍의 장점을 거의 살리지 못한 레거시 코드일 뿐이다.

 

필자는 POJO 기반의 코드인지 아닌지를 확인하기 위해 다음의 중요한 두 가지 기준을 적용한다.
첫째는 객체지향적인 설계원칙에 충실하도록 개발되어 있는지 여부이다. POJO의 자바 오브젝트라는 것은 단지 자바 언어 문법을 지켜 만들었다는 뜻이 아니다. 객체지향 언어로서의 자바 오브젝트의 특징을 가지고 있는지가 중요하다. POJO 코드가 객체지향적인 원리에 따라 설계되어 있지 않고 더 나아가 적절하게 리팩토링하는 것도 어려운 구조라면 POJO로 개발했다는 것이 주는 가치는 거의 없다고 본다. EJB를 사용하지 않으므로 오히려 코드가 <리스트 1> 같은 식으로 바뀌었다면 그것은 POJO 개발이라고 볼 수 없다. 끊임없이 반복적으로 등장하는 템플릿 코드와 테스트하기 힘든 구조, 확장이나 재활용의 어려움 등이 코드에 그대로 남아있다면 EJB의 문제점을 여전히 안고 있는 것이다. 
둘째는 테스트 코드 개발의 용이성이나 테스트 코드를 잘 작성했는지의 여부이다. EJB를 버리고 POJO 기반으로 개발한다고 하면서도 여전히 수정-빌드-배포-테스트라는 방식을 탈피하지 못하고 있다면 EJB로 개발했던 시절과 대체 무엇이 다를까? 잘 만들어진 POJO 애플리케이션은 자동화된 테스트 코드 작성이 편리하다. 코드 작성이 편리하면 좀더 자주 꼼꼼하게 만들게 되고 반복적으로 실행할 수 있으므로 코드 검증과 품질 향상에 유리하다. 또한 잘 만들어진 테스트 코드베이스가 있다면 리팩토링할 여유가 생겨 POJO 코드를 더 나은 설계구조로 변경할 가능성도 높아진다.

 

POJO로의 변환

<리스트 1>의 코드를 스프링의 POJO 프로그래밍 모델에 따라 수정해 보자. DAO 코드의 가장 큰 문제점은 JDBC의 오래된 코드 스타일을 그대로 사용하고 있다는 것이다. 따라서 try/catch/ finally의 전형적인 템플릿 코드가 실제 DAO 로직보다 더 많은 라인을 차지하고 있다. 또 Query를 불러오는 부분이 싱글톤을 사용하고 있다. 그나마 JDBC 코드를 간략하게 작성하도록 돕는 유틸리티 클래스를 사용했지만 정적 메소드를 이용했다. 싱글톤과 정적메소드는 테스트 코드를 만드는 데 가장 큰 장애물이다. 테스트를 쉽게 하기 위해 모의객체(mock object)로 변환하는 것이 불가능하기 때문이다. 객체지향적인 설계방식을 따른다면 Query를 가져오는 기능을 인터페이스로 만들어 사용하게 하고 이를 구현한 오브젝트를 DAO에서 참조할 수 있도록 하는 것이 바람직하다. 또한 템플릿 스타일의 과도한 코드를 제거하고 반복적인 JDBC 워크플로우를 제거하려면 콜백 방식으로 구현하는 게 좋다. 그러면 DAO의 데이터액세스 로직과 JDBC 처리 워크플로우를 구분할 수 있다. <리스트 2>는 이렇게 스프링을 이용한 POJO 스타일로 수정된 코드이다.

 

<리스트 2> 수정된 MyDAO

public class MyDAO {
    private DataSource dataSource;
    private SimpleJdbcTemplate jdbcTemplate;
    private QueryFactory queryFactory; 
    // setter methods
    public void foo(UrlVo urlVo) {
        jdbcTemplate.update(queryFactory.getQuery("FOO"), urlVo.getSeq());
    }
}

 

MyDAO는 DataSource, QueryFactory라는 협력객체를 인터페이스를 통해 액세스하므로 해당 오브젝트의 구현에 상관없이 동작할 수 있다. DataSource가 개발용에서 테스트용으로 또 실제 운영서버로 바뀐다고 하더라도 DAO는 그 부분에 신경 쓰지 않고 클래스의 목적인 데이터로직을 구현하는 데만 충실하게 만들어져 동작할 수 있게 되었다. 테스트를 위해 실제 DB가 아닌 Fake DB나 Embedded DB를 적용한다면 그 DB 설정을 돌려주는 적절한 DataSource를 MyDAO가 사용하도록 스프링을 통해 설정하면 그만이다.


또 JdbcTemplate이라는 JDBC 워크플로우를 제공하는 템플릿 기반의 Helper 클래스를 사용해서 반복적으로 등장하는 JDBC의 try/catch/finally 작업을 DAO에서 제외하고 순수하게 데이터 처리와 관련된 핵심 로직만 사용하도록 정리했다. 결과적으로 코드는 깔끔해지고 여러 클래스에 거쳐 나타나는 지저분한 중복은 제거되었다. 또한 DAO 오브젝트의 역할을 넘어선 것들은 의존하는 오브젝트로 분리하고 그 구현에 종속되지 않도록 인터페이스를 사용하게 했으므로 객체지향적인 설계에 충실한 오브젝트의 설계와 구현에 가깝도록 만들어졌다. 

이번에는 MyService를 살펴보자. MyService의 가장 큰 문제는 무엇인가?

그것은 MyService는 비즈니스 로직을 구현한 클래스임에도 불구하고, 실제로 코드 안에는 그와 상관없는 데이터액세스와 관련된 코드들이 덕지덕지 붙어 있다. MyService에 나타난 커넥션과 트랜잭션 처리 코드들은 어찌 보면 어쩔 수 없는 선택으로 생각할 수도 있다. DAO를 분리해 놓은 상태로, MyService에서 여러 개의 DAO를 호출해 처리하는 결과를 하나의 커넥션과 트랜잭션으로 묶으려면 MyService 레벨에서 처리해야 하기 때문이다. 그렇지 않고 DAO 메소드 단위로 커넥션과 트랜잭션을 다루면 더 심각한 결과를 가져온다. 하지만 결과적으로 MyService는 DB 처리 코드와 로직 코드가 짬뽕되어 있는 지저분한 코드가 되었고, DB 관련 코드와 구현에 종속이 되었으므로 MyService는 객체지향적인 장점을 살려 재활용되거나 발전하는 데 극히 제한을 받게 된다. 만일 트랜잭션 처리가 JDBC 방식에서 JTA로 바뀌었다면? MyService처럼 구현한 모든 서비스/비즈니스 로직 코드를 일일이 수정해야 하는 중노동에 시달려야 한다. 하나의 클래스에 여러 가지 레이어의 기술이 짬뽕되고 책임이 중복되어 나타나며 한 가지 기능을 수정하기 위해 수많은 클래스를 수정해야 한다면 이것이 과연 POJO를 지지하는 사람들이 기대했던 바로 그 POJO 프로그래밍의 장점이라고 할 수 있을까?

 

<리스트 3> 수정된 MyService

class MyService {
    private MyDAO myDAO;
    private XxDAO xxDAO;
    // setter methods
    public void foo(UrVo urVo) {
        if (xxDAO.check(urVo))
            return myDAO.foo(con, urVo);
        else
            return myDAO.boo(con, urVo);
    }
    
}

 

MyService를 순수한 비즈니스 로직에 충실한 POJO 기반으로 변경하려면 스프링과 같은 POJO 프레임워크의 도움이 절실히 필요하다. 겉으로 드러나는 DB 커넥션, 예외처리, 트랜잭션과 관련된 부분을 비즈니스 로직을 구현한 MyService에서 분리하기 위해서는 AOP 기술의 도움이 필요하기 때문이다. <리스트 3>은 스프링의 AOP로 선언적 트랜잭션 관리 기능을 적용해 변경한 MyService 코드이다.


<리스트 1>에서 보았던 MyService의 모든 지저분한 DB와 트랜잭션 처리 블록이 모두 제거되었다. 남은 것은 MyService의 비즈니스 로직에 충실한 코드들뿐이다. 필요에 따라 적절한 DAO를 호출해서 퍼시스턴스 관련된 기능을 DAO를 통해 처리하는 것만 있다. 이는 객체지향적인 설계 모델에 충실하게 만들어져 있다.
한 가지 남은 문제는 테스트 유용성인데, MyService의 메소드를 테스트하기 위해서는 MyDAO, XxDAO가 필요하다. DAO는 DB를 역시 필요로 하므로 MyService를 테스트하기 위해 그 메소드를 호출하면 2개의 DAO 코드와 DB까지 동작이 필요하기 때문이다. DB까지 연동하는 테스트는 그 데이터 준비도 만만치 않거니와 시간도 많이 걸린다. 따라서 테스트를 만들고 자주 수행하는 데 어려움을 준다.
이를 위해 MyDAO를 다시 생각해 볼 필요가 있다. MySer vice 입장에서 MyDAO는 오브젝트를 장기간 보존하고 그것을 다시 조회하도록 돕는 퍼시스턴스 기능의 필요에 따라 사용하는 대상이다. 그 구현이 어떻게 되는지는 중요하지 않을 뿐더러 퍼시스턴스 구현이 필요하면 바뀔 수도 있다고 가정하면 지금과 같은 MyDAO의 클래스를 직접 액세스하는 구조는 바람직하지 않다. 전략패턴(Strategy Pattern)의 개념을 적용해서 MyDAO를 인터페이스와 구현으로 분리하고 MyService는 MyDAO 인터페이스를 사용하도록 수정하면 이 문제를 모두 해결할 수 있다. 먼저 MyDAO 인터페이스를 정의한다.

 

interface My DAO {
    void foo(UrVo);
}

그리고 이를 구현하는 클래스를 만든다.

class MyJdbcDAO implements MyDAO {
    
}

 

이제 MyService는 MyDAO라는 인터페이스에 대해 프로그래밍하는 구조로 바뀌었다. 이것의 장점은 MyDAO의 구현이 언제든지 교체 가능하다는 것이다. 구현한 알고리즘을 통째로 교환해 사용할 수 있게 하는 것이 전략패턴의 특징이다. DAO 구현이 필요에 따라 JDBC가 아닌 하이버네이트나 다른 종류의 퍼시스턴스여야 한다면 언제든지 MyDAO 인터페이스를 구현해 만들면 된다. MyService는 구현이 바뀌는 것에 전혀 영향 받지 않는다.
MyService의 로직이 복잡해 구현하면서 자주 테스트가 필요하다면 MyDAO 인터페이스를 구현한 간단한 테스트용 DAO 클래스를 만들어 MyService가 그것을 사용하도록 만든다. 이는 MyService와 MyDAO의 관계를 느슨하게 만들어 주었기 때문에 모두 가능한 것이다. 여기서 나온 MyService와 MyDAO는 아직 개선의 여지가 남아 있다. 하지만 일단 여기까지만 살펴보자. 

여기서 사용한 기법들은 모두 객체지향의 기본 설계원리와 패턴에 등장하는 것들이지 어떤 최신 기법들이 아니다. 물론 AOP라는 새로운 기술을 사용하지만, 그것이 추구하는 것은 또 다른 프로그래밍 모델이 아닌 OOP에 더 충실한 자바 코드를 만드는 데 도움을 주는 것일 뿐이다. 
앞에서 살펴본 것처럼 POJO 기반의 프레임워크를 가져다 사용하기만 했다고 해서 POJO 프로그래밍이 되고 그 유익을 누리는 것은 결코 아니다. POJO가 지향하는 자바의 객체지향적인 설계의 기본에 충실하도록 POJO 프레임워크의 도움을 받아 구현하는 것이 진정한 POJO 프로그래밍이다. 결과적으로 이렇게 만들어진 POJO 기반의 코드는 EJB나 그 이전의 자바개발 방법을 따라 만든 어떤 코드보다 더 간결하고 깔끔해진다. 이것이 객체지향 언어인 자바를 사용하면서 누려야 하는 진정한 혜택이 아닐까.

 

풍성한 도메인 모델

이제는 POJO 개발의 조금 다른 영역을 생각해 보자. POJO의 자바 오브젝트가 지닌 기본 특징은 하나의 오브젝트 안에 상태(State)와 행위(Behavior)를 모두 가지고 있는 것이다. 쉽게 말해 인스턴스 변수와 로직을 가진 메소드를 가지고 있다는 의미이다. 문제는 자바의 그런 특성이 EJB에 와서 이상한 오브젝트 형태로 왜곡된 것이다. 빈약한 오브젝트(anemic object)라고 불리는 것이 바로 그것으로, 로직이 없고 상태만 가진 오브젝트들이 다수 사용되었다. 그러면 그 로직에 해당하는 행위는 어디로 갔을까? 그것은 절차적인(procedural) 스타일로 작성된 서비스 레이어의 메소드로 들어갔다. 결과적으로 서비스 오브젝트는 과도한 로직이 트랜잭션 스크립트 형태로 반복되어 길게 등장하면서 갈수록 비대해졌고, 도메인 모델을 구현한 오브젝트는 빈약한 오브젝트로 오로지 상태 인스턴스 변수만 가진 전혀 객체지향 언어 답지 않은 결과만 남게 되었다. 
이러한 스타일은 EJB뿐만 아니라 그 이후에 등장한 POJO 기반의 프로그래밍에서도 쉽게 찾아볼 수 있다. <리스트 4>와 <리스트 5>는 각각 은행의 계좌를 이체하는 서비스의 메소드와 계좌를 구현한 도메인 모델이다.

 

<리스트 4> 계좌이체 서비스 클래스

public class MoneyTransferServiceProceduralImpl implements MoneyTransferService {
    private AccountDAO accountDAO;
    private BankingTransactionDAO bankingTransactionDAO;

    public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {
        Account fromAccount = accountDAO.findAccount(fromAccountId);
        Account toAccount = accountDAO.findAccount(toAccountId);
        assert amount > 0;
        double newBalance = fromAccount.getBalance() - amount;
        switch (fromAccount.getOverdraftPolicy()) {
            case Account.NEVER:
                if (newBalance < 0)
                    throw new MoneyTransferException("In sufficient funds");
                break;
            case Account.ALLOWED:
                Calendar then = Calendar.getInstance();
                then.setTime(fromAccount.getDateOpened());
                Calendar now = Calendar.getInstance();
                double yearsOpened = now.get(Calendar.YEAR) - then.get(Calendar.YEAR);
                int monthsOpened = now.get(Calendar.MONTH) - then.get(Calendar.MONTH);
                if (monthsOpened < 0) {
                    yearsOpened--;
                    monthsOpened += 12;
                }
                yearsOpened = yearsOpened + (monthsOpened / 12.0);
                if (yearsOpened < fromAccount.getRequiredYearsOpen() || newBalance < fromAccount.getLimit())
                    throw new MoneyTransferException("Limit exceeded");
                break;
            default:
                throw new MoneyTransferException("Unknown overdraft type: " + fromAccount.getOverdraftPolicy());
        }
        fromAccount.setBalance(newBalance);
        toAccount.setBalance(toAccount.getBalance() + amount);
        TransferTransaction txn = new TransferTransaction(fromAccount, toAccount, amount, new Date());
        bankingTransactionDAO.addTransaction(txn);
        return txn;
    }
}

 

<리스트 5> Account 도메인 클래스

public class Account {
    public static final int NEVER = 1;
    public static final int ALLOWED = 2;

    private int id;
    private double balance;
    private int overdraftPoicy;
    private String accountId;
    private Date dateOpened;
    private double requiredYearsOpen;
    private double limit;
    // getters/setters
} 

 

MoneyTransferServiceProceduralImpl의 transfer 메소드는 길고 복잡하다. 자세히 살펴보면 사실 Account라는 오브젝트에 속해야 하는 로직들이 이체서비스의 서비스 로직에 빠져나와 있음을 알 수 있다. 반면에 Account 오브젝트에는 행위(메소드)는 없고 상태(필드)만 남아 있다. 만일 여기서 사용된 Account에 마땅히 들어가야 할 내용이 다른 곳에서 또 필요하다면? 그때는 복잡한 로직 코드가 여기저기 중복될 수밖에 없다. 
그럼 객체지향 기술의 장점을 충분히 누릴 수 있도록 이 코드를 POJO답게 수정해 보자. 가장 간단한 방법은 Account 오브젝트가 가지고 있어야 하는 행위를 Account로 옮기는 것이다. <리스트 6>과 <리스트 7>은 각각의 코드를 수정한 것이다.


<리스트 6> 수정된 계좌이체 서비스 클래스

public class MoneyTransferServiceProceduralImpl implements MoneyTransferService {
    …
    public BankingTransaction transfer(String fromAccountId, String toAccountId, double amount) {
        Account fromAccount = accountDAO.findAccount(fromAccountId);
        Account toAccount = accountDAO.findAccount(toAccountId);
        assert amount > 0;

        fromAccount.debit(amount);
        toAccount.credit(amount);

        TransferTransaction txn = new TransferTransaction(fromAccount, toAccount, amount, new Date());
        bankingTransactionDAO.addTransaction(txn);
        return txn;
    }
}

 

<리스트 7> 수정된 Account 오브젝트

class Account {

// fields

// getters/setters

 

public void debit(Account fromAccount, double amount) {

double newBalance = getBalance() ? amount;

switch (getOverdraftPolicy()) {

...

}

setBalance(newBalance);

}

public void credit(Account toAccount, double amount) {

setBalance(getBalance() + amount);

}

}

 

이체서비스 오브젝트 안의 한 메소드에 지저분하게 들어 있던 이체 로직은 그 로직이 있어야 할 Account 안에 절절한 메소드 형태로 옮겨졌다. 서비스 메소드에서는 그 오브젝트의 메소드를 호출해 서비스 로직을 수행하는 간단하고 명확한 코드로 역시 변경되었다. 이제 Account 오브젝트는 여러 로직과 계층에서 자신의 로직을 분산해 복사할 것 없이, 오브젝트 그대로 재활용되어 사용되는 객체지향의 혜택을 누릴 것이다. 또한 POJO이므로 Account와 MoneyTransferServiceProceduralImpl 모두 쉽게 테스트할 수 있다. 이렇게 객체지향 원리에 충실하게 도메인 모델을 만드는 것을 풍성한 도메인 모델(Rich Domain Model)이라고 한다.

 

진정한 POJO 프로그래밍 추구하기

이 글의 주제는 다이어트 POJO 프로그래밍이다. 필자처럼 체중이 많이 나가는 사람에게 다이어트는 매우 중요한 도전이자 과제이다. 다이어트가 왜 중요한가? 단지 체중을 줄여 날씬한 몸매를 만드는 것이 목적일까? 
그렇지 않다. 다이어트가 중요한 이유는 그것이 건강을 추구하기 때문이다. 다이어트를 통해 궁극적으로 얻고자 하는 것은 건강이다. 마찬가지로 POJO를 잘 사용하면 군더더기 없는 최소한의 코드로 이전과 동일한 결과를 낼 수 있지만, 더 중요한 의미는 더 건강한 코드를 만드는 데 있다.
단지 어느 순간에 상태가 좋다고 해서 건강하다고 할 수는 없다. 건강이란 외부의 스트레스와 변화에도 흔들리지 않고 견고하게 버틸 수 있는 힘인데, 이렇게 건강한 코드를 만들기 위해서는 반드시 자동화된 테스트 코드를 개발해야 한다. 잘 만들어진 테스트 코드는 지속적인 변화에 유연하게 대응할 수 있도록 도와준다. 변화를 두려워하지 않게 만들고 코드의 구조를 개선하기 위한 리팩토링도 안심하고 진행할 수 있게 지원한다. 또한 POJO로 잘 설계된 코드는 테스트 코드를 쉽게 만들 수 있도록 해준다. 이런 선순환이 지속되면 POJO 프로그래밍은 더 성숙된 결과로 나아가게 될 것이다. 그래서 필자는 POJO 프로그래밍의 꽃은 테스트 코드 작성이라고 생각한다. 
2005년에 프랑스의 온라인 세무시스템은 큰 변화를 겪었다. 기존에 EJB와 J2EE 기반으로 구현된 기존 시스템을 스프링과 하이버네이트를 사용하는 POJO 방식으로 전환한 것이다. 단 3개월 만에 시스템의 구조를 변환하면서 동시에 50개의 새로운 유즈케이스와 100개의 새로운 화면, 150개의 기존 화면을 전환시켰다. 
그 결과는 매우 성공적이었다. 손쉽게 작성할 수 있는 테스트와 간단한 패키징, 배포작업이 용이해졌고 지저분한 템플릿 코드를 제거하면서 생산성이 극대화되었다. 또한 기술적인 리스크가 줄고 일관성 유지가 쉬워졌으며 풍성한 도메인 모델을 적용하고 모든 레이어에 걸처 테스트를 작성하게 되어 큰 폭의 품질 향상이 이뤄졌다.
EJB나 무거운 기존 기술의 굴레에서 벗어나 객체지향 기술의 혜택을 가득 누릴 수 있는 즐거운 POJO 프로그래밍에 한번 도전해 보자. 혹시 지금 POJO 프레임워크를 사용해 개발하고 있다면 과연 자신이 만든 코드가 POJO의 원칙에 맞게 작성되었는지 살펴보자. 그리고 마지막에는 반드시 테스트를 작성하라. 그것이 POJO를 POJO답게 쓰게 하고 그 가치를 누리도록 도와줄 것이다.

 

원본 : http://cafe.daum.net/playtrade/9l2D/9 

http://lefthand29.blogspot.com/2012/02/pojo-plain-old-java-object-programming.html#!/2012/02/pojo-plain-old-java-object-programming.html

:

Spring - Bean 초기화 및 생명주기

FRAMEWORK/SPRING MVC 2014. 9. 29. 13:34

BeanFactory & ApplicationContext

bean factory - 스프링의 IoC 담당하는 핵심 컨테이너

빈을 등록/생성/조회/반환/관리한다보통은 bean factory  바로 사용하지 않고 이를 확장한 application context  이용한다. BeanFactory  bean factory  구현하는interface 이다. (getBean()등의 메소드가 정의되어 있음)

application context - bean factory 확장한 IoC 컨테이너

빈의 등록/생성/조회/반환/관리의 기능은 bean factory  같지만여기에 spring 각종 부가 서비스를 추가로 제공한다. ApplicationContext application context 구현해야 하는 interface이여, BeanFactory  상속한다

 


ApplicationContext가 추가적으로 지원하는 기능
1. MessageSource를 지원하여 애플리케이션에서 필요한 메시지들을 관리하는 것이 가능
2. Spring 프레임워크에서 관리하는 POJO 빈들 중 ApplicationListener를 구현하여 설정파일에 등록할 경우 등록된 리스너에게 이벤트를 전달하는 것이 가능

즉, ApplicationContext를 이용할 경우 애플리케이션내에 간단한 이벤트 기능을 구현하는 것이 가능

3. 애플리케이션내에서는 파일, URL과 같은 다양한 자원(Resource)에 접근할 필요가 있다.

ResourceLoader를 지원함으로 다양한 하위레벨의 자원을 Spring 프레임워크의 Resource 인스턴스로 생성하는 것이 가능

※ ApplicationContext를 직접 사용하는 방법을 알아둘 경우 테스트 프로그램을 작성할 때 유용하게 사용할 수 있다.

 

 

Spring 프레임워크에서 빈의 생명주기



 

POJO 초기화 과정
1. Spring 프레임워크는 먼저 빈 설정파일의 POJO 빈을 인스턴스화하지 않은 상태로 빈 설정 파일 정보를 초기화한다.

   빈 설정파일의 정보를 초기화하면서 XML DTD에 대하여 유효한지를 체크한다. 
2. 빈 인스턴스를 생성하면서 의존관계에 있는 빈이 존재하는지의 여부를 체크하여 의존 관계에 있는 빈이 존재하지

   않는다면 빈의 초기화가 실패한다. 
3. 의존관계에 있는 빈의 체크가 완료되면 setter 메써드를 호출하거나(Setter Injection의 경우), 생성자의 인자

   (Constructor Injection의 경우)로 실질적인 값을 추가하거나 다른 빈에 대한 레퍼런스(reference)를 전달한다. 
4. 생성한 빈이 BeanNameAware 인스턴스이면 setBeanName() 메소드를 호출한다. 
5. 생성한 빈이 BeanFactoryAware 인스턴스이면 setBeanFactory() 메소드를 호출한다. 
6. 생성한 빈이 ApplicationContextAware 인스턴스이면 setApplicationContext() 메소드를 호출한다.

   이 과정은 ApplicationContext를 이용하여 초기할 때만 실행된다. 
7. 생성한 빈이 InitializingBean 인스턴스이면 afterProperties() 메소드를 호출한다. 
8. 생성한 빈의 설정파일에 init-method가 설정되어 있다면 init-method에 해당하는 메소드를 호출한다

 

종료 메소드를 호출하는 과정
1. 생성한 빈이 DisposableBean 인스턴스이면 destory() 메소드를 호출한다. 
2. 생성한 빈의 설정파일에 destroy-method가 설정되어 있다면 destroy-method에 해당하는 메소드를 호출한다. 

POJO 빈에서 빈 설정 정보에 접근하기
POJO 빈에서 BeanNameAware, BeanFactoryAware, ApplicationContextAware 인터페이스 중 필요한 인터페이스를 상속하고 인터페이스의 메소드를 구현하면 된다.

 

빈의 생명주기 제어하기
Spring IoC 컨테이너의 Bean 초기화 및 소멸 작업의 커스트마이징을 하고싶을때 Spring의 초기화 콜백 메서드와 소멸 콜백 메서드를 인식하게하여 사용하면 되는데 그 방법으로는 아래 3가지 방법이 있다.

 
1. InitializingBean 과 DisposableBean 생명주기 인터페이스를 구현하고 초기화와 소멸작업을 위한

   afterPropertiesSet()과 destroy() 메소드를 구현.

2. Bean선언시(xml) init-method와 destroy-method 속성을 설정해 콜백 메소드의 이름을 지정한다.
3. Spring 2.5이후부터는 JSR-250(Common Annotations for the Java Plaform)에 정의되어 있는  

   @PostConstruct 와 @PreDestroy 생명주기 어노테이션을 사용하여 초기화 및 소멸 콜백 메서드를 적용할

   수 있다. 이 콜백 메소드를 호출하기 위해서는 IoC컨테이너에 CommonAnnotationBeanPostProcessor

   인스턴스가 등록돼 있어야한다.

 


빈 생명주기 제어의 간단한 예제
- Cashier Class는 장바구니의 Product들을 계산하는데 사용되며, 계산할 때마다 시각과 수량을 텍스트 파일에 기록한다. 이때 파일을 기록하기전 파일객체를 초기화 하고, 파일 작성후 소멸(close)시킨다.

 

Cashier Class

package com.apress.springrecipes.shop;


import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;

public class Cashier{
private String name;
private String path;
private BufferedWriter writer;

public void setBeanName(String beanName) {
this.name = beanName;
}

public String getPath() {
return path;
}

public void setPath(String path) {
this.path = path;
}

public void openFile() throws IOException {
File logFile = new File(path, name + ".txt");
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile, true)));
}

public void checkout(ShoppingCart cart) throws IOException {
double total = 0;
for (Product product : cart.getItems()) { total += product.getPrice(); }
writer.write(new Date() + "\t" + total + "\r\n");
writer.flush();
}

public void closeFile() throws IOException {
writer.close();
}
}

 

Bean Config

<beans ...>

...

<bean id=cashier1" class="com.apress.springrecipes.shop.Cashier">

<property name="name" value="casher1" />

<property name="name" value="casher1" />

</bean>

</beans>

 

 

1. InitializingBean과 DisposableBean 인터페이스 구현

...

import org.springframework.beans.factory.InitializingBean;

import org.springframework.beans.factory.DisposableBean;


public class Cashier implements InitializingBeanDisposableBean{

...

public void afterPropertiesSet() throws Exception {

openFile();

}

 

public void destroy() throws Exception {

closeFile();

}

}

 

위와 같이 스프링에 종속된 인터페이스를 구현하면 빈이 스프링에 종속되므로 스프링 IoC 컨테이너 외부에서 재사용하지 못한다.

 

 

2. init-method와 destroy-method 속성 설정

<bean id="cashier1" class="com.apress.springrecipes.shop.Cashier" init-method="openFiledestroy-method="closeFile">

<property name="name" value="cashier1" />

<property name="path" value="c:/cashier" />

</bean>

 

 

3. @PostConstruct와 @PreDestroy 어노테이션 적용

package com.apress.springrecipes.shop;

...

import javax.annotation.PostConstruct;

import javax.annotation.PreDestroy;


public class Cashier {

...

@PostConstruct

public void openFile() throws IOException {

File logFile = new File(path, name + ".txt");

writer = new BufferedWriter(new OutputStreamWriter(

new FileOutputStream(logFile, true)));

}

...

@PreDestroy

public void closeFile() throws IOException {

writer.close();

}

}

그리고 CommonAnnotationBeanPostProcessor 인스턴스를 IoC 컨테이너에 추가하면 된다.

 

<beans ...>

<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />

<bean id="cashier1" class="com.apress.springrecipes.shop.Cashier">

<property name="name" value="cashier1" />

<property name="path" value="c:/cashier" />

</bean>

</beans>

 

 

 

참조 :  http://www.javajigi.net/pages/viewpage.action?pageId=2621452http://ultteky.egloos.com/10795411

'FRAMEWORK > SPRING MVC' 카테고리의 다른 글

Spring - Transaction1 (스프링의 트랜잭션 지원)  (0) 2014.09.29
Spring - POJO  (0) 2014.09.29
Spring - Bean Scope  (0) 2014.09.29
Spring - IoC & DI  (0) 2014.09.29
Spring - AOP 개념 정리  (0) 2014.09.29
:

Spring - Bean Scope

FRAMEWORK/SPRING MVC 2014. 9. 29. 13:33

Bean Scope

Bean의 정의는 실제 bean 객체를 생성하는 방식을 정의하는 것이다.

Class와 마찬가지로 하나의 Bean 정의에 해당하는 다수의 객체가 생성될 수 있다.

Bean 정의를 통해 객체에 다양한 종속성 및 설정값을 주입할 수 있을 뿐 아니라, 객체의 범위(scope)를 정의할 수 있다. Spring Framework는 5가지 scope을 제공한다.

(그 중 3가지는 web-aware ApplicationContext를 사용할 때 이용할 수 있다).

 

 Scope

 설명

 singleton

 하나의 Bean 정의에 대하여 Spring IoC Container 내에 단 하나의 객체만 존재

 prototype

 하나의 Bean 정의에 대하여 다수의 객체가 존재 할 수 있다.

 request

 하나의 Bean 정의에 대하여 하나의 HTTP request의 생명주기 안에 단 하나의 객체만 존재

 즉, 각각의 HTTP Request는 자신만의 객체를 가진다.

 Web-aware String ApplicaiontContext 안에서만 유효하다.

 session

 하나의 Bean 정의에 대해서 하나의 HTTP Session의 생명주기 안에 단 하나의 객체만 존재

 Web-aware String ApplicaiontContext 안에서만 유효하다.

 global

 session

 하나의 Bean 정의에 대하여 하나의 global HTTP Session의 생명주기 안에 단 하나의 객체

 일반적으로 portlet context 안에서 유효하다

 Web-aware String ApplicaiontContext 안에서만 유효하다.

 

 

[ Singleton Scope ]

Bean이 singleton인 경우, 단지 하나의 공유 객체만 관리된다. Singleton은 Spring의 기본 scope이다.

Singleton은 객체가 하나만 생성되는 싱글톤 객체에 DI를 하게되면 DI 된 객체도 한 번 밖에 세팅되지 않기 때문에Dependency Injection (이후 DI)을 사용할 수 없다.

<bean id="beanTest" class="test.BeanTest/> // default가 singleton이기에 생략 가능
<bean id="beanTest" class="test.BeanTest" scope="singleton"/>
<bean id="beanTest" class="test.BeanTest" singleton="true"/>

 

Singleton Scope의 간단한 예제

BeanTest.java

package test;
public class BeanTest {    
    public BeanTest(){}
}


ExamMain.java

package test.;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class ExamMain {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("/src/test/exam06.xml");
        BeanTest beanTest1 = (BeanTest)context.getBean("beanTest");
        BeanTest beanTest2 = (BeanTest)context.getBean("beanTest");
        BeanTest beanTest3 = (BeanTest)context.getBean("beanTest");
  
        System.out.println("beanTest1 : " + beanTest1);
        System.out.println("beanTest2 : " + beanTest2);
        System.out.println("beanTest3 : " + beanTest3);
    }
}

 

exam06.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTest" class="test.BeanTest" scope="singleton" />
</beans>

 

결과 화면



 

위 소스의, exam06.xml 설정 파일을 보면 빈을 생성할 때 scope="singleton" 속성으로 지정되어 있다.

Singleton으로 생성되어 있기 때문에 getBean()으로 각각 호출한 beanTest1~3이 모두 동일한 빈 객체를 참조하고 있음을 알수 있다.

 



 

Singleton 패턴과 스프링의 Singleton 차이

http://toby.epril.com/?p=849 <= 참조 (깔끔하게 정리를 하고 싶은데,,,정리가 안된다,,,;;)

 

 

[ Prototype Scope ]

Singleton이 아닌 prototype scope의 형태로 정의된 bean은 필요한 매 순간 새로운 bean 객체가 생성된다

<bean id="beanTest" class="test.BeanTest" scope="prototype"/>

<bean id="beanTest" class="test.BeanTest" singleton="false"/>

 

Prototype scope을 사용할 때 염두에 두고 있어야 할 것이 있다.

Spring은 prototype bean의 전체 생명주기를 관리하지 않는다 - container는 객체화하고, 값을 설정하고, 다른 prototype 객체와 조립하여 Client에게 전달 한 후 더 이상 prototype 객체를 관리하지 않는다.

즉, scope에 관계없이 초기화(initialization) 생명주기 callback 메소드가 호출되는 반면에, prototype의 경우 파괴(destruction) 생명주기 callback 메소드는 호출되지 않는다. 이것은 client 코드가 prototype 객체를 clean up하고 prototype 객체가 들고 있던 리소스를 해제하는 책임을 가진다는 것을 의미한다.

 

Prototype Scope의 간단한 예제

exam06.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTest" class="test.BeanTest" scope="prototype" />
</beans>

 

결과화면



 

위의 scope속성을 위와 같이 prototype으로 수정해주면 빈을 사용할 때마다 빈을 새로 생성하기 때문에 서로 다른 객체를 참조하고 있음을 알 수 있다.

 



 

 

[ 기타 Scope ]

requestsessionglobal session scope들은 반드시 web-based 어플리케이션에서 사용할 수 있다.

기본 Web 설정(Initial web configuration)
requestsessionglobal session scope을 사용하기 위해서는 추가적인 설정이 필요하다.

 

추가 설정은 사용할 Servlet 환경에 따라 달라진다.만약 Spring Web MVC 안에서 bean에 접근할 경우, 즉 Spring DispatcherServlet 또는 DispatcherPortlet에서 처리되는 요청인 경우, 별도의 추가 설정은 필요없다.

DispatcherServlet과 DispatcherPortlet은 이미 모든 관련있는 상태를 제공한다.

 

만약 Servlet 2.4+ web container를 사용하고, JSF나 Struts 등과 같이 Spring의 DispatcherServlet의 외부에서 요청을 처리하는 경우, 다음javax.servlet.ServletRequestListener를 'web.xml' 파일에 추가해야 한다.

<web-app>
     ... 
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    ...
</web-app>

 

만약 다른 오래된 web container(Servlet 2.3)를 사용한다면, 제공되는 javax.servlet.Filter 구현체를 사용해야 한다. (filter mapping은 web 어플리케이션 설정에 따라 달라질 수 있으므로, 적절히 수정해야 한다.)

<web-app>
    ...
    <filter>
        <filter-name>requestContextFilter</filter-name> 
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping> 
        <filter-name>requestContextFilter</filter-name> 
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...
</web-app>

 

 

Request scope

<bean id="beanTest" class="test.BeanTest" scope="request"/>

 

위 정의에 따라, Spring container는 모든 HTTP request에 대해서 'loginActionbean 정의에 대한 LoginAction 객체를 생성할 것이다. 즉, 'loginActionbean은 HTTPrequest 수준에 한정된다(scoped). 요청에 대한 처리가 완료되었을 때, 한정된(scoped) bean도 폐기된다.

 

 

session scope

<bean id="beanTest" class="test.BeanTest" scope="session"/>

 

위 정의에 따라, Spring container는 하나의 HTTP Session 일생동안 'userPreferencesbean 정의에 대한 UserPreferences 객체를 생성할 것이다. 즉, 'userPreferences'bean은 HTTP Session 수준에 한정된다(scoped). HTTP Session이 폐기될 때, 한정된(scoped) bean로 폐기된다

 

 

global session scope

<bean id="beanTest" class="test.BeanTest" scope="globalSession"/>

 

global session scope은 HTTP Session scope과 비슷하지만 단지 portlet-based web 어플리케이션에서만 사용할 수 있다. Portlet 명세(specifications)는 global Session을 하나의 portlet web 어플리케이션을 구성하는 여러 portlet들 모두가 공유하는 것으로 정의하고 있다. global session scope으로 설정된 bean은 global portlet Session의 일생에 한정된다

 

 

 

 

 

참조목록

http://snoopy81.tistory.com/170

http://openframework.or.kr/framework_reference/spring/ver2.x/html/beans.html

http://www.egovframe.org/wiki/doku.php?id=egovframework:rte:fdl:ioc_container:bean_scope

'FRAMEWORK > SPRING MVC' 카테고리의 다른 글

Spring - POJO  (0) 2014.09.29
Spring - Bean 초기화 및 생명주기  (0) 2014.09.29
Spring - IoC & DI  (0) 2014.09.29
Spring - AOP 개념 정리  (0) 2014.09.29
Spring MVC 와 DispatcherServlet  (0) 2014.09.29
:

Spring - IoC & DI

FRAMEWORK/SPRING MVC 2014. 9. 29. 13:31

IoC(Inversion of Control - 제어의 역전)란?

IoC란 간단하게 말하여 프로그램의 제어 흐름 구조가 바뀌는 것이다.

일반적으로, main() 같은 프로그램이 시작되는 지점에서 다음에 사용할 오브젝트를 결정, 생성하고, 만들어진 오브젝트 내의 메소드를 호출하는 작업을 반복한다. 이런 구조에서 각 오브젝트느 프로그램 흐름을 결정하거나 사용할 오브젝트를 구성하는 작업에 능동적으로 참여한다.

즉, 모든 종류의 작업을 사용하는 쪽에서 제어하는 구조이다.

 

이에 반하여 IoC는 제어 흐름의 개념을 거꾸로 뒤집는다. 오브젝트는 자신이 사용할 오브젝트를 스스로 생성하거나 선택하지 않는다. 그리고 자신이 어떻게 만들어지고 어디서 사용되는지 알 수 없다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하는 것이다. 프로그램의 시작을 담당하는 main()같은 엔트리 포인트를 제외하면 모든 오브젝트는 이런 방식으로 위임받은 제어 권한을 갖는 특별한 오브젝트에 의해 결정되고 만들어지는 것이다.

 

위의 내용을 토대로 IoC를 요약하여 말하자면 아래와 같이 정의할 수 있다.

- 작업을 수행하는 쪽에서 Object 를 생성하는 제어 흐름의 개념을 거꾸로 뒤집는다.
- IoC 에서는 Object 가 자신이 사용할 Object 를 생성하거나 선택하지 않는다.
- 또한 Object 는 자신이 어떻게 생성되고 어떻게 사용되는지 알 수 없다.
- 모든 Object 는 제어 권한을 위임받는 특별한 Object 에 의해서 만들어 지고 사용된다. 

 

 

IoC 구현 방법

  • DL (Dependency Lookup)  - 의존성 검색
            저장소에 저장되어 있는 빈(Bean)에 접근하기 위하여 개발자들이 컨테이너에서 
    제공하는 API 를 이용하여 
            사용하고자 하는 빈(Bean) 을 Lookup 하는 것
  • DI (Dependency Injection) - 의존성 주입
            각 계층 사이, 각 클래스 사이에 필요로 하는 의존 관계를 컨테이너가 자동으로 연결해주는것
            각 클래스 사이의 의존 관계를 빈 설정(Bean Definition) 정보를 바탕으로 컨테이너가 자동으로 연결해 주는 것
            DL 사용 시 컨테이너 종속성이 증가하여, 이를 줄이기 위하여 DI를 사용
        Setter Injection - 인자가 없는 생성자나 인자가 없는 static factory 메소드가 bean을 인스턴스화 하기 위하여 
                              호출된 후 bean의 setter 메소드를 호출하여 실체화하는 방법
                              - 객체를 생성 후 의존성 삽입 방식이기에 구현시에 좀더 유연하게 사용할 수 있다.
                              - 세터를 통하여 필요한 값이 할당되기 전까지 객체를 사용할 수 없다.
                              - Spring 프레임워크의 빈 설정 파일에서 property 사용
  • public class SimpleMovieister{

    private MovieFinder movieFinder;  

    public void setMovieFinder(MovieFinder movieFinder){

    this.movieFinder = movieFinder;

    }

    }


        Constructor Injection - 생성자를 이용하여 클래스 사이의 의존 관계를 연결
                              - 생성자에 파라미터를 지정함으로 생성하고자하는 객체가 필요로 하는 것을 명확하게 알 수 있다.
                              - Setter메소드를 제공하지 않음으로 간단하게 필드를 불변 값으로 지정이 가능하다.
                              - 생성자의 파라미터가 많을 경우 코드가 복잡해 보일 수 있다.
                              - 조립기 입장에서는 생성의 순서를 지켜야 하기에 상당히 불편하다.
                              - Spring 프레임워크의 빈 설정 파일에서 constructor-arg 사용

    public class SimpleMovieister{

    private MovieFinder movieFinder;

    public SimpleMovieLister(MovieFinder movieFinder){

    this.movieFinder = movieFinder;

    }

    }

     

        Method Injection - Singleton 인스턴스와 Non Singleton 인스턴스의 의존 관계를 연결 시킬 필요가 있을 
                              경우 사용하지만, 많이 사용하지는 않는다.

     

     

    의존성 주입(DI)의 간단한 예제

     

    Setter Injection의 예제

    <bean id="exampleBean" class="examples.ExampleBean">
        <property name="beanOne"><ref bean="anotherExampleBean"/></property>
        <!-- setter injection using the neater 'ref' attribute -->
        <property name="beanTwo" ref="yetAnotherBean"/>
        <property name="integerProperty" value="1"/>
    </bean>

    <bean id="anotherExampleBean" class="examples.AnotherBean"/>
    <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

     

    public class ExampleBean {
        private AnotherBean beanOne;
        private YetAnotherBean beanTwo;
        private int i;

        public void setBeanOne(AnotherBean beanOne)
            this.beanOne = beanOne;

        public void setBeanTwo(YetAnotherBean beanTwo)
            this.beanTwo = beanTwo;

        public void setIntegerProperty(int i)
            this.i = i;
    }

     

     

    Constructor Injection의 예제

    <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
        <constructor-arg ref="yetAnotherBean"/>    
               // <constructor-arg><ref bean="yetAnotherBean"/></constructor-arg> 위와 동일
        <constructor-arg type="int" value="1"/>
    </bean>

    <bean id="anotherExampleBean" class="examples.AnotherBean"/>
    <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

     

    public class ExampleBean {
        private AnotherBean beanOne;
        private YetAnotherBean beanTwo;
        private int i;
        
        public ExampleBean(
            AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
            this.beanOne = anotherBean;
            this.beanTwo = yetAnotherBean;
            this.i = i;
        }
    }

     

     

    IoC 용어 정리

    bean - 스프링에서 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트

    자바빈, EJB 빈과 비슷한 오브젝트 단위의 애플리케이션 컴포넌트를 말한다하지만 스프링을 사용하는 애플리케이션에서 만들어지는 모든 오브젝트가 빈은아니다스프링의 빈은 스프링 컨테이너가 생성과 관계설정사용등을 제어해주는 오브젝트를 가리킨다.


    bean factory - 스프링의 IoC 담당하는 핵심 컨테이너

    빈을 등록/생성/조회/반환/관리한다보통은 bean factory  바로 사용하지 않고 이를 확장한 application context  이용한다. BeanFactory  bean factory  구현하는interface 이다. (getBean()등의 메소드가 정의되어 있음)

     

    application context - bean factory 확장한 IoC 컨테이너

    빈의 등록/생성/조회/반환/관리의 기능은 bean factory  같지만여기에 spring 각종 부가 서비스를 추가로 제공한다. ApplicationContext application context 구현해야 하는 interface이여, BeanFactory  상속한다.

     

    configuration metadata (설정정보/설정 메타정보)

    application context 혹은 bean factory  IoC 적용하기 위해 사용하는 메타정보

    스프링의 설정정보는 컨테이너에 어떤 기능을 세팅하거나 조정하는 경우에도 사용하지만 주로 bean  생성/구성하는 용도로 사용된다.

     

    container (IoC container)

    IoC 방식으로 bean 관리한다는 의미에서 bean factory  application context  가리킨다
    (spring container = application context) 
    application context
      자체로 ApplicationContext 인터페이스를 구현한 오브젝트를 가리키기도 하는데하나의 애플리케이션에 보통 여러개의 ApplicationContext Object 만들어진다이를 통칭해서 strping container라고 부를  있다.

    간단하게, 객체를 관리하는 컨테이너이다, 컨테이너에 객체를 담아 두고, 필요할 때에 컨테이너로부터 객체를 가져와 사용할 수 있도록 하고 있다.

     

    spring framework - IoC container, application context  포함해서 spring 제공하는 모든 기능을 통칭한다.

     

     

    클래스 호출방식

    일반적인 클래스 호출



    클래스내에 선언과 구현이 한몸이기때문에 다양한 형태로 변화가 불가능하다.

     

    인터페이스를 이용한 클래스 호출



    클래스를 인터페이스와 구현클래스로 분리를 하였다.
    구현클래스 교체가 용이하여 다양한 형태로 변화가 가능
    하지만 구현클래스 교체시 호출 클래스의 소스를 수정해야한다.

     

    팩토리패턴을 이용한 클래스 호출방식



    팩토리방식은 팩토리가 구현클래스를 생성하므로 클래스는 팩토리를 호출하는 코드로 충분하다.
    구현클래스 변경시 호출클래스에는 영향을 미치지 않고, 팩토리만 수정하면 된다.

    하지만 클래스에 팩토리를 호출하는 소스가 들어가야한다. 그것 자체가 팩토리에 의존함을 의미한다

     

    IoC를 이용한 클래스 호출 방식



    팩토리패턴의 장점을 더하여 어떠한것에도 의존하지 않는 형태로 구성이 가능하다.
    실행 시점에 클래스간의 관계가 형성이 된다. 
    즉 의존성이 삽입된다는 의미로 IoC를 DI(Dependency Injection) 라는 표현으로 사용한다.

     

     

    참조목록

    http://openframework.or.kr/framework_reference/spring/ver2.x/html/beans.html
    http://lefthand29.blogspot.com/2012/02/ioc-inversion-of-control-spring-ioc_22.html#!/2012/02/ioc-inversion-of-control-spring-ioc_22.html

    http://blog.daum.net/kitepc/557
    http://www.javajigi.net/pages/viewpage.action?pageId=5614#SpringIoC%EA%B0%9C%EB%85%90%EB%B0%8F%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%95%88-1.IoC%EC%9D%98%EA%B0%9C%EB%85%90
    http://code.google.com/p/developerhaus/wiki/TobySpring_10#10.2_IoC/DI를_위한_빈_설정_메타정보_작성

    http://trypsr.springnote.com/pages/251122

    'FRAMEWORK > SPRING MVC' 카테고리의 다른 글

    Spring - POJO  (0) 2014.09.29
    Spring - Bean 초기화 및 생명주기  (0) 2014.09.29
    Spring - Bean Scope  (0) 2014.09.29
    Spring - AOP 개념 정리  (0) 2014.09.29
    Spring MVC 와 DispatcherServlet  (0) 2014.09.29
    :