태그 : Springframework2.5

스프링에서 스캔된 컴포넌트에 외부 Property 값 주입하기

서설

스프링 2.5에 새로 추가된 기능 중 예상 외로 인기 있는 기능이 컴포넌트 스캔이다. Classpath를 따라서 탐색 하다가 정해진 필터링 규칙에 맞는 스프링 빈을 찾아 컨테이너에 등록해주는 기능인데 처음 들었을 때 잘 못 쓰면 독이 되겠다 싶었다. Autowiring은 몰라도 최소한 어떤 빈이 사용되는지는 XML에 등록해 주는 것이 관리 하기 좋지 않겠냐는 생각이었다. 빈 등록까지 자동으로 해버리면 애플리케이션 구성을 추적하기 어려워 유지보수가 곤란할 것이기 때문이다. 결국 나는 서비스 계층 후면은 XML을 사용한 전통적인 빈 설정으로, Web Tier는 빈 스캐닝과 Autowiring을 이용하는 방식으로 용처를 정리해서 사용하고 있다.

하지만 내가 결정권이 없는 여러 프로젝트에서 빈 스캐닝 기능을 사용하는 일이 많은 것이 현실이다. 그리고 절대 저질러서는 안 되는 범죄도 아니고 대부분 그리 복잡한 애플리케이션도 아니니 잘못 되었다고 말할 것도 아니다. 결국 두어 번 이런 상황에서 일을 하게 되었다. (사실 XML로 설정을 한다고 해도 관리가 허술하면 복잡하기는 마찮가지다. 설정 관리자라는 롤이라도 두어야 하는 걸까?)

이런 저런 것 떠나서 빈 스캔 기능을 사용하면 가장 곤란한 것이 특정 값을 빈에 설정할 수 없다는 것이다. 스프링은 단순히 빈 간의 종속성 문제만 해결해줄 뿐 아니라 빈의 초기 상태를 설정할 수도 있다. 그런데 XML이 아닌 방법으로 스프링에 의존하지 않고 POJO의 순수성을 유지한채 특정 값을 설정하는 방법이 마땅치 않다. (적어도 내 지식으로는 그렇다.)

애노테이션 환경에서 Property 값 설정

PropertyInjectionConfigurer는 스프링의 PropertyPlaceholderConfigurer나 PropertyOverrideConfigurer와 비슷한 기능을 한다. 즉, 빈 초기화 단계에서 지정된 Property 파일의 값을 특정 빈의 필드에 넣거나 특정 메소드를 이 값을 가지고 호출한다. 다만 XML 설정이 아닌 애노테이션 방식을 사용한다는 것이 다르다. @Property 애노테이션을 Property 값을 받기 원하는 필드나 메소드에는 달아 놓으면 PropertyInjectionConfigurer가 빈 초기화 단계에 이 애노테이션을 인식해서 원하는 값을 찾아 설정을 해준다.

물론 지금 1.0 M4 상태인 Spring JavaConfig나 1.0 M3 상태인 스프링 3가 정식 발표 된다면 문제가 안 되는 부분일 수 있다. 그러니 스프링 2.5만을 사용하고 JavaConfig를 기다릴 수 없는 (또는 JavaConfig를 사용 안 할) 프로젝트에만 사용하면 될 듯 하다. 그리고 JavaConfig의 @ExternalValue가 맘에 안 드는 사람도 있을 듯 하다.

파일 다운로드

property_injection.tar.gz

소스 파일을 이클립스에서 Java 프로젝트(Spring IDE plugins가 설치되어 있으면 spring project)로 Import해서 열면 바로 사용이 가능하다. 일단 처음에는 테스트가 있으니 작동 유무를 이를 통해서 확인 한다.

기존에 프로젝트 코드 베이스에 포함되어 있던 클래스 하나만 분리해서 만든 프로젝트이니 굳이 jar로 만들어 쓰기 보다는 그냥 사용할 프로젝트의 코드 베이스에 복사해서 패키지를 적절하게 변경한 다음에 사용하는 것이 좋을 듯 하다.

설정

가장 먼저 PropertyInjectionConfigurer를 XML에 등록한다.

    <bean class="property_injection.PropertyInjectionConfigurer">
        <property name="location" value="property_injection/test.properties"/>
    </bean>

