I have a service class which is not serializable and a bean which must be serializable but must have access to this service class:
class SomeBean implements Serializable
{
private StuffFactory factory;
@Autowired
public SomeBean(StuffFactory factory)
{
this.factory = factory;
}
public getOther()
{
return this.factory.getSomeOtherStuff();
}
}
This obviously doesn't work because now the SomeBean
class is no longer serializable. What is the correct way to solve this in Spring? When I make the factory
field transient then I loose the injected factory instance on deserialization, or not? And when I make the StuffFactory
also serializable then this class will no longer be a singleton because each SomeBean
instance will have it's own factory after deserialization then.
You need some kind of context for this magic to work.
One ugly way I can think of is a static ThreadLocal
holding the ApplicationContext
- it's how the spring-security works using SecurityContextHolder
.
But the best if You'd be able to externalize the logic that requires the StuffFactory
to some kind of singleton SomeBeanService
, i.e.:
public class SomeBeanService {
@Autowired
private StuffFactory stuffFactory;
public void doWithSomeBean(SomeBean bean) {
// do the stuff using stuffFactory here
}
}
Update:
The whole point in above alternative to ThreadLocal
is to get rid of the dependency on StuffFactory
from SomeBean
completely. This should be possible, but will require changes in architecture. The separation of concerns (one of, not only Spring, basic rules) implies that it might be a good idea to let SomeBean
be a simple data transfer object and the business logic to be moved to service layer.
If You'll be unable to achieve this, than the only way is using some kind of static
context (as Ralph also said). The implementation of such context may involve using a ThreadLocal
. This would allow to access the ApplicationContext
to get the StuffFactory
, but it's almost as ugly as global variables, so avoid it if possible.
Update2:
I just saw Your comment, that SomeBean
is stored in HTTP session, and hence the serialization/deserialization issue. Now even more I advice to change Your design and remove the dependency. Make SomeBean
a simple DTO, as small as possible to avoid overloaded sessions. There should be no logic requiring access to singleton Spring beans inside the SomeBean
. Such logic should be placed in the controller or service layer.
Java provide a way to control the serialization and deserialization by implementing the two methods:
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
So you can change the write to store the bean without the reference to the service and later one while reading the object "inject" the reference again.
@see http://java.sun.com/developer/technicalArticles/Programming/serialization/
You MUST create the instance in readObject
as bean. If you only use a simple new
it will not become a bean.
Therefore you need access to the Spring Context, for example via a static method.
May the best way is to use a bean factory to create the new instance.
An other way you can try to make the instance a spring bean is using @Configurable
but that requires real AspectJ.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With