Spring Scopes – Decoupling code
The Scenario
I have an application
that has a lot of classes – more than 10. To simplify the issue, let’s say that
I have a system that represents a school. So I will have a school class, a room
class, teacher and all the rest, for example:
class School(){
val classes : List[Class]
def gotMoney(total: int)
}
class Class(){
val teachers: List[Teacher]
def updateTeacher(raise: int)
}
class Teacher(){
def updateTeacherSalary(raise: int)
}
So we have in place - a
school that has a list of classes that each has a list of teachers. The normal
flow is that the school gets money and wants to give it to each teacher. So we
have a method to pass the information from the School to the Class and then to
each teacher.
Let’s say that I have
a web application that calls the system, and we want to add a profile parameter
that is sent during run time so that we can filter which teacher’s get a raise
and which don’t.
How do we pass this
new parameter from the Web API to the teacher class?
The Solution
Strait forward
A classic scenario is
to add a new parameter “profile” to all the methods that need the profile. The
limitation of this way is that we might want to add more than one parameter. In
addition this is a legacy application that has a lot of spaghetti, so this
would mean adding the parameter to a lot of methods that don’t really need to
know about this.
What we really need is
a way for a bunch of parameters to be entered in the Web API level and then be
available to other places of code.
Scope Solution
Assumptions
The current assumption
is that we have spring in place and that most of the service classes are spring
beans.
So the actual solution
is to create a session bean with scope prototype so that we have a new bean each time that we need
it. The bean is created on the Web API level and populated with the values,
then in the lower level API the services that needs the new parameters will
request the same prototype bean and get the values.
By using the prototype
bean we also insure that are application can support multithreading and keep
multiple instances of each session.
Standard web solution
If each bean will
inject a standard prototype bean then each inject will get a new bean and the
values will not persist. What we want is create only one bean per http request.
For this spring invented a bean with scope of HttpRequest (http://www.tutorialspoint.com/spring/spring_bean_scopes.html).
So what happens is that each time you request this bean spring will check if
for this specific http request a bean has already been created. If yes you get
the reference and if no you will get a new one.
For this to work your
application must be a web-aware Spring Application Context. This is so that spring
will hook into the http requests and know when to create a new instance.
But what if you want
to use this same mechanism but you don’t have a web-aware context? If you did
not have spring, the obvious solution would be to store the data you need on the
local thread and then you can have access to it from any other class.
So spring actually
supports another type of scope that is not in the standard documentation –
thread.
Thread Scope
To definite your bean,
you will use the standard definition of:
<bean id="sessionData" class="com.company.SessionData"
scope="thread"/>
Spring has a class that implements the thread scope which is: SimpleThreadScope.
It is a simple implementation with the limitation that it does not support a
callback for the destruction of the bean, since it does not know when the
thread is finished.
Since this is an unreleased class it is not supported by default and to
use it you must register it with spring like any other custom spring bean:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread"> <bean class="org.springframework.context.support.SimpleThreadScope"/> </entry> </map> </property> </bean>
.
Summary
When you need to
change a lot of code due to API changes, you need to think out of the box and
find a solution to magically pass the parameters from one section of the code
to another. The solution to this is to isolate the parameters in a spring bean
using the proper scope of the bean. You need to read up on spring scopes to
find the specific scope that you need. Of course you always have the option to
implement your own scope