location 속성에 설정 값이 등록되어 있는 propertis 파일의 경로를 지정한다. 만약 properties 파일이 여러개라면 이렇게 할 수도 있다.

    <bean class="property_injection.PropertyInjectionConfigurer">
        <property name="locations">
            <list>
                <value>classpath:property_injection/test.properties</value>
                <value>classpath:property_injection/another.properties</value>
            </list>
        </property>
    </bean>

물론 간단히 직접 Properties 값을 주는 것도 가능하다.

    <bean class="property_injection.PropertyInjectionConfigurer">
        <property name="properties">
            <value>
                mail.server=mail.domain.net
                mail.sender=admin@domain.net
            </value>
       </property>
    </bean>

설정 방법은 PropertyPlaceholderConfigurer와 같으니 레퍼런스를 참조하도록 한다.

필드에 @Property 사용

일단 PropertyInjectionConfigurer를 등록했으면 Property 값을 지정하고 싶은 필드나 메소드에 @Property 애노테이션을 표기해야 한다. 직접 필드에 값을 지정하도록 할 수 있다.

    @Property(name="mail.server")
    private String mailServer;

메소드에 @Property 사용

메서드에 @Property를 달아 놓으면 초기화 단계에 해당 메소드를 호출한다. 이 메소드는 매개 변수가 하나 이상이어야 한다.
   
    @Property(name="mail.server")
    public void setMailServer(String mailServer) {
        this.mailServer = mailServer;
    }

필수가 아닌 Property

만약 지정한 Property 값을 찾지 못하면 예외를 발생시키기 때문에 빈 생성을 하지 못하게 된다. Property 값이 있어도 되고 없어도 되는 값이라면 required 속성를 false 값으로 바꾸어 예외 발생 없이 넘어가도록 할 수 있다.

  @Property(name="property.name" required=false)

required 속성을 사용할 때에는 기본값을 필드에 지정하는 것이 좋을 것이다.

  @Property(name="mail.server" required=false)
  private String mailServer = "default.mailserver.com";

by 박성철 | 2009/05/05 05:09 | 프로그래밍 이야기 | 트랙백(26) | 덧글(0)

스프링 프레임워크 2.5의 @Service와 @Controller 관련 기능

어제 올린 What's New in Spring 2.5: Part 1를 보고 회사 동료가 게시판에 글을 올렸습니다.

그리고 @Repository,@ Service,@ Controller 과 @Component의 차이를 모르겠습니다.
@Compoent을 상속한거 같은데.. 각각 뭔가 기능이 추가 된듯 한데요..
DI만을 위해서는 이렇게 스테레오타입을 나눌 필요가 없지 않을까.
각각 어노테이션이 무얼 하는지 알 필요가 있지 않을까 생각됩니다.
대충 Repository는 data access exception translation 을 한다고 써 있네요

라고...

일단 저는 이렇게 답했습니다.

스테레오 타입을 만들어 놓고 전혀 사용을 안하는 것은 아니겠지만 기능 제공보다 구별할 수 있게 되었다는게 더 중요한 이유는 스프링을 가지고 상용 솔루션을 만드는 BEA같은 회사에서 추가적으로 각 컴포넌트를 역활별로 구별해서 다른 처리를 할 수 있게해주기 때문이라고 생각... 즉 스프링도 기본적으로 뭔가 역활별로 특화된 기능을 제공하는게 있겠지만 스프링을 커스터마이제이션하는 쪽에서 특화된 기능을 구현할 수 있게 되었다는 '가능성'이 더 중요하다는 말....


이렇게 답해 놓고 생각해보니 스프링은 @Service와 @Controller를 만들어 놓고 어떻게 쓰고 있을지 궁금했습니다. 그래서 소스를 받아 이클립스에 프로젝트 등록한 후 Java Search를 했습니다. 무식하게... 그래서 나온 결론...

@Service

관련된 기능이 전혀 없습니다. 그냥 스프링 자체에서는 역할 구별하는 표시용 말고는 없습니다. 표시를 하게 해줄테니 너희가 뭔가 하고 싶은게 있으면 만들어서 써라... 라는거죠.

