views:

263

answers:

3

Spring has many different ways of creating beans, but is it possible to create a bean by deserializing a resource?

My application has a number of Components, and each manipulates a certain type of data. During test, the data object is instantiated directly and set directly on the component, e.g. component.setData(someDataObject). At runtime, the data is available as a serialized object and read in from the serialized stream by the component (the stream is passed in as a Resource from the Spring context.)

Rather than having each component explicitly deserialize it's data from the stream, it would be more consistent and flexible to have Spring deserialize the data object from a resource and set it on the target component. That way, components are isolated from where their data is coming from.

Is there a DeserializerFactoryBean or something similar?

EDIT:

Here's some sample code to hopefully clarify:

public class ComponentData implements Externalizable
{
   // a complex data structure, behind a simpler interface       
}

public class Component
{
   private ComponenttData  componentData;

   public Component(ComponentData data)
   {
      componentData = data;
   }

   // other methods that operate using the data

   // factory method to create from serialized data
   static Component createFromResource(Resource resource)
   {
       return new Component(deserialize(resource));
   }
}

There are many types of component, and each component type is instantated several times with different data instances.

In test, Components and their ComponentData instnaces are constructed in code. In production, a spring-bean with a "factory-method" attribute is used to invoke the static Componnet.createFromResource method, which deserializes the data from the resource. There are many types of components, and each one has the same static method to construct from deserialized data. This in itself seems bad because of the repetition. It also seems odd to me that Component construction in test and production are not the same. If the deserialization can be moved into the Spring context, the static methods on Components can be removed, and all dependenty injection is then done by Spring rather than having to code this as a special case.

E.g. I imagine something like

<bean name="componentData" class="DeserializingFactoryBean">
    <constructor-arg value="/data/componentData1.dat"/> <!--  resource -->
</bean>

<bean name="component" class="Component">
    <constructor-arg ref="componentData"/>
</bean>

When I originally posted, I thought that this might exist, but I that I might have missed in in the vast spring javadocs. It seems from the initial responses Spring does not have a deserializing factory bean.

If a DeserializingFactoryBean is not the right approach, what alternatives exist?

A: 

Is there a DeserializerBeanFactory or something similar?

Never heard of one, and frankly I wouldn't want my initialization to be controlled by opaque (to mere humans) serialized objects.

But if you really think this is a good idea (ahem), it should not be difficult to create a custom BeanFactory that worked that way.

Stephen C
The serialized classes are a complex blob of linguistic data generated by an involved and lengthy process. I realize serialization can be fragile, however, in this case the code and the data will always be consistent. The data could be represented as XML, or some other transparent (to humans) format, but I don't see that I gain anything by doing that- the raw data is not intended for human eyes! The size would increase considerably.Serialization has valid uses, and so it seems to make sense to be able to tie this into spring as a source for beans.
mdma
@mdma - yours is a highly unusual use-case. And anyway, a custom BeanFactory should do the job for you.
Stephen C
A: 

Yes you can implement your own Beanfactory like done for SimpleJndiBeanFactory. But I am not sure, that is what you want. You need additional beans on a application context. I would implement a BeanFactoryPostProcessor loading the beans from the stream and autowire or register them.

Arne Burmeister
Thanks for the input, Arne. Using a BeanFactoryPostProcessor could work quite nicely in specific cases (e.g. configure each bean from resources based on the bean name - the BeanFactoryPostProcessor is configured with a mapping from bean names to resources, similar to mappings in Spring MVC), it does involve a fair bit of wiring and convention (e.g. which setter is used to set the data object.) I prefer a simple FactoryBean implementation, configured with a resource to deserialize is simple, versatile and testable, and would allow beans that uses the data to be configured in the usual way.
mdma
A: 

Thats to the other posters for their comments. As they noted, there there is no deserialization factory support in Spring, so I created one.

Below is code for a FactoryBean that creates beans via deserialization from a resource. By default, one instance is created, and the same instance returned for each use. You can set the property singleton to false, and a fresh instance will be deserialized with each use.

It is used like this

<bean name="myBean" class="DeserializingFactoryBean">
    <property name="source" value="mybean.ser"/>
    <property name="objectType" value="org.acme.MyBean"/>
</bean>

And the code

import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

import java.io.BufferedInputStream;
import java.io.ObjectInputStream;

public class DeserializingFactoryBean extends AbstractFactoryBean
{
    private Resource    source;

    private Class<?>    objectType;

    private int         deserializationCount;

    public DeserializingFactoryBean()
    {
    }

    public DeserializingFactoryBean(Resource source, Class<?> objectType)
    {
        this.source = source;
        this.objectType = objectType;
    }

    public void afterPropertiesSet() throws Exception
    {
        Assert.notNull(objectType, "Property 'objectType' may not be null");
        Assert.notNull(source, "Property 'source' may not be null");
        super.afterPropertiesSet();
    }

    public Resource getSource()
    {
        return source;
    }

    public void setSource(Resource source)
    {
        this.source = source;
    }

    public void setObjectType(Class<?> objectType)
    {
        this.objectType = objectType;
    }

    public Class getObjectType()
    {
        return objectType;
    }

    @Override
    protected Object createInstance() throws Exception
    {
        ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(source.getInputStream()));
        Object result = oin.readObject();
        if (!objectType.isInstance(result))
            throw new ClassCastException(String.format("Deserialized object is not an instance of %s",objectType.getName()));
        oin.close();
        return result;
    }
}
mdma