top bar

글 목록

레이블이 springframework인 게시물을 표시합니다. 모든 게시물 표시
레이블이 springframework인 게시물을 표시합니다. 모든 게시물 표시

2017년 12월 14일 목요일

[Spring] Request Bean Scope

스프링에서의 Bean이 기본적으로 'Singleton'이라는 것은 누구나 안다. 하지만 Singleton으로 관리되는 Bean의 Lifecycle을 'Scope'어노테이션을 이용해 제어 할 수 있다. 스프링의 Bean Scope는 총 5가지 인데, singleton, prototype, request, session, global session이 그것이다.

이 중에서, 조금은 특이한 방법으로 관리되는 request scope의 bean에 대해서 살펴 보고자 한다.

가령, 아래와 같은 HelloMessageGenerator 라는 클래스가 있다고 하자.

class HelloMessageGenerator {
    public String getMessage() {
        return "Hello " + this;
    }
}

HelloMessageGenerator의 getMessage메소드를 통해서 문자열을 가져오는데, 자바에서 유일하게 오버로딩 된 '+' 연산자를 통해서 'Hello [참조값]' 이라는 문자열을 반환한다. 이를 통해 getMessage를 호출하는데 사용되는 인스턴스의 참조값을 알 수 있다.

그리고 아래처럼 Spring Configuration에, 해당 클래스의 Bean 설정 코드를 작성한다.

@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloMessageGenerator requestMessage() {
    return new HelloMessageGenerator();
}

HelloMessageGenerator의 빈 설정을 보면 Scope어노테이션에 'proxyMode' 라는 것이 눈에 띈다. Aop를 적용하려는 것도 아닌데 왜 proxyMode를 설정해야 할까?

'request scope' 라는 것은 말그대로 Bean의 lifecycle이 request와 같다는 것이다. 때문에 해당 Bean은 '요청 -> 응답'이 끝나면 제거 되며, 요청이 없으면 생성되지 않는다. reuqest scope의 이러한 속성 때문에 proxy를 설정해야 하는데, 이유는 이렇다. 

일단 아래처럼 Controller를 작성해 보자.

@Controller
public class ScopesController {
    @Resource(name = "requestMessage")
    HelloMessageGenerator requestMessage;
 
    @RequestMapping("/scopes")
    public String getScopes(Model model) {
        requestMessage.setMessage("Good morning!");
        model.addAttribute("requestMessage", requestMessage.getMessage());
        return "scopesExample";
    }
}

서블릿 컨테이너에 스프링 기반의 웹 어플리케이션이 로딩될때 (쉽게 말해 톰캣을 구동할때) 각 빈이 생성되고 빈 컨테이너에 등록되며 'Dependency Injection'이 일어난다. 한마디로 이 시점에 각 객체의 의존관계를 바탕으로 빈이 주입 된다는 것이다. 위 경우는 ScopesController에 HelloMessageGenerator의 인스턴스가 주입되어야 한다. 하지만 해당 빈은 request scope이기때문에 생성할 수 없다. 때문에 'proxy'를 임시적으로 생성하여 의존성 주입을 수행하는 것이다. 그 후에 진짜 요청이 들어오면 다시 초기화를 하여 빈을 사용하게 된다.

proxy는 일단 그렇고, 결과적으로 위 코드를 수행하면 view단에 찍히는 'requestMessage' 문자열이 요청 때마다 다른 값으로 노출 될 것이다. 참고로 session, global session scope 설정 시에도 proxyMode를 지정해 줘야 하는데, 원리는 request scope의 경우와 같다.

정리 끗.

참고 - http://www.baeldung.com/spring-bean-scopes

2017년 7월 9일 일요일

[Spring] POJO의 이해

개요



'POJO'란, 'Plain Old Java Object'의 약자이다. 이 용어의 유래를 잘 찾아보면 굉장히 허무할 것이다. 이유는 아래와 같다.

2000년 즈음에, 마틴파울러는 '평범한' JAVA 오브젝트에 비즈니스 로직을 넣어 사용하는 것이 기존에 널리 사용되던 EJB 빈보다 뛰어난 점에 주목하고 있었는데, 이러한 장점에 비해 폼나는 용어가 없어서 만들었다는 것이 바로 'POJO' 라는 것이다. 링크 참고