@Controller

예상 했던 것 처럼 Sprint MVC와 Portlet에서 사용합니다. 다른 것은 아니고 Controller를 자동으로 인식하는 기능에서 @RequestMapping 어노테이션이 없고 @Controller만 있는 MVC 컨트롤러를 찾는데 사용하네요.

이상 끝.... ㅡ.ㅡ;



P.S. 참고로 첨언하면... @Repository, @Service, @Controller는 어떤 종류의 컴포넌트인지 표시하는 역할을 하구요. @Component는 그냥 특별한 역할 구분 없이 컴포넌트라는 표시만 합니다. 당연히 @Repository, @Service, @Controller는 @Component를 상속했구요. 그러니까 DB 작업 관련 컴포넌트는 @Repository를, 서비스 관련은 @Service, MVC 컨트롤러는 @Controller를 사용하고 그외 컴포넌트들은 @Component를 사용해서 표시해주면 두고두고 유용하게 쓸 수 있습니다. 당장은 컴포넌트 자동 인식 기능으로 사용 할 수 있구요.

by 박성철 | 2007/11/22 18:02 | 프로그래밍 이야기 | 트랙백 | 덧글(0)

What's New in Spring 2.5: Part 1


스프링 2.5가 드디어 릴리즈 되었네요.
좀 천천히 관심 둘 여유있을 줄 알았는데... 플젝 투입되어 있어서 정신이 없습니다.

19일, 인포큐에 스프링 2.5의 새로운  부분에 대한 글이 올라왔었습니다.
 
http://www.infoq.com/articles/spring-2.5-part-1

그냥 이런게 올라왔었구나... 나중에 읽어야지.. 하고 미뤄두었었는데 릴리즈도 된 마당에 대충이라도 읽어봤습니다.

2.0에서 XML 스키마 지원으로 스프링을 즐겁게 쓰도록 만들어 주었었는데... 이번에는 더욱 단순하고 강력한 스프링이 되었다고 하네요. 2.0은 2.5로 가기 위한 맛보기...ㅎㅎ

개인적으로 어노테이션을 통한 설정이 생각 만큼 생산성을 높여줄지에 대해서는 의문이지만 XML를 끔찍하게 여기는 사람들이 많기 때문에 매우 환영할만 하다고 생각합니다.

이왕 글을 쓴거 위 글의 내용을 약간 요약해보겠습니다. infoq에 기고된 글의 저작권이 어찌되는지 모르지만 요약 정도는 상관 없겠죠?

이 글은 총 3회에 나눠서 쓰여질 기고문의 첫번째에 해당합니다. 주로 어노테이션과 관련된 부분에 대해서 설명하고있구요.두번째 글은 Web Tier 쪽을, 마지막 글은 통합과 테스팅에 관련된 기술을 논하려고 한답니다. 아마도 두번째는SpringMVC를 마지막은 JUnit 4 지원과 OSGi가 될 듯 하네요.

JSR-250 어노테이션 지원

스프링 2.5에 새로 지원하는 어노테이션은 다음과 같다고 합니다.
  • @Resource
  • @PostConstruct
  • @PreDestroy
@Resource

DI를 위해서 사용합니다.
빈 컨테이너 안의 빈들 중에서 지정된 이름의 빈을 찾아서 DI를 해줍니다. 아래 예에서라면 스프링은 관라하는 빈들 중 이름이 dataSource인 놈을 가지고 setDataSource()를 호출하는거죠.
@Resource(name="dataSource")
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
직접 맴버 변수에 지정할 수도 있는데 이렇게 하면 setter 없이도 DI가 된다네요. 요거 맘에 듭니다.

@Resource
private DataSource dataSource; // inject the bean named 'dataSource'

위의 예에서 처럼 이름을 지정하지 않으면 변수 이름이 자동으로 사용되네요. 아래처럼 setter 메소드에 이름 없이 지정 할 수도 있다고 합니다.
private DataSource dataSource;
@Resource
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
이름으로 빈을 찾았는데 없으면 타입으로 찾아서 인젝션 해준답니다. 원하지 않으면 이 기능을 끌 수도 있습니다.

Lifecycle 어노테이션 @PostConstruct와 @PreDestory

