http://www.theserverside.com/articles/article.tss?l=SpringFramework

<div class="note"> 이 문서는 Spring프레임워크 소개문서입니다. 기존에 1.0버전을 기준으로 작성된 소개문서가 있으나 많은 부분이 업데이트되어 새로운 페이지에 번역작업을 수행합니다. 기존 소개문서와 비교해 보시면 처음 로드맵과 1.2버전에 들어와서 로드맵이 어떻게 번경이 되었는지 살펴보는것도 굉장히 큰 도움이 되리라 생각합니다. </div>

SpringFramework.gif

2005년 5월

이 글은#

2003년 10월 이 글의 첫번째 버전이 나간후 Spring프레임워크는 대중적으로 꾸준한 성장을 하고 있다. 이것은 1.0버전을 통해 현재 1.2버전까지 다루고 있으며 산업적 프로젝트의 넓은 범위에서 채택되고 있다. 이 글에서 나는 Spring이 달성한 것과 당신이 J2EE애플리케이션을 개발하는데 도움을 준다고 믿는 이유에 대해서 이야기를 할것이다.

아직도 다른 프레임워크를 사용하는가.?#

당신은 "다른 프레임워크 없이" 라는 생각을 할지도 모른다. 당신은 왜 이 글을 읽거나 Spring프레임워크를 다운로드 받는가.? 언제 많은 J2EE프레임워크가 있었고 언제 당신 자신만의 프레임워크를 빌드할수 있었는가.? 커뮤니티내 지속적인 높은 레벨의 관심사항은 Spring이 뭔가 가치있는 것을 제공해야만 한다는 하나의 표시이다. 여기엔 수많은 기술적 이유가 있다.

나는 여러가지 이유에서 Spring이 유일하다고 믿는다.

  • 이것은 다른 유명한 프레임워크가 하지 않는 중요한 위치를 차지한다. Spring은 당신의 비지니스 객체를 관리하기 위한 방법을 제공하는데 집중한다.
  • Spring은 포괄적이고 모듈적이다. Spring은 다른 부분에는 상관없이 특정부분만을 선택해서 사용할수 있고 내부적으로는 일관적인 계층화된 구조를 가지고 있다. 그래서 당신은 학습과정을 통해 최대한의 잇점을 얻을수 있다. 당신은 예를 들면 JDBC의 사용을 단순화하거나 당신의 모든 비지니스 객체를 관리하가 위해서 Spring을 사용할수도 있다. 그리고 현재 존재하는 프로젝트로 Spring을 새로 도입하는것은 쉽다.
  • Spring은 테스트되기 쉽게 코드를 작성하도록 당신을 돕기 위해 디자인되었다. Spring은 테스트지향 프로젝트에는 이상적인 프레임워크이다.
  • Spring은 증가하는 중요한 통합 기술이다. 이것의 역활은 다양한 큰 업체로 부터 인정받았다.

Spring은 당신의 프로젝트를 위한 하나 이상의 프레임워크의 의존성을 필요로 하지 않는다. Spring은 전형적인 애플리케이션의 가장 중요한 기반관계를 차지하는 잠재적인 one-stop가게이다. 이것은 또한 다른 프레임워크가 하지 않는 분야를 담당한다.

2003년 2월 이후 오프소스 프로젝트는 오랜기간동안 지속되어 왔다. 오픈소스 프로젝트는 내 책인 Expert One-on-One J2EE Design and Development에서 나온 기반코드로부터 시작되었다. 늦은 2002년 Expert One-on-One J2EE는 Spring의 배후의 기본적인 구조적 생각을 표현한다. 어쨌든 구조적 개념은 이른 2000년 으로 돌아간다. 그리고 많은 성공적이었던 상업적 프로젝트를 위한 기반구조를 개발하면서 생긴 나의 경험을 반영한다.

2003년 1월 이후 Spring은 소스포지에 호스팅되었다. 현재 Spring개발과 지원에 모든 시간을 할애하는 20명의 개발자가 있다. 크게 발전한 오픈소스 커뮤니티는 그들이 개별적으로 달성할수 있는 것보다 더 많은것을 달성하도록 돕고 있다.

Spring의 구조적 이득#

우리가 밑에서 설명을 하기 전에 Spring이 프로젝트로 가져올수 잇는 몇가지 이득을 알아보자.

  • Spring은 당신이 EJB를 사용하든지 말든지 당신의 미들티어 객체를 효과적으로 구성할수 있다. Spring은 특정 J2EE API를 연동하기 위해서 Struts또는 다른 프레임워크를 사용한다면 당신을 도와줄것이다. 그리고 이것이 미들티어내 대부분 가치있는 것처럼 Spring의 설정관리서비스는 어떠한 실행환경내에서 구조적 레이어내에서 사용될수 있다.
  • Spring은 많은 프로젝트에서 보여지는 싱글톤의 증식을 제거할수 있다. 나의 경험으로 볼때 이것은 테스트기능과 객체지향을 줄이는 심각한 문제다.
  • Spring은 애플리케이션과 프로젝트 도처에 일관적인 방법으로 설정을 다룸으로써 사용자정의 프라퍼티 파일 형식을 사용해야 하는 필요성을 제거할수 있다. 특정 클래스를 찾고 Javadoc이나 소스코드를 읽기 위한 마법같은 프라퍼티키나 시스템 프라퍼티를 걱정하지 말라. Spring을 사용하면 당신은 간단히 클래스의 자바빈즈 프라퍼티나 생성자 인자를 찾는다. Inversion of Control(이하 IoC)과 Dependency Injection(이하 DI)의 사용은 이러한 간단함을 달성하도록 도와준다.
  • Spring은 클래스보다 인터페이스를 사용하여 프로그래밍의 비용을 줄임으로써 좋은 프로그래밍 상황을 촉진할수 있다.
  • Spring은 가능한한 이 API의 최소한에만 의존하도록 빌드된 애플리케이션을 디자인한다. Spring애플리케이션내 대부분의 비지니스 객체는 Spring에 의존성을 가지지 않는다.
  • Spring을 사용하여 빌드된 애플리케이션은 단위 테스트하기 매우 쉽다.
  • Spring은 애플리케이션 구조의 결정보다 EJB구현 선택을 사용하도록 만들수 있다. 당신은 호출 코드의 영향없이 POJO나 로컬 EJB처럼 비지니스 인터페이스 구현을 선택할수 있다.
  • Spring은 EJB없이 많은 문제를 해결하도록 당신을 도와준다. Spring은 많은 애플리케이션을 위해 선호되는 EJB의 대안을 제공할수 있다. 만약 당신이 하나의 데이터베이스와 작업을 할 필요가 있다면 Spring은 EJEB컨테이너의 사용없이(JTA구현조차도 없는 상태에서) 선언적인 트랜잭션 관리를 달성하기 위한 AOP를 사용할수 있다.
  • Spring은 JDBC또는 TopLink, Hibernate또는 JDO구현과 같은 O/R맵핑을 사용하여 데이터접근을 위한 일관적인 프레임워크를 제공한다.
  • Spring은 많은 면에서 일관적이고 간단한 프로그래밍 모델을 제공한다. 이것은 이상적인 구조인 "glue"를 만든다. 당신은 JDBC, JMS, JavaMail, JNDI 그리고 많은 다른 중요한 API를 위해 Spring접근법내에 일관성을 보여줄수 있다.

Spring은 본질적으로 POJO를 사용하여 애플리케이션을 빌드하는 것을 가능하도록 하는 전용 기술이다. 이것은 개발자로부터 복잡한것을 숨기는 정교한 프레임워크를 요구하는 매력적인 목표이다.

게다가 Spring은 당신의 문제를 위한 가장 간단한 가능성있는 솔루션을 구현하도록 해준다.

Spring은 무엇을 하는가.?#

Spring은 많은 기능을 제공한다. 그래서 나는 순서대로 빨리 중요한 기능을 리뷰할것이다.

Mission statement#

첫번째로 Spring의 범위에 대해서 알아보자. 비록 Spring이 많은 부분을 담당한다고 하더라도, 우리는 이것이 위치하고 위치하지 않는 것에 대해서 분명한 것을 제시할것이다.

Spring의 가장 중요한 목표는 J2EE를 좀더 사용하기 쉽도록 하고 좋은 프로그래밍 형태를 촉진하는 것이다. 이것은 넓은 범위의 환경내 적용가능한 POJO기반의 프로그래밍 모델을 가능하게 함으로써 이것을 수행한다.

Spring은 외형적인 어떤것을 고치지 않는다. 게다가 당신은 Spring내에서 로깅, 커넥션 풀링, 분산 트랜잭션 코디네이터 패키지를 찾을수 없을것이다. 이런 모든것은 오픈소스 프로젝트(Commons Logging나 Commons DBCP와 같은)나 당신의 애플리케이션 서버에 의해서 제공된다. 이러한 이유로 우리는 O/R 맵핑 레이어를 제공하지 않는다. 여기엔 TopLink, Hibernate와 JDO와 같은 좋은 솔루션이 있다.

Spring은 존재하고 있는 기술들을 좀더 사용하기 쉽도록 하는데에도 목적이 있다. 예를 들면 비록 우리가 하위 레벨 트랜잭션 코디네이터의 비지니스내에서가 아니더라도 우리는 JTA나 다른 트랜잭션 전략위의 추상적 레이어를 제공한다.

Spring은 우리가 어떤 새로운것을 제공할수 있다는 느낌을 받지 않는다면 다른 오픈소스 프로젝트와 직접적으로 경쟁하지 않는다. 예를 들면 많은 개발자들처럼 우리는 Struts와 결코 행복하지 않고 MVC웹 프레임워크내 진보된 형태의 패지키을 가진다(Spring MVC채용이 빠르게 증가하고 있다 이것은 우리의 의도에 많은 이들이 동의를 하는것처럼 보인다.). 가벼운 IoC컨테이너와 AOP프레임워크같은 몇가지 점에서 Spring은 직접적인 경쟁을 가지지만 좀더 보편화된 솔루션은 없다.

Spring은 내부적인 일관성으로 부터 이익을 가진다. 모든 개발자는 같은 점에서 노래를 부른다. 우리는 다양한 범위에서 Inversion of Control같은 몇몇 중심이 되는 개념을 사용할수 있다.

Spring은 애플리케이션 서버 사이에 이식가능하다. 물론 이식가능성은 언제나 변한다. 하지만 우리는 개발자의 시각에서 플랫폼에 종속적이거나 표준이 아닌것 그리고 WebLogic, Tomcat, Resin, JBoss, WebSphere 그리고 다른 애플리케이션 서버에서 사용자를 지원하는 부분은 피한다. Spring은 침략적이지 않고 POJO이며 이식가능성의 희생없이 환경특유의 장점을 우리에게 얻도록 도와주는 접근법이다. Spring 1.2내 Weblogic의 향상된 트랜잭션 관리 기능의 경우에 이 사항아래 BEA특성의 API를 사용한다.

Inversion of control 컨테이너#

Spring디자인의 핵심은 자바빈즈와 함께 작동하도록 디자인된 org.springframework.beans패키지이다. 이 패키지는 사용자에 의해서 직접적으로 사용되지는 않는다. 하지만 다른 수많은 기능을 지원하는 것처럼 제공한다.

추상화의 다음으로 높은 레이어는 "Bean Factory"이다. Spring bean factory은 객체가 이름에 의해서 가져올수 있고 객체들 사이에 관계를 관리할수 있는 일반적인 factory이다.

bean factory은 두가지의 객체 모델을 제공한다.

  • "싱클톤" : 이 경우에는 특정 이름으로 룩업을 통해 가져올수 있는 객체의 공유 인스턴스가 하나 있다. 이것은 디폴트이다 그리고 매우 자주 사용된다. 이것은 비상태유지 서비스 객체들에게 이상적이다.
  • "프로토타입 또는 싱글톤이 아닌것(non-singleton)" : 이 경우에는 각각의 검색이 독립적인 객체의 생성한다. 예를 들면 이것은 그들 자신의 구별되는 객체 참조를 가지기 위해 각각의 호출자를 사용할수 있다.

