A number of people have been asking how to create a custom scope, so I wanted to write a short guide. Unfortunately there isn't a universal solution, so the best I can do is offer up a number of guidelines and give you some questions to consider before embarking on the implementation.
Before you begin, read the Javadoc for Context, the interface you will be implementing.
The first thing to consider when implementing a context is what you will use to store the bean instances. If you want to share the bean instances between multiple requests
(such as the HTTP session scope) then you will need a way to store the bean instances and to associate the correct set of bean instances with the current request
. If you want to simply share the bean instances within the request
then you have an easier task, as you can use a ThreadLocal to store the bean instances, and just remove it at the end of the request
.
Note that when I say request
I mean any request to the application, not just an HTTP request. CDI doesn't give you any help here, but for inspiration you can think about the built in HTTP contexts such as the session context which uses the HttpSession from the Servlet specification to store the bean instances, and request listeners to associate the context with the bean instances at the start of each request.
Having decided on where the bean instances will be stored, you need to think about how to associate the bean instance store with the context at the start of each request
, and how to clean up at the end of the request
. Luckily, there is a standard formula you can follow here! Within your context, use a ThreadLocal to hold the bean instances for the duration of the request
; the lifecycle of the context would look something like this:
- At the start of the
request
associate the bean instance store with the context by storing it in the thread local - During the
request
service any calls to Context.get() using the ThreadLocal - At the end of the request ensure that you clean up the ThreadLocal to prevent any memory leaks
There is a variant of this, where the bean instance store used during the request
does not write through to the underlying bean instance store, but is instead synchronised (e.g. last request
to end wins) at the end of the request
. This has the advantage of improving the apparent consistency of the data (consider multiple concurrent requests, if you write through to the underlying store the state at the end of the request could be a mix of that written in each request), as well as allowing for lazy-creation of the underlying store. This is also relatively simple to implement, the only complex part being the decision on synchronization scheme to use.
Once you have implemented your context, you need to register it, which you do via AfterBeanDiscovery.addContext.
I hope this is of use to people, and if anyone has other useful tips and hints, please do comment on the blog, and I will update it!