기존에 빈 생성시 빈을 초기화하고 소멸시 정리하는 작업을 하려면 InitializingBean과 DesposableBean 인터페이스를 상속하거나 빈설정시 명시적으로 초기화 메소드와 정리 메소드를 지정해 주었어야 합니다.
인터페이스를 사용하면 자동화 되어 좋기는 하지만 스프링 디펜던시가 생기고 - 솔직히 이 빈을 다른 곳에서 쓸 일이 얼마나 있겠냐만 늘 결벽증 때문에 사용안하게 된다죠 - 그래서 설정을 주로 사용하곤 했습니다.

사용은 간단합니다. 초기화 메소드와 정리 메소드 위에 각 어노테이션을 표기만 하면 되는거죠.

public class FilePoller {

@PostConstruct
public void startPolling() {
...
}
...

@PreDestroy
public void stopPolling() {
...
}
...
}
늘 XML 설정 할 때 마다 빼먹을까봐 신경 썼었는데 좋네요.

정밀한 오토와이어링

2.5 이전에도 여러가지 오토와이어링 방법을 제공했습니다. 컨스트럭터, 셋터의 타입, 셋터의 이름, 자동인식.... 하지만 전체적으로어떤 것을 선택할지 설정 할 수는 있어도 어떤 경우에는 타입으로 찾고 어떤 경우에는 이름으로 찾을지 정밀하게 제어 할 수는 없었습니다.

위에서 말한 것 처럼 2.5에서는 @Resource 어노테이션으로 메소드별 필드별로 이름 기반의오토와이어링을 지정 할 수 있게 되었습니다. 하지만 이건 제약이 있어 스프링 2.5에서는 @Autowired 어노테이션을추가했다고 합니다. 이건 비표준인거죠. 스프링 디펜던시가 생깁니다.

@Autowired 어노테이션은 컨스트럭터, 메소드, 필드에 사용하며 타입으로 오토와이어링 하도록 지정하는 기능을 가지고 있습니다. 그런데 메소드가 꼭 셋터일 필요는 없습니다. 심시어 패러미터가 여러개인 경우도 상관 없답니다. 오호...
@Autowired
public void setup(DataSource dataSource, AnotherObject o) { ... }

기본적으로 이 어노테이션으로 표시된 항목은 필수인 것으로 처리되지만 required 옵션을 false로 하면 빈을 못 찾아도 그냥 넘어갑니다. 예를 들어 다음에서 빈을 못찾으면 그냥 하드코딩한 DefaultStrategy가 사용되는거죠.

@Autowired(required=false)
private SomeStrategy strategy = new DefaultStrategy();

이렇게 타입으로 빈을 찾는데 해당 타입의 빈이 하나 이상일 경우 스프링은 오토와이어링이 실패한 것으로 처리합니다. 이 문제를 해결하기 위한 몇가지 방법이 있습니다. 우선 빈 설정에서 primary 속성을 사용하는 방법이 있다.

<bean id="dataSource" primary="true" ... />

이번 2.5에는 보다 정밀한 제어가 가능한데 @Qualifier 어노테이션을 사용하면 빈 이름을 지정할 수 있다. 타입이 아닌 이름으로 찾게 됩니다.

@Autowired
@Qualifier("primaryDataSource")
private DataSource dataSource;

이 어노테이션을 별도로 만든 이유는 다음 처럼 사용할 수 있게 하려는 것이라네요. 머리도 좋아요. 이 어노테이션은 컨스트럭터 매개변수, 필드, 메소드 매개변수 어디에도 쓸 수 있답니다.

@Autowired
public void setup(@Qualifier("primaryDataSource") DataSource dataSource, AnotherObject o) { ... }

이 뿐 아니라 커스텀 어노테이션을 사용한 인젝션에도 사용될 수 있다는데요. 요건 읽기 귀찮아서 일단 PASS...

스프링 컴포넌트 자동 인식

스프링 2.0부터 스테레오 타입 개념의 어노테이션이 도입되었습니다. @Repository는 데이타 억세스 코드라는 표시를 하는 어노테이션으로 사용된 것이죠.
2.5에는 일반적인 3-Tier 아키택쳐에서의 역활을 표기하기 위해 @Service와 @Controller라는 어노테이션을 추가했습니다. 이와 함께 제너릭 어노테이션인 @Component도 추가...

