개발 기록

> [Spring] Bean Scope - 2. 사용자 정의 Bean Scope 본문

Spring

> [Spring] Bean Scope - 2. 사용자 정의 Bean Scope

1z 2024. 1. 18. 13:25

 

 

 

 

 

1. 개요 

빈 범위를 재정의하여 확장할 수 있지만,  싱글톤 과 프로토타입 빈은 재정의 할 수 없을 뿐더러 일반적으로 범위를 재정의 하는 것은 지양하고 있다. 하지만  추가 기능을 요구하는 상황도 있기 때문에 Spring 2.0 부터는 사용자 정의 Spring Bean 범위를 정의할 수 있을 뿐만 아니라 기존 Spring Bean 범위(싱글톤 및 프로토타입 범위 제외)를 수정할 수도 있다.

 

 

1.   Custom Scope 구현 

빈의 범위는 스레드로 하여. 즉, 각 스레드에 대해 하나의 빈이 사용자 정의 스레드 범위 빈에 따라 생성되도록 해본다.

*Spring 3.0에는 SimpleThreadScope 라는 스레드 범위 클래스가 포함되어 있다. 

 

(1) Scope 인터페이스 구현

scope 인터페이스의 정의된 5가지 메서드 중 get 메서드만 구현해도 된다. 나머지는 선택 사항 이다.

import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
 
public class CustomThreadScope implements Scope {
    CustomThreadLocal customThreadLocal = new CustomThreadLocal();
   
   // 명명된 개체가 범위에 없을 시 새 개체를 만들고 반환한다.
    @Override
    public Object get(String str, ObjectFactory objectFactory) {
        @SuppressWarnings("unchecked")
        Map<String, Object> scope = (Map<String, Object>) customThreadLocal.get();
        Object object = scope.get(str);
        // 명명된 객체가 없을 경우
        // ObjectFactory를 사용하여 새 객체를 생성하고 map 에 추가 후 반환.
        if (object == null) {
            object = objectFactory.getObject();
            scope.put(str, object);
        }
       
        return object;
    }
   
    @Override
    public String getConversationId() {
        return null;
    }
   
    // 명명된 객체가 소멸되거나 범위 자체가 애플리케이션에 의해 소멸되는 경우 실행될 콜백 제공
    @Override
    public void registerDestructionCallback(String arg0, Runnable arg1) {
    }
   
   
   // 제거된 객체 반환
    @Override
    public Object remove(String str) {
        Map<String, Object> scope = (Map<String, Object>) customThreadLocal.get();
        return scope.remove(str);
    }
   
    @Override
    public Object resolveContextualObject(String arg0) {
        return null;
    }
   
    class CustomThreadLocal extends ThreadLocal {
        protected Map<String, Object> initialValue() {
            System.out.println("Initializing ThreadLocal");
            return new HashMap<String, Object>();
        }
    }
}

 

 

(2) Custom Bean Scope 등록 

Spring 컨테이너가 새 범위를 인식하도록 하기위해 ConfigurableBeanFactory 인스턴스 의 RegisterScope 메소드를 통해 등록한다.

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
        // args1: scope은 고유이름, 범위 식별/지정하는 데 사용. 
        // args2:사용하려는 사용자 정의 Scope 구현 의 실제 인스턴스
        factory.registerScope("threadScope", new CustomTreadScope());
    }
}

 

 

(3) Custom Bean Scope 사용 

@Configuration
public class BeanConfig {

    @Bean
    @Scope("threadScope")
    public User user() {
        User user = new User();
        user.setName("홍길동");
        return user;
    }

    @Bean
    public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
        return new CustomBeanFactoryPostProcessor();
    }
}

 

(4) TEST

   @Autowired
   private ApplicationContext context;
    
    @Test
    public void custombean_test() {
        // This code will be executed by child thread
        Runnable childThread = () -> {
            User v1 = context.getBean(User.class);
            User v2 = context.getBean(User.class);
            System.out.println("하위 스레드에서 생성된 두 객체의 해시코드");
            System.out.println(v1.hashCode() + " & " + v2.hashCode());
            System.out.println("하위 스레드에 의해 생성된 두 개체가 모두 동일한가 ? :" + (v1.hashCode() == v2.hashCode()));
        };
        new Thread(childThread).start();

        // This code will be executed by main thread
        User v1 = context.getBean(User.class);
        User v2 = context.getBean(User.class);
        System.out.println("메인 스레드에서 생성된 두 객체의 해시코드");
        System.out.println(v1.hashCode() + " & " + v2.hashCode());
        System.out.println("메인 스레드에 의해 생성된 두 개체가 모두 동일한가? :" + (v1.hashCode() == v2.hashCode()));

    }
 

 

★ 결과

하위 스레드에서 생성된 두 객체의 해시코드
1522311648 & 1522311648 
하위 스레드에 의해 생성된 두 개체가 모두 동일한가요 ? :true

 

메인 스레드에서 생성된 두 객체의 해시코드
400197113 & 400197113
메인 스레드에 의해 생성된 두 개체가 모두 동일한가요? :true

 

 

위의 방식으로 아래 경우도 구현 할 수 있다.

  • Route Scope - 경로별 실행 범위 지정을 제공하는 범위
  • 페이지 범위 - 페이지별 범위 지정을 제공하는 범위
  • 스레드 범위 - 스레드별 범위 지정을 제공하는 범위
  • 상속된 스레드 범위 - 스레드별 및 하위 스레드별 범위 지정을 제공하는 범위

 

 

 

참고

https://www.geeksforgeeks.org/custom-bean-scope-in-spring/