아래의 spring.io 웹사이트에, 스프링 관점에서 POJO의 이해를 돕는 글이 있다.

https://spring.io/understanding/POJO#understanding-pojos

위의 글 또한, POJO를 한 문장으로 잘 설명하고 있다.






여기서 'be bogged down' 이라는 문구가 낯설었는데, 이 숙어의 뜻을 찾아보니 '너무 복잡하거나 어려워서 다른것을 할 수 없는' 이라는 뜻이었다. 따라서 위 문장은 "프레임워크에 의해 확장된 즉, 그것으로 인해 'bogged down' 되지 '않은' 자바 오브젝트" 정도의 뜻이 되지 않을까 싶다. 뭐 이러한 뜻풀이는 난해 할 수도 있으니, 그냥 '평범한 자바 오브젝트' 라고 이해해야 겠다.


예시를 통한 이해



가령, 'JMS'를 통해 메시지 수신의 기능을 하는 클래스를 설계한다고 해보자. 아래와 같을 것이다.

public class ExampleListener implements MessageListener {

    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            try {
                System.out.println(((TextMessage) message).getText());
            }
            catch (JMSException ex) {
                throw new RuntimeException(ex);
            }
        }
        else {
            throw new IllegalArgumentException("Message type error");
        }
    }
}

위 클래스는 특정 메시징 솔루션(JMS)의 인터페이스를 구현(implements) 하고 있다. 다시 말하면 해당 인터페이스에 '종속'되어 있는 것이다. 따라서 다른 대안적인 메시징 솔루션을 적용하려고 한다면 매우 큰 변경 비용이 들 것은 자명하다. (당연히, 거대한 코드 베이스를 가지고 있는 대규모 프로젝트의 경우를 말한다)

이것을 'POJO'의 관점에서 접근한다면, 메시지를 처리하는 클래스가 특정 솔루션에 종속되지 않은, (원문에서는 'solution free'라고 표현한다) 상태로 설계 되어야 한다. 아래와 같다.

@Component
public class ExampleListenerPojo {

    @JmsListener(destination = "myDestination")
    public void processOrder(String message) {
        System.out.println(message);
    }
}

이것이 스프링이 추구하는 'POJO' 클래스의 모습이다. 'JMS'와 관련된 확장 코드가 보이지 않으며, 따라서 'JMS'와 커넥션을 연결하고 메시지를 처리하는 책임은 모두 어노테이션에 위임 된 것을 볼 수 있다.

그리고 만약 'JMS'에서 'RabbitMQ'로 메시징 솔루션을 변경한다고 했을때, 개발자는 '@JmsListener' 어노테이션을 '@RabbitListener' 어노테이션으로 교체 하기만 하면 된다. 또한 이러한 설계는 라이브러리에 종속적이지 않아 단위 테스트를 용이하게 해 준다. 여기서 스프링 'POJO'의 강력함이 드러난다고 볼 수 있다.

이렇듯 'POJO'를 통해, 스프링은 개발자의 코드와 외부 라이브러리의 'Coupling' 을 최소화 시켜준다. 이것은 다시말하면 개발자가 작성한 서비스 코드는 그 서비스 자체로 주체가 되는 것이 아니라 전체 어플리케이션의 한 part로서 동작하고, 전체 어플리케이션에 '연결'(wiring) 되게 함으로서, 좀더 확장성 있는 소프트웨어 설계를 가능하게 해준다는 것이다. 또한 이것은 'Inversion Of Control(IOC)'과 'Dependency Injection(의존성 주입)' 의 메인 컨셉이기도 하다.

하지만 단순히 클래스 설계시 'implements'와 'extends' 키워드를 사용하지 않는다고 해서 POJO 인가? 라고 했을때는 조금은 의아하다. 스프링을 사용한 프로젝트는 이미 어노테이션으로 떡칠(?)되어 있을 것이고, 이 어노테이션 자체도 특정 라이브러리에 종속되어 있기 때문이다.

'스프링은 완전한 POJO를 제공한다'에 대해선 반대의 입장이지만,
흠.. 내공이 부족해서 일까. POJO의 관해선 조금 더 연구가 필요 할 것 같다.