Spring컨테이너가 객체들 사이의 관계를 관리하기 때문에 이것은 관리되는 POJO와 핫스와핑(hot swapping)지원을 위해 투명한 풀링과 같은 서비스를 통해 필요한 곳에 값을 추가할수 있다. 컨테이너는 호출자에 대한 영향과 안전한 쓰레드의 특성의 손실없이 수행시간에 교환되기 위한 참조의 목표를 허락하는 간접적인 방법을 소개한다. DI의 아름다움중 하나는 API의 포함없이 가능한 명료함이라는 것이다.

org.springframework.beans.factory.BeanFactory은 간단한 인터페이스이기 때문에 이것은 다른 방법으로 구현될수 있다. BeanDefinitionReader인터페이스는 그들 스스로 BeanFactory구현으로부터 메터데이터 형태를 구분한다. 그래서 일반적인 BeanFactory구현은 Spring이 메터데이터의 다른 타입과 함께 사용될수 있다. 당신은 몇몇의 사용자만이 필요성을 알게 되더라도 당신 자신의 BeanFactory나 BeanDefinitionReader를 쉽게 구현할수 있다. 대부분 공통적으로 사용되는 BeanFactory정의는

  • XmlBeanFactory. 이것 파서는 간단하다. 직관적인 XML구조는 명명된 객체의 클래스와 프라퍼티를 정의한다. 우리는 좀더 쉽게 사용하도록 DTD를 제공한다.
  • ListableBeanFactoryImpl: 이것은 프라퍼티 파일내에 bean 정의들을 파싱하고 프로그램적으로 BeanFactory들을 생성하기 위한 능력을 제공한다.

각각의 bean 정의는 POJO(클래스명과 자바빈즈 초기화 프라퍼티에 의해 정의되는)나 FactoryBean이 될수 있다. FactoryBean인터페이스는 우회의 단계를 추가한다. 전형적으로 이것은 AOP나 다른 접근법을 사용해서 프록시 객체를 생성하는데 사용된다. 선언적인 트랜잭션 관리를 추가하는 프록시(이것은 개념적으로 EJB인터셉터와 유사하지만 실제로는 좀더 간단하다)가 그 예이다. 이것은 기본적으로 EJB인터셉션과 유사하다. 하지만 실제상황에서는 좀더 간단하고 강력하게 작동한다. .

BeanFactory는 상위로 부터 "상속" 정의처럼 계층적으로 관계한다. 이것은 전체 애플리케이션을 통해 컨트롤러 서블릿과 같은 개별적인 자원들이 그들 자신만의 독립적인 객체집합을 가지는 동안 공통적인 설정의 공유를 가능하게 한다.

자바빈즈의 사용을 위한 동기는 ServerSide에서 얻을수 있는 Expert One-on-One J2EE Design and Development의 4장을 설명하는 무료 free PDF(/articles/article.tss?l=RodJohnsonInterview) 에서 설명된다.

bean factory개념을 통해 Spring은 Inversion of Control컨테이너이다.(EJB컨테이너와 같은 무거운 컨테이너를 좋아하지 않는다. Spring BeanFactory는 한줄의 코드로 생성될수 있는 컨테이너이고 어떠한 특정 배치단계를 요구하지 않는다.)

Inversion of Control의 개념은 종종 할리우드 원리(나를 호출하지 말라. 내가 당신을 호출할것이다.)내에서 종종 표현된다. IoC는 프레임워크내부로 어떤것이 생성되도록 책임을 넘기고 애플리케이션 코드내에서 빠져나온다. EJB와 같은 전통적인 컨테이너 구조내에서 컴포넌트는 컨테이너에게 "객체 X가 있는 곳, 내가 나의 작업을 수행할 필요가 있는 것"이라고 말한다. IoC는 컨테이너에게 객체 X가 필요할때 런타임시 그것을 제공한다. 컨테이너는 메소드 시그너처에 기반해서 이 것을 생성하지 않고 가능한한 XML과 같은 설정 데이터를 사용한다.

나의 경험과 Spring사용자의 경험에서 이것은 IoC가 애플리케이션 코드로 가져다 주는 이익을 지나치게 강조하는것은 힘들다.

DI는 컨테이너 API(공동으로 사용되는 객체나 애플리케이션 객체 인스턴스로 설정값을 넣어주는것과 같은 의존성 삽입을 위해 사용되는 통상적인 자바 메소드들)에 명시적인 의존성을 제거하는 IoC의 형태이다. 설정이 EJB와 같은 전통적인 컨테이너 구조내 있는 동안 이러한 방법에 관여하는 곳에서 컴포넌트는 "객체 X가 있는곳이 내가 작업할 필요가 있는 것이다."라고 컨테이너에게 알릴것이다. DI를 사용하면 컨테이너는 객체 X에 의해서 필요한 컴포넌트를 찾고 실행시 이것을 제공한다. 컨테이너는 메소드 시그너처(자바빈즈 프라퍼티또는 생성자)와 XML과 같은 설정 데이터에 기반해서 이것을 찾는다.

DI의 두가지 방식은 Setter Injection(자바빈즈 setter메소드에 의한)과 Constructor Injection(생성자메소드의 인자를 통한)이다. Spring은 두가지 방식에 대한 정교한 지원을 제공한다. 또한 객체를 설정할때 두가지를 섞어서 사용할수 있도록 한다.

DI의 모든 형태를 지원하는 것만큼 Spring은 콜백이벤트의 범위와 필요할때 전통적인 룩업을 위한 API 제공한다. 어쨌든 우리는 일반적으로는 순수한 DI를 추천한다.

DI는 여러가지 중요한 이득을 가진다. 예를 들면

  • 컴포넌트는 런타임시 협력자(collaborators)를 룩업할 필요가 없다. 그들은 쓰고 관리하는데 좀더 간단하다. Spring의 IoC버전내에서 컴포넌트는 자바빈즈 setter메소드를 표시하거나 생성자 인자를 통해 다른 컴포넌트의 의존성을 표현한다. EJB와 같은 부분은 개발자가 코드를 써야만 하는 JNDI룩업일것이다.
  • 같은 이유로 애플리케이션 코드는 테스트하기가 좀더 쉽다. 자바빈즈 프라퍼티는 간단하고 핵심자바이며 테스트하기 쉽다. 객체를 생성하고 관련 프라퍼티를 셋팅하는 스스로 포함된 JUnit테스트 메소드를 쓴다.
  • 좋은 IoC구현은 강력한 타이핑을 유지한다. 만약 당신이 협력자(collaborators)를 룩업하기 위한 일반적인 factory을 사용할 필요가 있다면 당신은 기대하는 타입으로 결과를 변환할 것이다. 이것은 중요한 문제가 아니다. 하지만 이것은 세련되지 않았다. IoC와 함께 당신은 당신의 코드에 강력하게 쓰여진 의존성을 표시하고 프레임워크는 타입변환을 책임질수 있다. 이것은 프레임워크가 애플리케이션을 설정할때 에러처럼 나타날수 있는 타입 미스매치(mismatch)를 의미한다. 당신은 당신의 코드에서 클래스 형변환 예외에 대한 걱정을 할필요가 없다.
  • 의존성은 명시적이다. 예를 들면 만약 애플리케이션 클래스가 프라퍼티 파일을 로드하거나 데이터베이스에 접근한다면 환경적인 가정은 코드를 읽지 않고 명백하지는 않을것이다(테스트를 복잡하게 하고 배치 유연서을 감소시킨다.) DI접근법으로 의존성은 명시적이고 생성자나 자바빈즈 프라퍼티내에서 분명하다.
  • 대부분의 비지니스 객체는 IoC컨테이너 API에 의존하지 않는다. 이것은 기존 코드를 사용하기 쉽도록 만들고 IoC컨테이너 내부나 외부의 객체를 사용하기 쉽도록 만든다. 예를 들면 Spring사용자는 종종 Spring bean처럼 Jakarta Commons DBCP데이터소스를 설정한다. 여기엔 이것을 하기 위해서 사용자정의 코드를 쓸 필요가 없다. 우리는 IoC컨테이너가 침략적(이것을 사용해서 API에 의존적인 것을 당신의 코드에 넣는)이지 않다고 말한다. 대부분의 POJO는 Spring bean factory에서 컴포넌트가 될수 있다. 다중 인자를 가진 생성자를 가진 자바빈즈나 객체들은 특별히 잘 작동한다. 하지만 Spring은 정적인 factory메소드나 IoC컨테이너에 의해 관리되는 다른 객체들 위의 메소드로 부터 객체를 인스턴스화하기 위한 유일한 지원을 제공한다.

이 마지막 이익은 강조할만하다. DI는 컨테이너의 애플리케이션 코드의 의존성 최소화라는 점에서 EJB와 같은 전통적인 컨테이너 구조와는 다르다. 이것은 당신의 비지니스 객체가 다른 DI프레임워크내에서 또는 어떤 프레임워크 밖에서 어떠한 코드변경없이 잠재적으로 실행가능하다는것을 의미한다.

나의 경험과 Spring사용자의 경험에서 이것은 IoC그리고 특별히 DI가 애플리케이션 코드로 가져다 주는 이익을 지나치게 강조하는것은 힘들다.

DI는 J2EE커뮤니티에서 최초의 일이지만 새로운 개념이 아니다. 대안적인 DI컨테이너(PicoContainer 그리고 HiveMind)가 있다. PicoContainer는 가볍고 자바빈즈 프라퍼티보다 생성자를 통해 의존성의 표현을 강조한다. 이것은 자바코드 밖에서 Spring내에서 비교적 기능을 제한하는 메타데이터를 사용하지 않는다. HiveMind는 비록 Spring프로젝트의 포괄적인 범위나 사용자 커뮤니티의 부족에도 불구하고 Spring과 개념적으로 좀더 유사(심지어는 IoC를 사용하는것 또한)하다. EJB3.0은 기본적인 DI능력을 제공한다.

Spring BeanFactory는 매우 가볍다. 사용자는 단독으로 작동하는 스윙 애플리케이션만큼 애플릿내에서 그것들을 성공적으로 사용한다(그들은 EJB컨테이너내에서도 잘 작동한다.). 특별한 배치단계와 발견될만한 시작시간이 없다. 컨테이너를 애플리케이션의 어느 티어에서 인스턴스화 할수 있도록 구체화하는 능력은 매우 가치있다.

Spring BeanFactory개념은 Spring도처에서 사용된다. Spring이 내부적으로 일관적일수 있는 핵심적인 이유이다. Spring은 또한 전체 기능의 프레임워크 도처에 기본적인 개념으로 IoC를 사용하는 면에서 IoC컨테이너 사이에서는 유일하다.

애플리케이션 개발자를 위해 가장 중요한 것은 하나 이상의 BeanFactory가 잘 정의된 비지니스 객체의 레이어를 제공하는 것이다. 이것은 유사하지만 로컬 세션빈즈의 레이어보다는 훨씬 더 간단하다. EJB와는 다르게 레이어내의 객체는 서로 밀접한 관계일수 있고 그들의 관계는 자신의 factory에 의해서 관리된다. 비지니스 객체의 잘 정의된 레이어는 성공적인 구조를 위해서 매우 중요하다.

Spring ApplicationContext는 다음의 사항들을 지원하는 BeanFactory의 하위 인터페이스이다.

  • 메시지 룩업, 국제화 지원
  • 이벤트 기법, 이벤트를 통지하기 위해 표시되거나 등록하는 애플리케이션 객체를 허락한다.
  • 컨테이너 행위를 커스터마이징하는 특정 애플리케이션에 특정적이거나이나 일반적인 bean 정의의 자동 인식
  • 이식가능한 파일과 자원 접근

XmlBeanFactory 예제#

Spring사용자는 대개 XML "bean definition"파일내에 그들의 애플리케이션을 설정한다. Spring XML bean 정의 문서의 가장 상위는 <bean> 요소이다. <bean> 요소는 하나 이상의 <bean>정의를 포함한다. 우리는 대개 각각의 bean정의의 클래스와 프라퍼티를 명시한다. 우리는 우리의 코드내에 이 bean을 사용할 이름이 될 id를 명시해야만 한다.

J2EE애플리케이션내에서 공통적으로 보여지는 관계를 가진 3개의 애플리케이션 객체를 설정한 간단한 예제를 보자.

  • J2EE 데이터소스
  • 데이터소스를 사용하는 DAO
  • 이 작업의 과정내의 DAO를 사용하는 비지니스 객체

