반응형

POJO 구성 방식

5. @Scope 를 붙여 POJO 스코프 지정

@Scope 는 빈 스코프를 지정하는 어노테이션이다. 스프링은 기본적으로 IoC 컨테이너에 선언한 빈마다 정확히 하나의 인스턴스를 생성하고 전체 스코프에서 이를 공유한다. 이것이 스프링에서 사용하는 빈의 기본 스코프인 singleton 이다. 전체 스프링 빈 스포크는 다음과 같다.

Scope Description
singleton IoC 컨테이너당 빈 인스턴스 하나를 생성
prototype 요청마다 빈 인스턴스를 새로 생성
request HTTP 요청당 하나의 빈 인스턴스 생성. (WebApplicationContext 만 해당)
session HTTP 세션당 빈 인스턴스 생성. (WebApplicationContext 만 해당)
globalSession 전역 HTTP 세션당 빈 인스턴스 생성. (PortalApplicationContext 만 해당)

 

다음과 같이 동일한 빈에 대해 getBean 을 각각 요청할 경우, 특별히 스코프를 명시하지 않으면 singleton 으로 인스턴스를 생성한다. 고로 (1) 과 (2) 는 모두 "prod2" 를 화면에 출력하게 된다.

Product prod1 = context.getBean("product", Product.class);
Product prod2 = context.getBean("product", Product.class);

prod1.setName("prod1");
prod2.setName("prod2");

// (1)
System.out.println(prod1.getName());
// (2)
System.out.println(prod2.getName());

 

하지만 클래스에 @Scope 를 붙여 해당 빈의 스코프를 명시하게 되면, 해당 클래스의 인스턴스는 스코프에 따라 인스턴스 생성 규칙을 달리 하게 된다. 다음과 같이 클래스에 prototype 스코프임을 명시하게 되면, 위의 예제에서 (1) 은 "prod1" 을, (2) 는 "prod2" 를 출력하게 된다.

@Component
@Scope("prototype")
public class Product { ... }

 

6. 외부 리소스 데이터 사용

스프링이 제공하는 @PropertySource 를 통해 *.properties 파일을 읽어들일 수 있다. 또한 스프링 Resource 인터페이스에 @Value 를 적용하면 어떤 파일이라도 읽어들일 수 있게 된다.

 

예를 들어, 다음과 같이 정의된 option.properties 파일이 존재한다고 하자.

special.discount=0.1
summer.discount=0.15
endofyear.discount=0.2

위와 같은 파일이 존재할 때, 아래와 같이 구성 클래스에 @PropertySource 를 붙여 사용하고자 하는 properties 파일을 명시하면 endOfYearDiscount 라는 필드에 자동으로 파일 내에 정의된 값이 입력되게 된다. @Value 에서 ${key:defaultValue} 와 같이 주입받고자 하는 값을 지정할 수 있으며, defaultValue 는 만약 값을 찾지 못할 경우에 기본으로 갖게 되는 값을 의미한다.

@Configuration
@PropertySource("classpath:option.properties")
public class ProductConfiguration {

    @Value("${endofyear.discount:0}")
    private double enfOfYearDiscount;
}

 

properties 파일 외에 다른 파일 데이터를 사용하고자 할 경우에는 Resource 인터페이스를 사용할 수 있다.

public class BannerLoader {
	
    private Resource banner;
    
    public void setBanner(Resource banner) {
    	this.banner = banner;
    }
    
    @PostConstruct
    public void showBanner() throws IOException {
    	Files.lines(Paths.get(banner.getURI()), Charset.forName("UTF-8"))
        	.ForEachOrdered(System.out::println);
    }
}

위와 같은 BannerLoader 클래스가 존재한다고 하자. showBanner() 메소드는 등록된 banner 를 읽어 파일의 내용을 차례대로 읽어 한 줄씩 출력하는 기능을 수행한다. 이 메소드에 @PostConstruct 를 붙임으로써, 인스턴스가 생성된 뒤 showBanner() 메소드를 호출하게 된다. 아래와 같이 구성 클래스를 작성하면 스프링은 어플리케이션 실행 시 BannerLoader 의 빈 인스턴스를 생성하므로 어플리케이션 실행 시 자동으로 이 메소드를 호출한다.

@Configuration
@PropertySource("classpath:option.properties")
public class ProductConfiguration {
	
    @Value("classpath:banner.txt")
    private Resource banner;
    
    @Bean
    public BannerLoader bannerLoader() {
    	BannerLoader b1 = new BannerLoader();
        b1.setBanner(banner);
        return b1;
    }
}

@Value("classpath:banner.txt") 를 통해 스프링은 banner.txt 파일을 찾고 이를 banner 필드에 주입하고 Resource 빈 객체로 변환할 수 있게 된다.

 

7. 프로퍼티 파일에서 로케일마다 다른 다국어 메시지 해석

MessageSource 인터페이스에는 리소스 번들 메시지를 처리하는 메소드가 정의되어 있다. ResourceBundleMessageSource 는 MessageSource 구현체로, 로케일별로 분릴된 리소스 번들 메시지를 해석할 수 있다.

 

리소스 번들 메시지를 구분 처리하려면 다음과 같이 ReloadableResourceBundleMessageSource 인스턴스를 자바 구성 파일에 정의한다.

@Configuration
public class Configuration {
	
    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
    	ReloadableResourceBundleMessageSource messageSource =
        	new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:messages");
        return messageSource;
    }
}

빈 인스턴스는 반드시 messageSource 라고 명시해야 ApplicationContext 가 알아서 감지할 수 있다. 위의 예에서는 자바 클래스패스 내에서 이름이 messages 로 시작하는 파일들을 찾도록 설정했다. 이렇게 MessageSource 를 정의하고 미국 로케일에서 텍스트 메시지를 찾으면 messages_en_US.properties 리소스 번들 파일을 제일 먼저 찾게 된다. 이런 이름을 가진 파일이 없거나 메시지를 찾지 못한다면 언어에 맞는 messages_en.properties 를 찾고 이 파일마저 없으면 전체 로케일의 기본 파일인 messages.properties 를 선택한다.

 

다음과 같이 ApplicationContext 를 구성해 getMessage() 메소들 메시지를 해석할 수 있게 된다. 이 메소드에서 첫 번째 인자는 메시지 키, 두 번째는 각 메시지의 매개변수 자리에 끼워넣을 값들, 세 번째는 대상 로케일이다.

public static void main(String[] args) throws Exception {
	
    ApplicationContext context = 
    	new AnnotationConfigApplicationContext(Configuration.class);
        
    String alert = context.getMessage("alert.checkout", null, Locale.US);
    
    System.out.println("Message for alert.checkout is " + alert);
}

위와 같이 messages 파일을 클래스패스에 정의하고 어플리케이션 콘텍스트의 getMessage() 메소드를 사용하면 위처럼 메시지를 가져다 쓸 수 있다. 특히 로케일별 메시지를 적용함으로써 여러 지역에서 서비스를 진행할 때 각 지역 별 메시지를 자동으로 출력하도록 할 수 있을 것 같다.

 

#Reference.

 

스프링 5 레시피(4판)

이 책은 스프링 5에 새로 탑재된 기능 및 다양한 구성 옵션 등 업데이트된 프레임워크 전반을 실무에 유용한 해법을 제시하는 형식으로 다룹니다. IoC 컨테이너 같은 스프링 기초부터 스프링 AOP/A

www.hanbit.co.kr

 

+ Recent posts