Spring의 디자인 패턴들

이상민·2021년 11월 15일
5

스프링

목록 보기
9/9
post-thumbnail

1. 싱글턴 패턴

1-1. 싱글턴 빈

싱글턴이란 보통 전역에서 하나의 객체만 존재하는 것을 말하지만, 스프링에서는 조금 다르다. 스프링은 싱글턴의 정의를 느슨하게 하여 스프링 IoC 컨테이너 당 하나의 객체를 가지는 것을 싱글턴이라 한다. 기본적으로 스프링의 빈들은 이런 싱글턴으로 되어있다.

1-2. Autowired 싱글턴

여러곳에서 동일한 객체를 autowired 해도 이 둘의 객체 id를 비교하면, 동일한 객체를 주입 받았음을 알 수 있다. 객체 주입 시 새로운 객체를 만들도록 설정하고 싶다면, bean scope을 프로토타입으로 변경해야한다.


2. 팩토리 메소드 패턴

2-1. 어플리케이션 컨텍스트

스프링 부트를 쓰면서 사실 잊고 살기는 하지만, 기본적으로 bean을 주입 받으려면 어플리케이션 컨텍스트 객체의 getBean() 팩토리 메소드를 사용할 수 있다 (ApplicationContextBeanFactory를 상속하기 때문). 이때 어플리케이션 컨텐스트 객체가 팩토리 역할을 한다. 이는 스프링 DI 프레임워크의 근간이다.

public void whenGetSimpleBean_thenReturnConstructedBean() {
    
    ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
    
    Foo foo = context.getBean(Foo.class);
    
    assertNotNull(foo);
}


public void whenGetPrototypeBean_thenReturnConstructedBean() {
    
    String expectedName = "Some name";
    ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
    
    Bar bar = context.getBean(Bar.class, expectedName);
    
    assertNotNull(bar);
    assertThat(bar.getName(), is(expectedName));
}

2-2. 외부 설정

이 패턴은 외부 설정을 통해서도 완전히 바꿀 수 있다는 점에서 매우 유연하다.


3. 프록시 패턴

3-1. @Transactional

실제 객체 대신 프록시 객체를 사용하므로서 bean으로의 접근을 제어해 트랜잭션 일관성을 보장한다. @Transactional은 invokeWithinTransaction() 메소드를 통해 코드를 실행한다. 접근을 제어하기 때문에 다양한 동작을 그 중간에 할 수 있게 된다.

@Nullable
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
            final InvocationCallback invocation) throws Throwable {

        // If the transaction attribute is null, the method is non-transactional.
        TransactionAttributeSource tas = getTransactionAttributeSource();
        final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

            Object retVal;
            try {
                // This is an around advice: Invoke the next interceptor in the chain.
                // This will normally result in a target object being invoked.
                // 개발자가 작성한 코드가 실행되는 부분
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // target invocation exception
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                cleanupTransactionInfo(txInfo);
            }
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }

스프링은 CGLib을 통해 프록시를 만들고 bean을 래핑해 메소드가 원자적으로 실행되도록한다

Code Generator Library의 약자로, 클래스의 바이트코드를 조작하여 Proxy 객체를 생성해주는 라이브러리

Spring은 CGLib을 사용하여 인터페이스가 아닌 타깃의 클래스에 대해서도 Proxy를 생성한다.


4. 템플렛 메소드 패턴

4-1. JDBCTemplate

데이터베이스에 질의할때, 아래 단계를 거치게 된다. JdbcTemplate의 query 메소드는 템플렛 메소드 패턴으로 동작한다.

  1. 커넥션 연결
  2. 쿼리 실행
  3. 클린업
  4. 커넥션 해제

4-2. DispatcherServlet

DispatcherServletdoService() 메소드는 http 요청에 대해 처리하는 메소드이다. FrameworkServlet을 상속 받아 메소드 오버라이딩을 통해 기능을 구현한다.

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        ...

        initContextHolders(request, localeContext, requestAttributes);

        try {
            // 동작을 단계로 나눠 메소드를 작성하는 템플렛 메소드 패턴 
            doService(request, response); 
        }
        ...
        
    }

    // 자식 클래스에게 구현을 위임한다 
    protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;

}
public class DispatcherServlet extends FrameworkServlet {

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logRequest(request);
        ...
    }

}

참고

https://www.baeldung.com/spring-framework-design-patterns
https://sabarada.tistory.com/19

profile
편하게 읽기 좋은 단위의 포스트를 추구하는 개발자입니다

0개의 댓글