이들 어노테이션들은 객체에 역할을 부여해서 AOP나 후처리기가 뭔가 추가적인 작업을 하도록 해줍니다.

이 어노테이션들로 사용할 수 있는 새 기능 중 하나가 컴포넌트 자동 인식 기능... 오토와이어링을 쓰더라도 기본적인 빈 설정을 해주었어야 했지만 이 컴포넌트 자동 탐색 기능을 쓰면 이 기본적인 빈 설정 조차 필요없게 됩니다.

2.5의 PetClinic 샘플을 보면 하부구조에 해당하는 컴포넌트들은 XML에 설정이 되어 있지만 Web Tier 컨트롤러들은 자동 인식 기능을 사용하도록 되어 있다는군요. 저도 처음 이 기능을 들었을 때에 Web Tier에 적용하면 좋겠다고 생각되더군요. XML의 명시적이고 중앙집중적인 특징도 무시는 못하죠. 개발할 때에는 편의상 오토와이어링을 쓰더라도 운영환경에서는 XML이 역시... 혹시 새로 생긴 자동 탐색 기능과 오토와이어링 기능으로 구성된 빈 컨테이너의 내용을 XML로 생성해주는 방법이 있다면 좀 알려주세요.

<context:component-scan base-package="org.springframework.samples.petclinic.web"/>

이렇게 지정된 패키지 안의 클래스들을 탐색해서 스테레오타입이 지정된 클래스들을 찾아냅니다. 예를 들면 이런 Spring MVC 컨트롤러...

@Controller

public class ClinicController {

private final Clinic clinic;

@Autowired
public ClinicController(Clinic clinic) {
this.clinic = clinic;
}
...

탐색 규칙을 커스터마이제이션 할 수도 있습니다. 다음과 같이 하면 스테레오 타입을 찾는 기본 룰은 사용하지 않고 Stub로 시작하는 클래스들과 Mock 어노테이션이 되어 있는 클래스들을 스프링 객체로 인식하게 할 수 있습니다.

<context:component-scan base-package="example" use-default-filters="false">
<context:include-filter type="aspectj" expression="example..Stub*"/>
<context:include-filter type="annotation" expression="example.Mock"/>
</context:component-scan>

기본 탐색 규칙을 조정 할 수도 있습니다. 아래 예는 탐색시 @Repository 어노테이션을 제외시킵니다.

<context:component-scan base-package="example">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>

 물론 커스텀 어노테이션을 탐색에 사용하게 할 수도 있습니다. @Generic 제넥릭 어노테이션으로 다음과 같이 커스텀 어노테이션에 @Generic 어노테이션을 사용하면

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface BackgroundTask {
String value() default "";
}

이 어노테이션이 지정된 객체들은 모두 스프링 객체로 인식이 됩니다.

@BackgroundTask
public class FilePoller {

@PostConstruct
public void startPolling() {
...
}

@PreDestroy
public void stopPolling() {
...
}
...
}

이렇게 자동 인식된 빈들은 클래스 이름으로 등록이 됩니다. 위의 예에 나오는 FilePoller 클래스는 filePoller라는 이름으로 스프링에 등록되는거죠. 만약 다른 이름을 원하면 이렇게 합니다.

@Service("petClinic")
public class SimpleJdbcClinic {
...
}

기본적으로 스프링은 빈을 싱글톤으로 관리하지만 다른 스코프를 써야 할 경우도 있습니다. 이 경우는 요렇게...

@Component
@Scope("session")
public class ShoppingCart {
...
}

전 지금 진행중인 프로젝트에 2.5를 적용할 예정입니다. 원래 아발론으로 만들어진 서버 프로그램인데 스프링으로 바꾸고 있거든요.

프로그래밍이 즐거워지네요. ㅎㅎ

아! 아시겠지만 제가 잘못 이해해서 생긴 오류에 대해서는 책임 안집니다. ;;;

by 박성철 | 2007/11/21 18:20 | 프로그래밍 이야기 | 트랙백(2) | 덧글(1)

◀ 이전 페이지다음 페이지 ▶