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

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 - 경로별 실행 범위 지정을 제공하는 범위
- 페이지 범위 - 페이지별 범위 지정을 제공하는 범위
- 스레드 범위 - 스레드별 범위 지정을 제공하는 범위
- 상속된 스레드 범위 - 스레드별 및 하위 스레드별 범위 지정을 제공하는 범위
참고
'Spring' 카테고리의 다른 글
| > [Spring Event] Spring Event 적용 - @TransactionalEventListener, @Async (0) | 2024.01.19 |
|---|---|
| > [Spring Event] Spring Event 비동기 처리 (0) | 2024.01.19 |
| > [Spring] Bean Scope - 1. 개념과 사용 방법(singleton, prototype, request) (0) | 2024.01.18 |
| > [Spring Event] Event 개념과 Spring Event 사용법 (0) | 2024.01.16 |
| > [Spring Security]- 인증 정보는 어디에 저장되는 걸까? (0) | 2024.01.10 |