'How to controll the lifecycle of a custom scope in Spring
I have been trying to create a custom scope in Spring Framework, but I fail to understand how is the scope actually instantiated and destroyed.
Assuming I want to create an EventScope and I have a com.example.EventHandler.handleEvent(Event event) method that handles an incoming event.
How do I ensure that every Event starts/stops a scope when it is being handled? If I have several EventScoped beans wired in, How do I make sure they are using the same scope instance during an event? It seems like a new EventScopeImpl would be created for every bean? If I have to use a ThreadLocal or some other static context, what is even the point of using a custom scope when I can use the ContextHolder class directly?
My code is as follows?
EventScopeImpl.java
public class EventScopeImpl implements Scope {
private final Map<String, Object> scopedObjects
= Collections.synchronizedMap(new HashMap<String, Object>());
private final Map<String, Runnable> destructionCallbacks
= Collections.synchronizedMap(new HashMap<String, Runnable>());
@Override
public Object get(final String name, final ObjectFactory<?> objectFactory) {
if(!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
@Override
public Object remove(final String name) {
Optional.ofNullable(destructionCallbacks.remove(name)).ifPresent(Runnable::run);
return scopedObjects.remove(name);
}
@Override
public void registerDestructionCallback(final String name, final Runnable callback) {
destructionCallbacks.put(name, callback);
}
@Override
public Object resolveContextualObject(final String key) {
return EventContextHolder.instance().getValue(key);
}
@Override
public String getConversationId() {
return null;
}
}
EventScope.java
@Qualifier
@Scope(value = "event", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EventScope{}
EventContextHolder.java
public class EventContextHolder{
@Getter
@Accessors(fluent = true)
public static final EventContextHolder instance = new EventContextHolder();
private final static ThreadLocal<Context> context = ThreadLocal.withInitial(Context::new);
private EventContextHolder(){}
public void setValue(final String key, final Object value){
context.get().data().put(key,value);
}
public Object getValue(final String key){
return context.get().data().get(key);
}
public void clear(){
context.get().data().clear();
}
private static class Context{
@Accessors(fluent = true)
@Getter
private final ConcurrentHashMap<String,Object> data = new ConcurrentHashMap<>();
}
}
EventAspect.java
@Aspect
@Component
public class EventAspect {
@Pointcut("execution(com.example.EventHandler.handleEvent(..))")
public void eventHandlerMethod() {};
@Around("eventHandlerMethod()")
public Object startMessageContext(ProceedingJoinPoint pjp) throws Throwable {
final Event event;
if(pjp.getArgs() > 0 && Event.class.isInstance(pjp.getArgs()[0]){
event = (Event)pjp.getArgs()[0];
}
@Cleanup("clear") EventContextHolder context = EventContextHolder.instance();
context.setValue("event",event);
Object retVal = pjp.proceed();
return retVal;
}
}
EventContext.java
@RequiredArgsConstructor
@Getter
@Component
@EventScope
@Lazy
public class EventContext {
@Lazy
@Value("#{event}")
private Event event;
}
EventScopeConfiguration.java
@Configuration
public class EventScopeConfiguration {
@Bean
public BeanFactoryPostProcessor eventScopeBeanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope(
"event", new EventScopeImpl());
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
