'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