다음의 예제에서 우리는 Jakarta Commons DBCP프로젝트로 부터 BasicDataSource를 사용한다(C3PO프로젝트의 ComboPooledDataSource또한 굉장히 멋진 옵션이다.). 다른 많은 클래스들처럼 BasicDataSource는 Spring bean factory내에서 자바빈즈 스타일의 설정을 제공하는것처럼 쉽게 사용될수 있다. shutdown시 호출될 필요가 있는 close메소드는 어떤 Spring인터페이스를 구현하기 위한 BasicDataSource의 필요성을 피하기 위해 spring의 "destroy-method"속성을 통해 등록될수 있다.

역자주 : Spring XML설정파일에서 destroy-method의 값을 대체하는 방법은 DisposableBean인터페이스를 구현해서 사용하면 된다. Spring의 개념에 좀더 들어가면 나오겠지만 destroy-method에 반대되는 값은 init-method이고 이 값은 InitializingBean인터페이스를 구현해서 사용하면 된다.

<beans>
  <bean id="myDataSource"  class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/mydb" />
    <property name="username" value="someone" />
  </bean>

우리가 관심을 가지는 BasicDataSource의 모든 프라퍼티는 문자열이다. 그래서 우리는 value 속성과 함께 그 값을 명시한다(이것은 Spring 1.2에서 새로 추가된 사항이다. 이것은 기존에 <value>요소를 사용하던 방식에 대한 편리한 대안으로 사용된다.).

역자주 : 기존에 1.2버전 이전에는 다음의 소스코드처럼 value라는 하위 xml요소를 둬서 셋팅했다. 물론 이와같은 방법도 계속적으로 사용가능하다.

<beans>

  <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
    <property name="url"><value>jdbc:mysql://localhost:3306/mydb</value></property>
    <property name="username"><value>root</value></property>
  </bean>
Spring은 문자열표현을 필요한 다른 어떠한 타입으로 변환하기 위한 표준적인 자바빈즈 PropertyEditor기법을 사용한다.

지금 우리는 데이터소스에 대한 bean 참조를 가지는 DAO를 정의한다. bean들 사이의 관계는 "ref"속성이나 <ref>요소를 사용해서 명시한다.

  <bean id="exampleDataAccessObject" class="example.ExampleDataAccessObject">
    <property name="dataSource" ref="myDataSource" />
  </bean>

비지니스 객체는 DAO에 대한 참조와 int형 프라퍼티를 가진다. 이 경우에 나는 Spring 1.2의 이전버전에 친숙한 사용자를 위해 하위요소 문법을 사용했다.

<bean id="exampleBusinessObject" class="example.ExampleBusinessObject">
    <property name="dataAccessObject"><ref bean="exampleDataAccessObject"/></property>
    <property name="exampleParam"><value>10</value></property>
  </bean>

</beans>

객체간의 관계는 예제처럼 설정내에 명시적으로 셋팅한다. 우리는 대부분의 경우에 좋은 것이 되기 위해 이것을 고려한다. 어쨌든 Spring은 bean사이에 의존성을 발견하는 곳에 우리가 "autowire" 지원이라고 부르는것을 제공한다. PicoContainer과 같은 이 제한은 만약 특정 타입의 다중 bean들이 있다면 해석되어야만 하는 타입의 인스턴스 의존성을 해결하는것은 불가능하다. 긍정적인 면에서 만족하지 않는 의존성은 factory가 초기화될때 잡힐수 있다.(Spring은 명시적 설정을 위해 옵션적인 의존성 체크를 제공한다.)

우리는 위 예제에서 설명된 autowire기능을 사용할수 있다. 만약 우리가 명시적인 관계를 작성하길 원하지 않는다면.

<bean id="exampleBusinessObject" class="example.ExampleBusinessObject" autowire="byType">
    <property name="exampleParam" value="10" />
 </bean>

이 사용법으로 Spring은 exampleBusinessObject의 데이터소스 프라퍼티가 현재의 BeanFactory내에 발견되는 데이터소스의 구현이 되기 위해 셋팅되어야만 하는것이라는것을 해결할것이다. 만약에 현재의 BeanFactory내에 요구되는 타입의 bean이 없거나 하나보다 많다면 에러이다. 우리는 여전히 레퍼런스가 아닌것처럼 exampleParam프라퍼티를 셋팅할 필요가 있다.

Autowire지원은 설정양을 줄이는 장점을 가진다. 이것은 리플렉션(reflection)을 사용해서 애플리케이션 구조에 대한 것을 알수 있게 해준다는것을 의미한다. 그래서 만약 당신이 자바빈즈 프라퍼티의 추가적인 생성자 인자를 추가한다면 이것은 설정변경이 필요없이 성공적으로 활성화될것이다. autowiring의 교환은 주의깊게 평가될 필요가 있다.

자바코드로부터 관계를 구체화하는것은 그것을 하드코딩하는것보다 막대한 이익을 가진다. 이것은 자바코드내 어떤 라인의 변경도 없이 XML파일을 변경하는것으로 가능하다. 예를 들면 우리는 대안적인 커넥션풀이나 테스트 데이터소스를 사용하기 위해 다른 bean클래스를 참조하기 위한 myDataSource bean정의를 간단히 변경할수 있다. 우리는 대안적인 하나의 XML내에서 애플리케이션 서버로부터 데이터소스를 얻기 위해 Spring JNDI위치의 FactoryBean을 사용할수 있다. 자바코드나 다른 bean정의에는 어떠한 영향도 없을것이다.

<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/myDataSource" />
  </bean>

지금 비지니스 객체의 예제를 위한 자바코드를 보자. 밑의 코드리스트에서 Spring의존적인것들은 없다. EJB컨테이너와는 달리 Spring BeanFactory는 침략적이지 않다. 당신은 애플리케이션 객체로 코드를 인식하도록 할 필요는 없다.

public class ExampleBusinessObject implements MyBusinessObject {

  private ExampleDataAccessObject dao;
  private int exampleParam;

  public void setDataAccessObject(ExampleDataAccessObject dao) {
    this.dao = dao;
  }

  public void setExampleParam(int exampleParam) {
    this.exampleParam = exampleParam;
  }

  public void myBusinessMethod() {
    // do stuff using dao
  }
}

프라퍼티 setter는 bean정의 문서내에서 XML참조에 대응된다는 것에 주의하라. 객체가 사용되기 전에 Spring에 의해 호출된다.

그러한 애플리케이션 bean은 Spring에 의존할 필요가 없다. 그들은 어떠한 Spring인터페이스를 구현하거나 Spring클래스를 확장할 필요가 없다. 그들은 단지 자바빈즈 명명 형태를 관찰할 필요는 있다. Spring애플리케이션 컨텍스트 밖에서 재사용은 쉽다. 디폴트 생성자를 사용해서 그것을 인스턴스화하고 그것의 프라퍼티를 setDataSource() 와 setExampleParam() 호출을 통해 수동으로 셋팅한다. 당신이 인자없는 생성자를 가진만큼 당신은 코드내에 한줄로 프로그램적인 생성작업을 지원하길 원한다면 다중 프라퍼티를 가져오는 다른 생성자를 정의하는데 자유롭다.

자바빈즈 프라퍼티는 함께 작동할 비지니스 인터페이스 호출자위에 선언되지 않는다. 그들은 상세하게 구현한다. 우리는 연결된 객체나 호출 코드에 영향없이 다른 bean프라퍼티를 가지는 다른 구현클래스와 쉽게 "플러그인" 할수 있다.

물론 Spring XML bean factory는 여기서 서술된 것보다 더 많은 능력을 가지고 있다. 하지만 이것은 당신에게 기본적인 접근법에 대한 것만 제시할것이다. 간단한 프라퍼티들과 당신이 자바빈즈 PropertyEditor를 가지기 위한 프라퍼티만큼 Spring은 자동적으로 리스트, 맵 그리고 java.util.Properties를 다룰것이다. 다른 향상된 컨테이너 능력은 다음을 포함한다.

  • (내부빈)Inner bean, 가장 상위 레벨의 범위에서 보여지지 않는 익명 bean정의를 포함하는 프라퍼티 요소내
  • 후 프로세서(Post processors) : 컨테이너 행위를 커스터마이징 하는 특별한 bean정의
  • 메소드 삽입(Method Injection), 컨테이너내 IoC의 형태는 추상메소드나 의존성을 삽입하기 위한 견고한 메소드를 오버라이딩 하는 것을 구현한다. 이것은 Setter Injection이나 Constructor Injection보다는 잘 사용되지 않는 DI의 형태이다. 어쨌든 이것은 각각의 호출을 위해 새로운 객체 인스턴스를 찾을때나 시간을 넘어 다양한 설정을 허락하고자 할때 명시적인 컨테이너 의존성을 피하기 위해 유용하다.

bean factory와 애플리케이션 컨텍스트는 언제나 J2EE서버나 웹컨테이너에 의해 정의된 범위내에 속한다.

  • 서블릿 컨텍스트. Spring MVC프레임워크내에서 애플리케이션 컨텍스트는 공통객체를 포함하는 각각의 웹애플리케이션을 위해 정의된다. Spring은 Spring MVC프레임워크에 의존하는 것 없이 리스너나 서블릿을 통해 그러한 컨텍스트를 인스턴스화하는 기능을 제공한다. 그래서 이것은 Struts, WebWork 또는 다른 웹프레임워크내에서도 사용이 될수 있다.
  • 서블릿 : Spring MVC프레임워크내의 각각의 컨트롤러 서블릿은 가장 상위 애플리케이션에서 파생된 자신만의 애플리케이션 컨텍스트를 가진다. 이것은 또한 Struts나 다른 MVC프레임워크와 함께 완성하기가 쉽다.
  • EJB : Spring은 EJB jar파일내의 XML문서로 부터 로드된 BeanFactory를 제공하고 EJB제작을 간단하게 하는 EJB를 위한 편리한 슈퍼클래스를 제공한다.

J2EE스펙에 의해 제공되는 사항은 일반적으로 bean factory를 시작하기 위해 싱글톤을 사용할 필요를 제거한다.

어쨌든 이것은 우리가 바란다면 BeanFactory를 프로그램으로 인스턴스화하는것은 자명하다. 예를 들면 우리는 다음 2줄의 코드로 위에서 정의된 bean factory를 생성할수 있고 비지니스 객체에 대한 참조를 얻을수 있다.

XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("myFile.xml", getClass()));
MyBusinessObject mbo = (MyBusinessObjectbf.getBean("exampleBusinessObject");

역자 주 : 기존엔 다음과 같은 방식을 사용했다. 사실 큰 차이가 있는건 아니지만 다양한 방법으로 이제 설정이 가능하다는 예이다.

InputStream is = getClass().getResourceAsStream("myFile.xml");
XmlBeanFactory bf = new XmlBeanFactory(is);
MyBusinessObject mbo = (MyBusinessObjectbf.getBean("exampleBusinessObject");

이 코드는 애플리케이션 서버 밖에서 작동할것이다. 이것은 Spring IoC컨테이너가 순수 자바인것처럼 J2EE에 의존하지 않는다. Spring Rich프로젝트(Spring을 사용해서 스윙 애플리케이션의 개발을 단순화하기 위한 프레임워크)는 Spring의 통합테스팅 기능을 사용해서 J2EE환경밖에서 사용될수 있는 방법을 그린다. 이 기능은 이 글 나중에 얘기된다. DI와 관련된 기능은 J2EE또는 서버측 환경에 너무 일반적이고 가치있다.

JDBC추상화 그리고 데이터접근 예외 구조#

데이터접근은 Spring을 부각시키는데 다른 영역이다.

JDBC는 데이터베이스 참조로 부터 상당히 좋은 추상화를 제공한다. 하지만 사용하기 위해서 지겨운 API이다. 몇몇 문제는 다음과 같다.

  • ResultSets, Statements 그리고 (가장 중요한)Connections 보장하기 위해 장황한 에러 핸들링을 위한 필요가 사용 후에 닫힌다. 이것은 JDBC의 정확한 사용이 많은 코드내에서 빨리 결과를 생성한다. 이것은 또한 에러들의 공통적인 원인이다. Connection누설은 애플리케이션을 빨리 다운시켜버릴수 있다.
  • 비교적 정보의 가치가 없는 SQLException. JDBC는 예외구조를 제공하지 않지만 모든 에러에 대해 응답으로 SQLException을 던진다. 정말로 나쁘게 되는것을 찾는건 예를 들면 SQLState와 에러코드를 포함하는 데드락이나 적합하지 않은 SQL의 문제인가.? 그러한 값들의 의미는 데이터베이스마다 다양하다.

Spring은 두가지 방법으로 이러한 문제를 담당한다.

  • API를 제공함으로써 지루하고 에러를 발생시키는 경향이 있는 예외 핸들링을 애플리케이션 코드에서 프레임워크로 이동시킨다. 프레임워크는 모든 예외 핸들링을 처리한다. 애플리케이션 코드는 SQL문과 결과를 추출하는데 집중을 할수 있다.
  • 당신의 애플리케이션 코드를 위한 의미있는 예외 구조를 제공함으로써 SQLException을 대신하여 작동한다. Spring이 데이터베이스로부터 Connection을 처음으로 얻었을 때 이것은 데이터베이스를 확인하기 위해 메터데이터를 시험한다. 이것은 org.springframework.dao.DataAccessException으로 부터 유래된 자신만의 구조로 SQLException을 올바른 예외로 맵핑시키기 위해 이러한 정보를 사용한다. 게다가 당신의 코드는 의미있는 예외와 함께 작동을 할수 있고 선호하는 SQLState나 에러코드에 대해서 걱정할 필요가 없다. Spring의 데이터접근 예외는 JDBC에 특성화되어있지 않다. 그래서 당신의 DAO들은 그들이 던질 예외들 때문에 JDBC에 묶일 필요가 없다.

다음의 UML클래스 다이어그램은 이 데이터접근 예외 구조의 일부를 설명하고 복잡함을 표시한다. 여기에 보여진 예외중 어느것도 JDBC에 특성화된것이 없다. 이러한 예외의 몇몇 JDBC기반 하위 클래스도 있지만 호출하는 코드는 JDBC의 의존성으로 부터 떨어져 대개 추상화된다. 만약 당신이 당신의 퍼시스턴스 전략을 숨기기 위해 진실로 API에 관용적인 DAO인터페이스를 사용하고자 한다면 필수이다.

DataAccessException.gif

Spring은 두가지 레벨의 JDBC API를 제공한다. 첫번째는 org.springframework.jdbc.core패키지에 있고 애플리케이션 코드에서 부터 프레임워크내부로 컨트롤을 이동시키기 위해 콜백을 사용하고 에러핸들링과 connection취득 그리고 릴리즈를 한다. 이것은 Inversion of Control의 다른 타입이지만 설정 관리를 위해 사용될수 있는 동등한 가치를 가진다.

Spring은 JDO(PersistenceManager를 획득하고 버리는), JTA를 이용한 트랜잭션 관리 그리고 JNDI처럼 자원을 획득하고 없애는 특별한 단계를 포함한 다양한 API를 할당하기 위해 유사한 콜백 접근법을 사용한다. 콜백과 같은 것을 수행하는 Spring 클래스는 템플릿들을 호출한다.

예를 들면, Spring JdbcTemplate객체는 SQL쿼리를 수행하고 다음처럼 리스트로 결과를 저장하기 위해 사용될수 있다.

JdbcTemplate template = new JdbcTemplate(dataSource);
List names = template.query("SELECT USER.NAME FROM USER",
  new RowMapper() {
    public Object mapRow(ResultSet rs, int rowNumthrows SQLException;
      return rs.getString(1);
    }
  });

mapRow콜백 메소드는 ResultSet의 각각의 row를 위해 호출될것이다.

역자 주 : 기존엔 mapRow메소드 대신에 processRow메소드를 사용했다. 소스의 형태는 다음과 같다.

JdbcTemplate template = new JdbcTemplate(dataSource);
final List names = new LinkedList();
template.query("SELECT USER.NAME FROM USER",
  new RowCallbackHandler() {
    public void processRow(ResultSet rsthrows SQLException {
      names.add(rs.getString(1));
    }
  });

콜백내 애플리케이션 코드는 SQLException을 던지는데 자유롭다. Spring은 어떤 예외를 잡을것이고 자신의 구조내에서 그것들을 다시 던진다. 애플리케이션 개발자는 예외를 선택할수 있다.

JdbcTemplate은 prepared statement와 배치형태의 업데이트를 포함한 다른 시나리오를 지원하기 위해 많은 메소드를 제공한다. SQL함수들을 수행하는 것과 같은 간단한 작업은 다음처럼 콜백없이도 달성될수 있다. 예제는 바인드변수의 사용을 설명한다.

int youngUserCount = template.queryForInt("SELECT COUNT(0) FROM USER WHERE USER.AGE < ?",
  new Object[] { new Integer(25) });

Spring JDBC추상화는 굉장히 큰 결과들을 포함하는 애플리케이션의 경우에도 표준 JDBC보다 낮은 성능상의 오버헤드를 가진다.

좀더 높은 레벨의 JDBC추상화는 org.springframework.jdbc.object패키지내에 있다. 이것은 핵심 JDBC콜백 기능에 내장되지만 자바 객체처럼 모델화된 쿼리, update또는 저장 프로시저등의 RDBMS작동내 API를 제공한다. 이 API는 내가 직관적이고 크게 유용하게 본 JDO쿼리 API에 의해 부분적으로 영감을 받았다.

사용자 객체를 반환하는 쿼리 객체는 이것처럼 보일것이다.

class UserQuery extends MappingSqlQuery {

  public UserQuery(DataSource datasource) {
    super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?");
    declareParameter(new SqlParameter(Types.NUMERIC));
    compile();
  }

  // Map a result set row to a Java object
  protected Object mapRow(ResultSet rs, int rownumthrows SQLException {
    User user = new User();
    user.setId(rs.getLong("USER_ID"));
    user.setForename(rs.getString("FORENAME"));
    return user;
  }

  public User findUser(long id) {
    // Use superclass convenience method to provide strong typing
    return (UserfindObject(id);
  }
}

이 클래스는 다음처럼 사용될수 있다.

User user = userQuery.findUser(25);

그러한 객체는 종종 DAO내부의 내부(inner)클래스이다. 그것들은 하위클래스가 일반적이지 않은 어떤것이 아니라면 threadsafe한 상태이다.

org.springframework.jdbc.object패키지내 다른 중요한 클래스는 StoredProcedure클래스이다. Spring은 하나의 비지니스 메소드와 함께 자바 클래스에 의해 프록시된 저장된 프로시저를 가능하게 한다. 만약 당신이 좋아한다면 저장 프로시저의 사용에 의존적인 것으로 부터 당신의 애플리케이션 코드에 자유로울수 있도록 하는것을 의미하도록 저장 프로시저를 구현하는 인터페이스를 정의할수 있다.

Spring 데이터접근 예외구조는 체크되지(런타임) 않은 예외에 기반하고 있다. 다른 프로젝트에서 Spring을 이용한 작업이 나는 올바른 선택이라고 확신을 한다.

데이터접근 예외는 언제나 복구가능한것은 아니다. 예를 들어 만약 우리가 데이터베이스에 연결을 할 수 없다면 특정 비지니스 객체는 문제점들 가운데 작업을 가능하게 할것 같지 않다. 잠재적인 예외는 낙관적인 락위반이지만 모든 애플리케이션이 낙관적인 락을 사용하는것은 아니다. 현명하게 다룰수 없는 치명적인 예외를 잡기 위한 코드를 작성하는데 집중하는것은 언제나 나쁘다. 서블릿또는 EJB와 같은 가장 높은 수준의 핸들러에 그것들을 전하는것은 언제나 좀더 선호된다. 모든 Spring데이터접근 예외는 DataAccessException의 하위클래스이다. 그래서 우리가 모든 Spring 데이터접근 예외들을 잡도록 선택한다면 우리는 다음처럼 쉽게 할수 있다.

만약에 우리가 체크되지 않은 데이터접근 예외로부터 복구하기를 원한다면 우리는 여전히 그렇게 할수 있다. 우리는 단지 복구가능한 상황만을 다루기 위해서 코드를 작성할수도 있다. 예를 들면 우리가 낙관적인 락위반만이 복구가능하다고 생각할때 우리는 다음처럼 Spring DAO내 코드를 작성할수 있다.

try {
  // do work
}
catch (OptimisticLockingFailureException ex) {
  // I'm interested in this
}

Spring 데이터접근 예외가 체크된다면 우리는 다음의 코드를 작성할 필요가 있을것이다. 우리는 어떻든 이것을 작성하는것을 선택할수 있다는 것을 알라.

try {
  // do work
}
catch (OptimisticLockingFailureException ex) {
  // I'm interested in this
}
catch (DataAccessException ex) {
  // Fatal; just rethrow it
}

첫번째 예제를 위한 하나의 잠재적인 객체화는 컴파일러가 두번째또한 적용한 잠재적인 복구가능한 예외를 강제적으로 다룰수 없다. 우리가 기본적인 예외(DataAccessException)를 잡기위해서 강요하기 때문에 컴파일러는 하위클래스(OptimisticLockingFailureException)를 위해 체크를 강요할수 없을것이다. 그래서 컴파일러는 복구가능하지 않은 문제를 다루기 위한 코드를 작성하도록 우리에게 강요할것이지만 복구가능한 문제를 다루기 위해 우리에게 강요하는것은 도움을 주지 못한다.

Spring의 체크되지 않은 데이터접근 예외 사용은 많은면에서 일관적이다. JDBC는 체크된 예외를 사용하는 몇몇 데이터접근 API중 하나이다. 예를 들면 TopLink와 JDO가 체크되지 않은 예외를 사용한다. Hibernate는 3.x버전에서 체크된 예외를 체크되지 않는 예외로 교체한다.

Spring JDBC는 여러가지 방법으로 당신을 도울수 있다.

  • 당신은 JDBC를 사용하기 위해 finally블럭을 다시 사용할 필요가 결코 없다.
  • Connection누설은 지난일이 될것이다.
  • 당신은 전체적으로 좀더 적은 양의 코드를 작성할 필요가 있을것이고 그 코드는 필요한 SQL에 집중을 하게 될것이다.
  • 당신은 분명하지 않은 에러코드를 가지고 작업을 수행하기 위해 당신의 RDBMS문서를 통해 조사할 필요가 없을것이다. 당신의 애플리케이션은 RDBMS정의 에러 핸들링 코드의 의존적일 필요가 없을것이다.
  • 어떠한 persistence기술을 사용하든지 당신은 특정 데이터접근 API에 의존하는 비지니스 로직없이 DAO패턴을 구현하는것이 쉽다는 것을 알게 될것이다.
  • 당신은 BLOB핸들링과 결과를 반환하는 저장프로시저를 호출하는것과 같은 향상된 점에서 향상된 이식가능성으로 부터 이득을 얻을수 있을것이다.

여기서 우리는 실질적인 생산성 획득과 적은 수의 버그를 보게 될것이다. 나는 JDBC코드를 쓰는것을 몹시 싫어한다. 지금 나는 부수적인 JDBC자원 관리보다 내가 수행하고자 하는 SQL문에 집중할수 있다는 것을 안다.

Spring의 JDBC추상화는 당신이 Spring의 다른 부분을 사용하길 강요하지 않는다면 당신이 바랄때 독자적으로 사용될수도 있다.

O/R 맵핑 통합#

물론 종종 당신은 관계적인 데이터접근보다 O/R맵핑을 사용하길 원할것이다. 당신의 전체 애플리케이션 프레임워크는 이것또한 지원해야만 한다. 게다가 Spring은 Hibernate(2.x와 3.x버전)와 JDO(1.x와 2.x버전), TopLink와 다른 ORM제품들을 통합한다. 이것의 데이터접근 구조는 어떠한 데이터접근 기술과도 통합이 되도록 한다. Spring과 Hibernate는 특별히 인기 있는 조합이다.

당신은 왜 직접적으로 ORM만 사용하지 않고 ORM제품에 Spring을 같이 사용하는가.? Spring은 다음과 같은 중요한 가치를 추가한다.

  • 세션 관리. Spring은 효과적이고 쉬우며 Hibernate또는 TopLink세션과 같은 작업의 안전한 핸들링을 제공한다. ORM툴을 사용한 관련 코드는 대개 효과적이고 적절한 트랜잭션 핸들링을 위해 같은 "Session"객체를 사용할 필요가 있다. Spring은 선언적인 방법이나 AOP메소드 인터셉터 접근법 또는 자바코드 레벨에서 "template"래퍼 클래스의 명시적인 방법을 사용해서 투명한 생성과 현재 쓰레드에 세션을 바인딩할수 있다. 게다가 Spring은 ORM기술의 많은 사용자에게 영향을 받은 이슈들에 대한 문제를 해결했다.
  • 자원 관리. Spring애플리케이션 컨텍스트는 경로와 Hibernate SessionFactories, JDBC 데이터소스 그리고 다른 관련 자원의 설정을 다룰수 있다. 이것은 관리와 변경을 쉽도록 만들어준다.
  • 통합된 트랜잭션 관리. Spring은 선언적인 방법이나 AOP메소드 인터셉터 또는 자바코드 레벨에서 "template"래퍼 클래스의 명시적인 방법을 사용해서 당신의 ORM코드를 랩핑하도록 한다. 이런 경우에 트랜잭션 의미는 당신을 위해서 다루어지고 예외의 경우에 적당한 트랜잭션 핸들링이 처리된다. 밑에서 논의되는 것처럼 당신의 ORM관련코드에 영향을 미치는 것 없이 다양한 트랜잭션 관리자를 사용하고 바꿀수 있는 이익을 가진다. 추가된 이익처럼 JDBC관련 코드는 ORM코드와 함께 트랜잭션적으로 완전히 통합될수 있다. 이것은 ORM내에서 구현하지 않더라도 기능적으로 매우 유용하다.
  • 위에서 서술된 예외 랩핑. Spring은 ORM레이어로부터 예외들을 포장할수 있고 선호하는 예외를 추상화된 런타임 예외들로 변환을 할수 있다. 이것은 복구할수 없고 지겨운 반복적 catches/throws코드와 예외 선언 없이 선호하는 레이어에서만 대부분의 퍼시스턴스 예외를 다룰수 있도록 한다. 당신은 여전히 필요한 어느곳이든지 예외들을 잡고 다룰수 있다. JDBC예외들은 같은 구조로 변환하고 일관적인 프로그램 모델내에서 JDBC와 함께 같은 작업을 수행할수 있다는 것을 의미한다.
  • 업체 락(lock-in)을 피하기. ORM솔루션은 다른 특징에 다른 성능을 가진다. 그리고 모든 솔루션에 완벽하게 적합한 하나의 값은 없다. 대안으로 당신은 당신의 ORM툴을 사용해서 구현하기 위해 적합하지 않은 어떠한 기능을 발견하게 될것이다. 게다가 이것은 당신의 데이터접근 객체 인터페이스의 툴 기반의 구현으로 부터 당신의 구조를 분리한다. 만약 당신이 기능, 성능 또는 다른 어떠한 사항의 이유로 다른 구현물로 교체를 해야 할 필요가 있다면 Spring은 다른것과 쉽게 교체하도록 해준다.Spring의 ORM툴의 트랜잭션과 예외의 추상화는 IoC접근법으로 당신에게 매퍼/DAO객체가 데이터접근 기능을 구현하는것에서 쉽게 교체를 하게 해준다. 이것은 ORM툴의 어떠한 능력의 감소없이 당신의 애플리케이션의 어떤 범위에서 모든 ORM기반 코드를 격리하기 쉽게 해준다. PetClinic샘플 애플리케이션은 JDBC, Hibernate, TopLink 그리고 Apache OJB를 통해 Spring이 제공하는 이식가능성 이득을 설명한다.
  • 테스팅의 쉬움. Spring의 Inversion of Control접근법은 Hibernate세션 factory, 데이터소스, 트랜잭션 관리자 그리고 매퍼 객체 구현물 같은 자원의 구현과 위치를 교체하는것을 쉽게 만든다. 이것은 퍼시스턴스 관련 코드의 각각의 일부분을 분리하거나 테스트를 좀더 쉽게 만든다.

Spring은 데이터접근을 위한 혼합-대응(mix-and-match)접근법을 돕는다. 몇몇 ORM업체의 목적에도 불구하고 ORM이 많은 경우에 가치있는 제품이라고 하더라도 모든 문제를 위한 솔루션이 아니다. Spring은 JTA사용없이 퍼시스턴스 접근법과 혼합하고 대응하더라도 일관적인 구조, 트랜잭션 전략을 가능하게 한다.

ORM이 이론적으로 적합하지 않는 경우에 Spring의 단순화된 JDBC만이 선택할수 있는 것이 아니다. iBATIS SQL Maps에 의해 제공되는 "mapped statement"접근법도 가치가 있어보인다. 이것은 쿼리 결과로부터 맵핑된 객체의 생성을 자동으로 하는 동안 SQL에서 높은 레벨의 제어를 제공한다. Spring은 SQLMaps와 통합된다. Spring의 PetStore샘플 애플리케이션은 iBATIS통합을 설명한다.

트랜잭션 관리#

데이터접근 API를 추상화는것은 충분하지 않다. 우리는 트랜잭션 관리에 대해서 생각해 볼 필요가 있다. JTA는 명백한 해결법이지만 이것은 직접적으로 사용하기에는 성가신 API이다. 그리고 EJB CMT가 트랜잭션 관리를 위한 단지 이론적인 옵션이라고 많은 J2EE개발자들이 느끼고 있다. Spring은 이것을 바꿨다.

Spring은 트랜잭션 관리를 위해 자신만의 추상화를 제공한다. Spring은 이전을 위해 다음의 사항들을 사용한다.

  • 콜백 템플릿을 통한 프로그램 트랜잭션 관리는 직접적으로 JTA를 사용하는 것보다 좀더 쉬운 JdbcTemplate과 유사하다.
  • 선언적인 트랜잭션 관리는 EJB CMT와 유사하지만 EJB컨테이너는 필요없다. 실제로 우리는 Spring의 선언적인 트랜잭션 관리능력이 EJB CMT의 몇몇 유일하고 중요한 이득과 함께 호환가능한 수퍼셋(superset)이다.

Spring의 트랜잭션 추상화는 JTA나 다른 어떠한 트랜잭션 관리 기술에 묶이지 않는 유일한 것이다. Spring은 트랜잭션 내부구조를 참조하여 애플리케이션 코드를 분리하는 트랜잭션 전략의 개념을 사용한다.

왜 당신은 이것에 대해서 주의해야 하는가.? 모든 트랜잭션 관리를 위해 JTA가 가장 좋은 답이 아닌가.? 만약 당신이 하나의 데이터베이스를 사용하는 애플리케이션을 작성한다면 당신은 JTA의 복잡함을 사용할 필요가 없다. 당신은 XA트랜잭션이나 두 단계의 커밋에 흥미롭지는 않을것이다. 당신은 이러한 것들을 제공하는 높은 성능의 애플리케이션 서버 조차도 필요하지 않을것이다. 하지만 반면에 당신은 다중 데이터소스로 작업을 수행해야 할때 당신의 코드를 다시 쓰고 싶지는 않을것이다.

JDBC나 Hibernate트랜잭션을 직접적으로 사용함으로써 JTA의 오버헤드를 피할 결심을 했다고 생각해보라. 만약 당신이 다중 데이터소스와 작업할 필요가 있다면 당신은 트랜잭션 관리 코드를 쪼개야만 하고 그것을 JTA트랜잭션으로 교체해야만 한다. 이것은 그다지 매력적이지 않고 대부분의 J2EE에 전역적인 JTA트랜잭션을 사용하길 추천한다. Spring트랜잭션 추상화를 사용하는 것은 어쨌든 당신은 JDBC나 Hibernate보다 JTA를 사용하기 위해 Spring을 다시 설정해야만 한다. 이것은 코드상의 변경이 아닌 설정상의 변경이다. 게다가 Spring은 당신에게 애플리케이션 규모를 크게 하는것만큼 작게 하는 것도 가능하게 한다.

AOP

2003년 이후 엔터프라이즈 관련 사항에 EJB에 의해 할당된 트랜잭션 관리와 같은 AOP솔루션을 적용하는 것이 굉장히 큰 관심사항이 되었다.

Spring의 AOP지원의 첫번째 목표는 POJO를 위해 J2EE서비스를 제공하는 것이다. 어쨌든 Spring AOP는 벤더 락(lock in)의 위험 없이 애플리케이션 서버사이에 이식가능한 장점을 가진다. 이것은 웹이나 EJB컨테이너와 잘 작동한다. 그리고 WebLogic, Tomcat, JBoss, Resin, Jetty, Orion 그리고 많은 다른 애플리케이션 서버와 웹 컨테이너에서 성공적으로 사용되었다.

Spring AOP는 메소드 인터셉션(interception)을 지원한다. 제공되는 AOP의 핵심적인 개념은 다음과 같다.

  • 인터셉션(Interception) : 사용자 정의 행위는 어떠한 인터페이스나 클래스에 대해서 메소드 호출전이나 후에 추가된다. 이것은 AspectJ 용어에서 "around advice"와 유사하다.
  • 소개(Introduction) : advice는 추가적인 인터페이스를 구현하기 위한 객체를 야기한다는 것을 명시한다. 이것은 복잡한 상속상태에 이르게 될수 있다.

역자주 : AOP개념을 모두 설명할수는 없지만 기본적으로 AOP에 의해 해당 메소드가 호출되는 부분을 joinpoints라고 하고 joinpoints에서 호출되는 코드들을 advice라고 부른다. joinpoints들의 묶음을 pointcuts라고 부른다. 그리고 advice와 pointcuts를 모아 Aspects라고 지칭한다.

  • 정적이고 동적인 pointcuts : 인터셉터가 위치해야만 하는 곳에 프로그램 수행내 point를 정의하라. 정적 pointcuts는 메소드 시그너처를 의미하고 동적 pointcuts는 그들이 값을 구하는 지점에 메소드 인자로 간주될수 있다. pointcuts는 다른 애플리케이션과 코드 컨텍스트내 적용될 표준적인 인터셉터를 가능하게 하는 인터셉터로 부터 개별로 정의된다.

Spring은 상태유지(adviced객체당 하나의 인스턴스)와 비상태유지(모든 advice를 위한 하나의 인스턴스) 인터셉터 모두 지원한다.

Spring은 필드 인터셉터를 지원하지 않는다. 이것은 신중한 디자인 결정이다. 나는 언제나 필드 인터셉터가 encapsulation을 어긴다고 느낀다. 나는 OOP와 충돌이 나는것보다 보완물처럼 AOP를 생각하는것을 선호한다. 나는 만약 5년이나 10년안에 우리가 AOP가 애플리케이션 디자인의 가장 상위부분을 차지한다 하더라도 놀라지 않을것이다.

Spring은 런타임시 동적 프록시(인터페이스가 존재하는 곳에)나 CGLIB바이트 코드 생성자(클래스의 프록시를 가능하게 하는)를 사용해서 AOP를 구현한다. 둘다 애플리케이션 서버나 단독환경에서 잘 작동한다.

Spring은 AOP친화 인터페이스(http://www.sourceforge.net/projects/aopalliance)를 구현한 첫번째 AOP프레임워크이다. AOP프레임워크 사이의 인터셉터의 상호작용을 허락하는 인터페이스를 정의하기 위해 표현된다.

TheServerSide에서 진행중이고 논쟁된 이러한 종류의 가로채기(interception)은 "true AOP"이다. 나는 이것이 이렇게 불리는 것에 대해 특별히 걱정하지 않는다. 단지 실제로 유용할수 있다. 나는 이것을 declarative middleware" 라고 부르고 싶다. Spring AOP의 생각은 간단하고 local비상태유지 세션빈에 가벼운 대안이다. 획일적인 EJB컨테이너의 필요가 없어지고 당신이 필요한 서비스와 함께 당신 자신만의 컨테이너를 빌드하도록 한다. 우리는 어떠한 충고(advising)와 모든 POJO를 추천하지 않는다. 그래서 추천되는 것을 분명하게 하기 위해 local SLSBs로 유추한다. (어쨌든 EJB와는 달리 당신은 선호하는 지점에 드문 시나리오내 훌륭하게 얻어진 객체를 위해 Sping AOP을 적용하는것이 자유로울 것이다.)

Spring은 클래스로더보다 인스턴스에서 객체를 통지하기 때문에 이것은 다른 advice로 같은 클래스의 다중 인스턴스를 사용하거나 통지된 인스턴스와 함께 통지되지 않은 인스턴스를 사용하는 것이 가능하다.

아마도 Spring AOP의 가장 공통적인 사용은 선언적인 트랜잭션 관리이다. 이것은 위에서 서술된 TransactionTemplate추상화위에서 빌드되고 어떠한 POJO위에서 선언적인 트랜잭션 관리를 전달할수 있다. 트랜잭션 전략에 의존적으로 기법을 이해하는것은 JTA, JDBC, Hibernate 또는 다른 어떠한 트랜잭션 관리를 제공하는 API가 될수 있다.

Spring은 Spring애플리케이션으로 AspectJ의 aspect를 포함하는 것처럼 하기 위한 기능을 제공하는 AspectJ와 통합한다. Spring 1.1이후로 Spring IoC컨테이너를 사용해서 AspectJ 의 aspect의 의존성 삽입이 가능해졌다. 게다가 AspectJ의 aspect는 Spring관리 객체에 의존될수 있다. 곧 나오는 AspectJ 5와의 통합은 annotation기반 pointcut에 기반으로 하여 Spring을 사용하는 어떠한 POJO에 의존성을 삽입하는 능력을 제공하기 위한 AspectJ로 좀더 놀랍다.

Spring은 클래스로더보다 인스턴스에서 객체를 통지하기 때문에 이것은 다른 advice로 같은 클래스의 다중 인스턴스를 사용하거나 통지된 인스턴스와 함께 통지되지 않은 인스턴스를 사용하는 것이 가능하다.

아마도 Spring AOP의 가장 공통적인 사용은 선언적인 트랜잭션 관리이다. 이것은 위에서 서술된 트랜잭션 추상화를 빌드하고 어떠한 POJO위에서 선언적인 트랜잭션 관리를 전달할수 있다. 트랜잭션 전략에 의존적으로 기법을 이해하는것은 JTA, JDBC, Hibernate 또는 다른 어떠한 트랜잭션 관리를 제공하는 API가 될수 있다.

Spring의 선언적 트랜잭션 관리는 EJB CMT와 다음과 같은 차이점이 있다.

  • 트랜잭션 관리는 어떠한 POJO에 적용될수 있다. 우리는 비지니스 객체가 인터페이스를 구현하는것을 추천하지만 이것은 프로그래밍 실제상황의 문제이고 프레임워크에 의해 강요되지 않는다.
  • 프로그램적인 롤백은 Spring트랜잭션 API을 사용해서 전통적인 POJO내 달성될수 있다. 우리는 이것을 위해 ThreadLocal변수들을 사용해서 정적 메소드를 제공한다. 그리고 당신은 롤백을 확실히 하기 위해 EJBContext와 같은 컨텍스트 객체를 상속할 필요가 없다.
  • 당신은 선언적으로 "롤백 규칙들"을 정의할수 있다. EJB가 잡히지 않은 애플리케이션 예외(체크되지 않은 예외와 다른 Throwables에 대해서만)에서 트랜잭션을 자동적으로 롤백하는것에 반하여 애플리케이션 개발자는 어떠한 특정 예외에서 트랜잭션을 롤백하기를 원한다. Spring트랜잭션 관리는 당신에게 예외와 하위 클래스가 자동 롤백을 야기하는 것을 선언적으로 정의하도록 허락한다. 디폴트 행위는 EJB와 같지만 당신은 체크되지 않은 예외처럼 체크된 예외에서도 자동적으로 롤백을 수행할수 있다. 이것은 Spring트랜잭션 API에서 의존성(EJB프로그램적인 롤백은 EJBContext에서 수행되는 것처럼)을 생성하는 프로그램적인 롤백을 위한 필요성을 최소화하는 중요한 이익을 가진다.
  • 만약 그들이 트랜잭션 기반구조를 참조함으로써 제공된다면 Spring트랜잭션 추상화 지원 savepoints을 참조하기 때문에 Spring의 선언적인 트랜잭션 관리는 EJB CMT에 의해 명시된 전달 모델에 추가적으로 내포된 트랜잭션을 지원할수 있다. 예를 들면 만약 당신이 오라클에서 JDBC작업을 수행한다면 당신은 Spring을 사용해서 선언적인 내포된 트랜잭션을 사용할수 있을것이다.
  • 트랜잭션 관리는 JTA에 묶여있지 않다. 위에서 설명된 것처럼 Spring트랜잭션 관리는 다른 트랜잭션 전략과도 작동할수 있다.

물론 이것은 Spring AOP의 사용으로 애플리케이션 정의 aspect를 구현하는것이 가능하다. Spring의 기능보다 AOP개념으로 당신의 안정적인 레벨에 의존적으로 이것을 하는것을 선택할수 있다. 하지만 이것은 매우 유용하다. 성공적인 예제는 우리가 볼때 다음을 포함한다.

  • 사용자 정의 보안 인터셉션, 요구되는 보안 체크의 복잡함은 표준적인 J2EE보안 구조의 능력을 넘어선다.
  • 개발 기간동안 사용을 위해 aspect를 디버깅하고 프로파일링하기
  • 한 지점에서 일관적인 예외 핸들링 정책을 적용하는 Aspects
  • 유별난 시나리오에서 관리자나 사용자들에게 경고하기 위해 이메일을 보내는 인터셉션

애플리케이션정의 aspect는 많은 메소드를 통해 반복적인 코드를 위한 필요성을 제거하는 강력한 방법이 될수 있다.

Spring AOP는 Spring BeanFactory개념과 함께 투명하게 통합된다. Spring BeanFactory에서 나온 객체를 얻는 코드는 이것이 통지되든지 되지 않든지 알 필요가 없다. 어떤 객체처럼 계약은 인터페이스 객체 구현을 함으로써 정의될 것이다.

다음의 XML은 AOP프록시를 정의하는 방법을 그리고 있다.

<bean id="myTest"
  class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="proxyInterfaces">
    <value>org.springframework.beans.ITestBean</value>
  </property>
  <property name="interceptorNames">
    <list>
      <value>txInterceptor</value>
      <value>target</value>
    </list>
  </property>
</bean>

참조내 사용되거나 BeanFactory의 getBean()메소드에 의해 반환되는것처럼 bean의 타입이 proxy인터페이스에 의존할지라도 bean정의의 클래스는 항상 AOP프레임워크의 ProxyFactoryBean이라는것에 주의하라. ProxyFactoryBean의 "interceptorNames"프라퍼티가 문자열의 리스트를 가져온다.(bean이름이 bean참조보다 더 사용되어야만 한다. 만약 프록시가 "프로토타입"이라면 싱글톤 bean정의보다 상태유지 인터셉터의 새로운 인스턴스가 생성될 필요가 있다.). 위 리스트내 이름은 인터셉터나 pointcuts가 될수 있다. 리스트내 "target"값은 자동적으로 "호출 인터셉터"를 생성한다. 이것은 프록시 인터페이스를 구현하는 factory내 bean의 이름이다. 이 예제내 myTest빈은 bean factory내에서 다른 bean처럼 사용될수 있다. 예를 들면 다른 객체는 >ref<요소와 Spring IoC에 의해 셋팅될 참조들을 통해 참조될수 있다.

만약 당신이 XML메타데이터없이 트랜잭션적인 프록시를 조정하기 위해 Java5.0 annotations를 사용하거나 Spring factory내 정의된 많은 빈즈들을 위한 일관적인 프록시 전략을 적용하기 위한 XML의 일부를 사용하는 능력을 사용하는것과 같은 AOP프레임워크의 강력한 능력이 필요하지 않다면 좀더 간결하게 프록시를 셋업하는 많은 방법이 있다.

BeanFactory사용없이 프로그램적으로 AOP프록시를 생성하는 것은 가능하다. 비록 이것이 좀더 드물게 사용이 되더라도.

TestBean target = new TestBean();
DebugInterceptor di = new DebugInterceptor();
MyInterceptor mi = new MyInterceptor();
ProxyFactory factory = new ProxyFactory(target);
factory.addInterceptor(0, di);
factory.addInterceptor(1, mi);
// An "invoker interceptor" is automatically added to wrap the target
ITestBean tb = (ITestBeanfactory.getProxy();

우리는 이것이 자바코드로 부터 애플리케이션을 묶는 작업을 구체화하는 가장 좋은 방법이고 AOP는 예외가 아니라고 믿는다.

나는 기업용 서비스를 달성하기 위해 EJB를 사용하는 것의 대안으로 AOP의 사용이 유력하게 증가할 것이라고 믿는다. 이것은 Spring의 중요한 중심점이 될것이다.

MVC 웹 프레임워크#

Spring은 강력하고 설정가능한 MVC웹 프레임워크를 포함한다.

Sprign의 MVC모델은 Struts로부터 가져온것은 아니지만 Struts의 그것과 상당히 유사하다. Spring컨트롤러는 모든 클라이언트에 대해 한개의 인스턴스만이 수행되는 멀티쓰레드방식의 서비스 객체인 Struts Action과 유사하다. 어쨌든 우리는 Spring MVC가 Struts를 뛰어 넘어서 몇몇 중대한 장점을 가진다고 믿는다. 예를 들면

  • Spring은 컨트롤러와 자바빈 모델 그리고 뷰들 사이에 매우 분명한 분리를 제공한다.
  • Spring MVC는 당신의 액션과 폼객체를 견고한 상속을 강요하는 Struts와는 달리 매우 유연하다. Spring MVC는 전체적으로 인터페이스에 기초를 둔다. 게다가 Spring MVC프레임워크의 모든 부분에 대해서 당신 자신의 인터페이스내 플러깅(plugging)을 통해 설정가능하다. 물론 우리는 구현 옵션처럼 편리한 클래스를 제공하기도 한다.
  • WebWork처럼 Spring은 많은 요청의 핸들링으로 공통적으로 행위를 분배하기 쉽도록 만드는 컨트롤러 만큼 인터셉터를 제공한다.
  • Spring MVC는 진실로 뷰에 관용적이다. 당신이 Velocity, XLST또는 다른 뷰관련 기술들을 사용하길 원하지 않는다면 당신은 JSP를 사용하도록 강요하지 않는다. 만약 사용자 정의 뷰 기법 즉 예를 들어 당신 자신만의 템플릿 언어를 사용하길 원한다면 당신은 그것과 통합하기 위해 Spring View인터페이스를 쉽게 구현할수 있다.
  • Spring컨트롤러는 다른 객체처럼 IoC를 통해 설정가능하다. 이것은 테스트를 쉽게 만들고 Spring에 의해 관리되는 다른 객체들과 함께 멋지게 통합된다.
  • Spring MVC웹티어는 디스패처 서블릿의 컨트롤러의 명시적인 의존성과 강요된 견고한 상속을 피함으로써 Struts 웹티어보다 전형적으로 테스트하기 더 쉽다.
  • 웹 티어는 비지니스 객체 레이어의 가장 상위의 작은(thin)레이어가 된다. 이것은 좋은 실제상황을 만들어준다. Struts와 다른 전용 웹프레임워크는 당신을 당신의 비지니스 객체의 구현을 하도록 만든다. Spring은 당신의 애플리케이션의 모든 티어(tier)를 위해 통합된 프레임워크를 제공한다.

Struts 1.1에서 처럼 당신은 당신이 Spring MVC애플리케이션내 필요한만큼의 많은 디스패치(dispatcher)서블릿을 가질수 있다.

다음의 예제는 간단한 Spring컨트롤러가 같은 애플리케이션 컨텍스트내 정의된 비지니스 객체에 접근하는 방법을 보여준다. 이 컨트롤러는 handleRequest()메소드내에서 구글(google)검색을 수행한다.

public class GoogleSearchController implements Controller {

  private IGoogleSearchPort google;

  private String googleKey;

  public void setGoogle(IGoogleSearchPort google) {
    this.google = google;
  }

  public void setGoogleKey(String googleKey) {
    this.googleKey = googleKey;
  }

  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    String query = request.getParameter("query");
    GoogleSearchResult result =
      // Google property definitions omitted...

      // Use google business object
      google.doGoogleSearch(this.googleKey, query,
        start, maxResults, filter, restrict,
        safeSearch, lr, ie, oe);

    return new ModelAndView("googleResults""result", result);
  }
}

프로토타입내에서 이 코드는 Spring FactoryBean에 의해 반환되는 IGoogleSearchPort(이것은 GLUE웹서비스 프록시이다)로 부터 얻어진다. 어쨌든 Spring IoC는 웹서비스 라이브러리에 기초를 둠으로써 이 컨트롤러를 분리시킨다. 인터페이스는 밑에서 논의되는 일반적인 자바객체, 테스트 스텁(stub), 모의(mock) 객체 또는 EJB프록시에 의해 동등하게 구현될수 있다. 이 컨트롤러는 어떠한 리소스 룩업(검색)을 포함하지 않는다. 코드를 제외하곤 어떠한 것도 이것의 웹 통합을 지원하기 위해 필요하지 않다.

Spring은 데이터바인딩, 폼, 마법사 그리고 다른 복잡한 워크플로우를 위한 지원도 제공한다.

만약 당신의 요구사항이 정말 복잡하다면 당신은 다른 어떠한 전통적인 웹 MVC프레임워크보다 웹흐름을 위한 높은 수준의 추상화를 제공하는 강력한 프레임워크인 Spring Web Flow를 고려해봐야 할것이다.

Spring MVC프레임워크에 대한 좋은 소개는 Thomas Risberg의 Spring MVC튜토리얼(http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step.html)이다. 또한 "Spring프레임워크를 사용한 웹 MVC(http://www.springframework.org/docs/web_mvc.html)."도 보라.

만약 당신이 좋아하는 MVC프레임워크를 사용하는게 좋다면 Spring의 레이어화된 하부구조가 당신이 당신의 MVC레이어 없이 Spring의 나머지를 사용하도록 허락할 것이다. 우리는 Struts, WebWork 또는 웹티어내 Tapestry나 JSF를 사용하지만 미들티어 관리와 데이터접근을 위해 Spring을 사용하는 Spring 사용자들이 있다.

EJB들을 구현하기#

만약 당신이 EJB를 사용하기로 선택한다면 Spring은 EJB구현과 EJB에 대한 클라이언트측 접근의 중요한 이익을 제공할수 있다.

이것은 지금 비지니스 로직을 EJB외관 뒤 POJO로 리팩터하기 위한 가장 좋은 상황처럼 간주된다. (EJB가 컨테이너에 크게 의존적인것과 격리수준내에서 테스트가 어려운것처럼 다른 것 사이에 이것은 비지니스 로직의 단위 테스팅을 쉽게 만든다.) Spring은 EJB jar파일내 포함된 XML문서에 기반하여 자동적으로 BeanFactory를 로드함으로써 이것을 쉽게 만드는 세션빈과 메시지빈을 위해 편리한 슈퍼클래스들을 제공한다.

이것은 비상태유지 세션 EJB가 획득되고 협력자(collaborator)를 사용하는것을 의미한다.

import org.springframework.ejb.support.AbstractStatelessSessionBean;

public class MyEJB extends AbstractStatelessSessionBean
      implements MyBusinessInterface {
  private MyPOJO myPOJO;

  protected void onEjbCreate() {
    this.myPOJO = getBeanFactory().getBean("myPOJO");
  }

  public void myBusinessMethod() {
    this.myPOJO.invokeMethod();
  }
}

MyPOJO가 인터페이스라고 가정하자. 구현클래스와 원시프라퍼티와 협력자와 같은 어떠한 설정을 요구한다. 이것은 XML bean factory정의내 숨겨진다.

우리는 Spring에게 표준적인 ejb-jar.xml 배치서술자내 ejb/BeanFactoryPath라고 명명된 환경변수정의를 통해 XML문서를 로드하는 곳을 알린다. 마치 다음처럼:

<session>
  <ejb-name>myComponent</ejb-name>
  <local-home>com.test.ejb.myEjbBeanLocalHome</local-home>
  <local>com.mycom.MyComponentLocal</local>
  <ejb-class>com.mycom.MyComponentEJB</ejb-class>
  <session-type>Stateless</session-type>
  <transaction-type>Container</transaction-type>

  <env-entry>
    <env-entry-name>ejb/BeanFactoryPath</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>/myComponent-ejb-beans.xml</env-entry-value></env-entry>
  </env-entry>
</session>

myComponent-ejb-beans.xml파일은 클래스패스로부터 로드될것이다. 이 경우에는 EJB jar파일의 가장 상위에서 각각의 EJB는 자신만의 XML문서를 정의할수 있다. 그래서 이 기법은 각각의 EJB jar파일마다 여러번 사용이 될수 있다.

Spring 슈퍼클래스는 setSessionContext() 와 ejbCreate()와 같은 EJB생명주기 메소드를 구현하고 Spring onEjbCreate메소드의 구현을 개발자에게 남겨주었다.

EJB3.0이 공개초안(public draft)상태에서 유효할때 우리는 그러한 환경내에서 좀더 다양한 DI를 제공하기 위한 Spring IoC 컨테이너의 사용을 위한 지원을 제공한다. 우리는 데이터접근 API처럼 Spring과 함께 JSR-220 O/R맵핑 API를 통합할것이다.

EJB 사용하기#

Spring은 또한 EJB를 구현하는 것 만큼 그것을 사용하기 쉽도록 만든다. 많은 EJB애플리케이션은 서블릿 로케이터(locator)와 비지니스 위임(delegate)패턴을 사용한다. 클라이언트 코드 도처에 JNDI룩업을 써대는 것보다 훨씬 좋다. 하지만 그들의 일반적인 구현은 분명한 단점을 가진다. 예를 들면 :

  • EJB를 사용하는 전형적인 코드는 서블릿 로케이터와 비지니스 위임 싱글톤에 의존한다. 이것은 테스트를 힘들게 한다.
  • 서비스 로케이터 패턴의 경우에 비지니스 위임없이 사용된다. 애플리케이션 코드는 여전히 EJB home에서 create()메소드를 호출함으로써 종료되고 결과 예외를 처리한다. 게다가 이것은 EJB API에 묶이고 EJB프로그래밍 모델의 복잡함을 남긴다.
  • 비지니스 위임 패턴의 구현은 EJB에서 같은 메소드를 간단히 호출하는 수많은 메소드를 써야만 하는 명백한 코드중복의 결과를 야기한다.

이러한 그리고 다른 이유로 전통적인 EJB접근은 생산성을 제거하고 암시적인 복잡함이라는 결과를 만든다.

Spring은 코드가 없는 비지니스 위임을 소개함으로써 나가보겠다. Spring으로 당신은 실제값을 추가하지 않는 한 다른 서비스 로케이터, 다른 JNDI룩업 또는 손으로 직접 코딩한 비지니스 위임내 중복된 메소드를 쓸 필요가 결코 없을것이다.

예를 들면 우리가 로컬 EJB를 사용하는 웹 컨트롤러를 가지고 있다고 생각을 하자. 우리는 가장 좋은 상황을 끄집어 내고 EJB비지니스 메소드 인터페이스 패턴을 사용할 것이다. 그래서 EJB의 local 인터페이스는 비 EJB스펙 비지니스 메소드 인터페이스를 확장한다. (이것을 수행하기 위해 가장 중요한 이유중에 하나가 local인터페이스와 bean구현클래스내 메소드 시그너처사이 동기화를 확실하게 하는것이다.). 이 비지니스 메소드 인터페이스를 MyComponent라고 부르자. 물론 우리는 local home인터페이스를 구현하고 SessionBean과 MyComponent비지니스 메소드 인터페이스를 구현할 bean구현 클래스를 제공할 필요가 있을것이다.

Spring EJB접근으로 EJB구현물로 우리의 웹티어 컨트롤러를 걸 필요가 있는 자바코딩은 우리의 컨트롤러에 MyComponent타입의 setter메소드를 보이는 것이다. 이것은 다음처럼 인스턴스 변수처럼 참조를 저장할 것이다.

private MyComponent myComponent;

public void setMyComponent(MyComponent myComponent) {
  this.myComponent = myComponent;
}

우리는 어떠한 비지니스 메소드내 인스턴스 변수를 계속적으로 사용할수 있다.

Spring은 이것처럼 XML bean정의 엔트리를 통해 자동적으로 나머지 작업을 수행한다. LocalStatelessSessionProxyFactoryBean은 어떠한 EJB를 위해 사용될수 있는 일반적인 factory bean이다. 이것은 Spring에 의해 자동적으로 MyComponent타입으로 형변환 될수 있다.

<bean id="myComponent"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">

  <property name="jndiName" value="myComponent" />
  <property name="businessInterface" value="com.mycom.MyComponent" />
</bean>

<bean id="myController"
  class "com.mycom.myController"
>
  <property name="myComponent" ref="myComponent" />
</bean>

이 장면뒤에 많은 마법같은 것들이 있다. Spring AOP프레임워크의 특별함, 비록 당신이 결과를 즐기기 위해 AOP개념과 함께 강요하지 않는다. "myComponent" bean정의는 EJB를 위해 비지니스 메소드 인터페이스를 구현하는 프록시를 생성한다. EJB local home은 시작시 캐시된다. 그래서 단지 하나의 JNDI룩업만 있을 뿐이다. 각각의 EJB호출은 프록시가 local EJB에서 create()메소드를 호출하고 EJB에서 관련된 비지니스 메소드를 호출한다.

myController bean정의는 프록시를 위해 컨트롤러 클래스의 myController프라퍼티를 셋팅한다.

EJB접근 기법은 대량의 명백한 애플리케이션 코드를 만든다.

  • 웹 티어 코드는 EJB사용에서 어떠한 의존성도 가지지 않는다. 만약 우리가 POJO나 모의객체 또는 다른 테스트 스텁으로 EJB참조를 대체하길 원한다면 우리는 간단히 자바코드의 변경없이 myComponent bean정의를 변경할수 있다.
  • 우리는 JNDI룩업의 한줄의 코드나 우리의 애플리케이션의 일부처럼 다른 EJB연관 코드를 쓰지 않아도 된다.

우리는 단지 org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean factory bean을 통해 remote EJB에 같은 접근법을 적용할수 있다. 어쨌든 우리는 remote EJB의 비지니스 메소드 인터페이스에 RemoteExceptions을 숨길수는 없다.

테스팅#

당신들이 대개 추측하는 것처럼 나 그리고 다른 Spring개발자들은 포괄적인 단위 테스팅의 중요성을 믿는다. 우리는 프레임워크가 철저히 단위 테스트를 하고 프레임워크 디자인의 가장 중요한 목표는 애플리케이션을 단위 테스트를 위해 프레임워크를 쉽게 빌드하는 것이 필수라고 믿는다.

Spring스스로는 훌륭한 단위 테스팅을 가진다. 우리는 이 프로젝트에서 첫번째 개발 테스트의 잇점을 발견했다. 예를 들면 이것은 내부적으로 분산된 팀의 대단히 큰 효과처럼 작동하고 사용자는 CVS스냅샷이 안정되고 사용하기 안전한것을 언급한다.

우리는 Spring에 빌드된 애플리케이션이 다음과 같은 이유로 테스트 하기 매우 쉽다고 믿는다.

  • IoC가 단위테스팅을 돕는다.
  • 애플리케이션들은 테스트가 전형적으로 힘든 JNDI와 같은 J2EE서비스를 직접적으로 사용하기 위한 연관된 코드를 포함하지 않는다.
  • Spring bean factory또는 컨텍스트는 컨테이너 밖에서 셋팅될수 있다.

컨테이너 밖에서 Spring bean factory을 셋팅하는 능력은 개발 프로세스를 위해 흥미로운 옵션을 제공한다. Spring을 사용하는 다양한 웹 애플리케이션 프로젝트는 작업이 비지니스 인터페이스와 웹 컨테이너밖에서 그들의 구현을 통합 테스팅을 정의함으로써 시작된다. 단지 비지니스 성능이 사실상 완벽해진 후에 웹 인터페이스를 제공하기 위해 추가된 가벼운(thin)레이어이다.

Spring 1.1.1이후 Spring은 배치된 환경밖에서 통합테스팅의 형식을 위해 강력하고 유일한 지원을 제공하고 있다. 이것은 배치된 환경에 대해서 단위테스팅이나 테스팅을 위해 대리인같은 영향을 끼지지 않는다. 어쨌든 이것은 명백하게 품질을 향상시킬수 있다. org.springframework.test패키지는 Spring컨테이너를 사용해서 통합테스팅을 위한 가치있는 수퍼클래스를 제공한다. 하지만 애플리케이션버서나 다른 배치환경에 의존하지는 않는다. 그러한 테스트는 특별한 배치작업없이 IDE없이 JUnit만으로도 실행할수 있따. 그들은 단위테스트보다 실행하는것이 좀더 늦다. 하지만 Cactus테스트나 애플리케이션서버에 배치를 의존하는 원격테스트보다는 훨씬 빠르다. 전형적으로 이것은 내장 데이터베이스가 아닌 데이터베이스에 배치하는 수백개의 테스트를 수행하는것이 가능하다. 하지만 제품은 수초내 제품내에서 사용된다. 그러한 테스트는 당신의 Spring컨텍스트의 정확한 묶음과 JDBC나 ORM툴을 사용한 데이터접근을 빨리 확인할수 있다. 당신은 당신의 DAO구현 클래스를 테스트할수 있다.

org.springframework.test패키지내 가능한 기능은 다음을 포함한다.

  • DI를 통한 JUnit테스트 케이스를 활성화하는 능력. 이것은 테스트할때 Spring XML설정을 재사용하는것을 가능하게 만든다. 그리고 테스트를 위한 사용자 정의 셋업 코드를 위한 필요성을 제거한다.
  • 테스트 케이스 사이 컨테이너 설정을 캐시하기 위한 능력. JDBC connection pools 또는 Hibernate SessionFactories와 같은 늦게 초기화(slow-to-initialize)하는 자원이 있는 곳에 성능을 크게 증가시킨다.
  • 각각의 테스트 메소드에서 트랜잭션을 생성하고 디폴트로 테스트의 결말에 롤백을 수행하기 위한 기반구조. 이것은 다른 테스트의 환경에 대한 영향을 끼치는것없이 어떠한 데이터접근을 수행하기 위한 테스트를 위해 이것을 가능하게 만든다. 이 기능을 사용한 복잡한 프로젝트내 나의 경험으로 롤백 기반의 접근법같은 생산성과 속도의 획득은 매우 명백하다.

누가 Spring을 사용하는가.?#

Spring을 사용한 많은 제품의 애플리케이션이 있다. 사용자는 주요한 세계적인 투자은행, 잘 알려진 닷컴, 다양한 컨설턴트, 교육기관, 정부조직, 방어기구, 다양한 항공사 등을 포함한다.

많은 사용자는 Spring의 모든 부분을 사용하지만 몇몇 사용자는 격리수준내에서 컴포넌트들을 사용한다. 예를 들면 많은 수의 사용자는 우리의 JDBC나 다른 데이터접근 기능을 사용하여 시작한다.

로드맵#

2003년 10월에 이 글의 첫번째 버전이 나간후 Spring은 2004년 3월 1.0, 2004년 9월, 2005년 5월 1.2버전으로 진행되어 왔다. 우리는 "빠른 릴리즈, 자주 릴리즈"라는 기본적인 철학을 가지고 있다. 그래서 유지관리를 위한 버전과 하위 향상을 위한 버전은 전형적으로 4-6주 사이에 계속적으로 릴리즈된다.

이 시간 이후로 향상되는 사항은 다음을 포함한다.

  • RMI와 다양한 웹 서비스 프로토콜을 포함한 다중 프로토콜을 지원하는 원격 프레임워크의 소개
  • 정적이거나 인스턴스 factory메소드를 위한 호출로 부터 얻어지는 관리 객체를 위한 능력과 같은 메소드 삽입과 다른 IoC컨테이너 향상을 위한 지원
  • TopLink와 Hibernate 3.x를 포함한 좀더 많은 데이터접근 기술과의 통합
  • Java 5.0 annotations(1.2)에 의해 설정되는 선언적인 트랜잭션 관리를 위한 지원. 트랜잭션적인 메소드를 확인하기 위한 XML메타데이터를 위한 필요성을 제거한다.
  • Spring 관리객체(1.2)의 JMX관리를 위한 지원
  • Jasper 리포트, Quartz 스케줄러 그리고 AspectJ와의 통합
  • 웹 레이어 기술의 JSF와의 통합

우리는 빠른 기술혁신과 향상을 지속할것이다. 다음의 큰 릴리즈는 1.3(2005년 3분기쯤에 기대하고 있다.)이 될것이다. 계획된 향상사항은 다음을 포함한다.

  • XML설정상의 향상(1.3에서 계획중). 하나의 XML문서로 한개 이상의 객체를 정의함으로써 기본적인 Spring설정 형식을 확장하는 사용자정의 XML태그를 허락한다. 이것은 전형적인 설정을 명백하게 단순화하고 설정에러를 줄일뿐 아니라 Spring에 기초를 둔 다른 제품의 개발을 위해 이상적인 것이 될것이다.
  • Spring핵심에 Spring Web Flow 통합(1.3에서 계획중)
  • 수행중인 애플리케이션의 동적인 재설정을 위한 지원
  • Groovy, Jython또는 다른 Java플랫폼에서 구동되는 스크립트 언어에서 애플리케이션 객체를 묶기 위한 지원. 그러한 객체는 Spring IoC컨테이너의 완전한 서비스로부터 이득이 될것이고 스크립트가 변경되었을때 IoC컨테이너에 의해 그들에게 참조를 주는 데 영향이 없이 동적으로 재로딩될것이다.

민첩한 프로젝트처럼 Spring은 사용자 요구사항에 의해 조정된다. 그래서 우리는 아무도 사용하지 않는 기능에 대해서는 개발을 하지 않는다. 그리고 우리는 사용자 커뮤니티에서 주의깊게 듣고 있다.

Spring Modules는 Interface21의 Rob Harrop에 의해 진행되는 관련 프로젝트이다. 이것은 Spring핵심으로 완전하지 않은 영역으로 Spring플랫폼의 범위를 확장하는것이다. 이 프로젝트는 아직 계획중인 상태이다. 그래서 이 기능의 몇몇은 Spring핵심에 통합될것이다. Spring Modules는 Lucene검색엔진과 OSWorkflow워크플러우엔진등과의 통합같은 영역을 포함한다.

흥미있게도 이 문서의 첫번째 버전이 Spring 1.0릴리즈의 6개월전에 배포가 됐음에도 불구하고 대부분의 코드와 설정이 현재의 1.2에서 거의 변화가 없이 작동을 하고 있다. 우리는 이러한 이전버전과의 호환성을 굉장히 자랑스럽게 여기고 있다. 이것은 침략적이지 않은 API를 제공하기 위한 DI와 AOP의 능력을 설명하고 치명적인 애플리케이션을 수행하기 위해 안정적인 프레임워크를 제공하기 위한 커뮤니티에 응답을 다양하게 표시하고 있다.

요약#

Spring은 J2Ee에서 공통적으로 발생하는 많은 문제를 해결하는 강력한 프레임워크이다. 많은 Spring의 기능은 기존의 J2EE를 넘어서 자바환경의 넓은 범위에 사용가능하다.

Spring은 클래스보다 인터페이스에 프로그래밍하는 것과 같은 좋은 상황을 장려하고 비지니스 객체를 관리하는 일관적인 방법을 제공한다. Spring의 구조적인 기본은 자바빈즈 프라퍼티의 사용에 기초를 둔 Inversion of Control이다. 어쨌든 이것은 전체 그림의 일부이다. Spring은 모든 구조적인 티어들을 할당하는 포괄적인 솔루션내 기본적인 빌딩블럭처럼 IoC컨테이너를 사용하는 것중에 유일하다.

Spring은 생산성을 증가시키고 에러를 줄이는 간단하고 제품같은 JDBC프레임워크를 포함한 유일한 데이터접근 추상화를 제공한다. Spring의 데이터접근 구조는 Hibernate와 다른 O/R맵핑 솔루션과 통합한다.

Spring은 또한 JTA나 JDBC같은 트랜잭션 기술에 기초를 둔 일관적인 프로그래밍 모델을 가능하게 하는 유일한 트랜잭션 관리 추상화를 제공한다.

Spring은 선언적인 트랜잭션 관리와 POJO를 적용하기 위한 다른 기업용 서비스 또는 당신이 바란다면 당신 자신의 사용자 정의 aspect를 구현하는 능력을 제공하는 표준적인 자바로 쓰여진 AOP프레임워크를 제공한다. 이 프레임워크는 EJB의 복잡함을 줄이도록 많은 애플리케이션에 가능하도록 할수 있도록 충분히 강력하다.

Spring은 또한 전체의 IoC컨테이너로 통합될수 있는 강력하고 유연한 MVC웹 프레임워크를 제공한다.

좀더 많은 정보를 얻기 위해서는#

Spring에 대해서 좀더 많은 정보를 얻기 위해서는 다음의 자원을 보라.

  • Interface21에서 제공하는 Spring핵심 훈련 과정 - http://www.springframework.com/training.
  • Expert One-on-One J2EE Design and Development (Rod Johnson, Wrox, 2002). Spring이 이 책이 발간된 이후 많은 것이 향상되었으나 Spring의 기본개념을 이해하는데는 여전히 훌륭하다.
  • J2EE without EJB (Rod Johnson with Juergen Hoeller, Wrox, 2004). Spring과 가벼운 컨테이너 구조를 위한 이론적 설명을 순차적으로 J2EE 디자인과 개발을 다루고
  • Spring참조문서. 프린트가능한 형태로 240페이지 넘는다. Spring은 당신의 애플리케이션을 위한 템플릿처럼 사용될수 있는 가장 좋은 상황을 설명한 다양한 샘플 애클리케이션을 포함하고 있다.
  • Pro Spring: 핵심 개발자인 Rob Harrop에 의해 심도있게 Spring을 다루었다. 역자가 Spring관련책 중에 가장 추천하는 책이다. Spring의 기본개념을 이해하거나 다양한 ORM과의 연동및 Spring MVC를 위한 책으로 최고라고 생각한다.
  • Spring: A Developer's Notebook : Bruce Tate 와 Justin Gehtland에 의해 Spring을 소개했다.
  • Spring Framework 홈페이지: http://www.springframework.org/. javadoc와 다양한 튜토리얼을 포함하고 있다.
  • 포럼과 소스포지의 다운로드.
  • Spring 개발자 메일링 리스트

우리는 Spring문서와 예제를 향상시키는데 최선을 다하고 있다. 우리는 포럼과 메일링리스트의 훌륭한 응답비율에 자부심을 가지고 있다. 우리는 우리의 커뮤니티에 당신을 환영한다.

저자에 대해서

Rod Johnson has almost ten years experience as a Java developer and architect and has worked with J2EE since the platform emerged. He is the author of the best-selling Expert One-on-One J2EE Design and Development (Wrox, 2002), and J2EE without EJB (Wrox, 2004, with Juergen Hoeller) and has contributed to several other books on J2EE. Rod serves on two Java specification committees and is a regular conference speaker. He is CEO of Interface21, an international consultancy that leads Spring Framework development and offers expert services on the Spring Framework and J2EE in general.

Add new attachment

Only authorized users are allowed to upload new attachments.

List of attachments

Kind Attachment Name Size Version Date Modified Author Change note
gif
DataAccessException.gif 7.7 kB 1 06-Apr-2006 09:45 61.83.100.237
gif
SpringFramework.gif 9.5 kB 1 06-Apr-2006 09:45 61.83.100.237
« This page (revision-2) was last changed on 06-Apr-2006 09:45 by 211.45.120.88