Saturday, 22 April 2017

Serialization Proxy Pattern in Java

When you serialize an object in Java it is converted to byte stream and object is reconstituted using that byte stream during the process of deserialization.

Sometimes this extraneous behavior of creating object using the byte stream is not what you want and you still want constructor (or any other method if required) to be called when your object is created during the process of deserialization.

Serialization proxy pattern

Serialization proxy pattern is a way to design your class where proxy pattern defines its serialization mechanism.

Before going into any more details about serialization proxy pattern let’s know about two methods.

  • writeReplace()
  • readResolve()

writeReplace() method

Serializable classes that use an alternative object (proxy object) when writing an object to the stream should implement writeReplace() method with the exact signature:

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

This writeReplace() method is invoked by serialization if the method exists and this method is defined within the original class whose object is serialized. Thus, the method can have private, protected and package-private access modifier.

readResolve() method

Classes that need to provide a replacement object when the serialized object is read from the stream should implement readResolve() method with the exact signature.

 ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

This readResolve() method follows the same invocation rules and accessibility rules as writeReplace.

How Serialization Proxy pattern works

Rather than serializing the original class you provide functionality using the writeReplace() method to serialize the proxy class instead. Here note that writeReplace() method is implemented in the original class.

At the time of deserialization proxy object is deserialized and then the readResolve() method is called. That’s where you will have to provide the functionality to create the original class object regular way. Here note that readResolve() method is implemented in the proxy class.

Proxy class

Generally serialization proxy pattern is implemented by creating a proxy class as a nested static class with in the original class. Since it needs access to the fields of the outer class so that it is better to create proxy class as a nested class.

Example code

Time is right to see an example of the serialization proxy pattern. Here we have a Person class which has a constructor with args. When a Person class object is created it is initialized using this constructor and that’s what you want to do when you deseliarize a serialized Person class object.

For doing that you will use Serialization proxy pattern and create a proxy class (called PersonProxy here) as a nested static class. You will also implement writeReplace() and readResolve() methods.

Person class

import java.io.Serializable;

public class Person implements Serializable{
 
 /**
  * 
  */
 private static final long serialVersionUID = 9140203997753929147L;
 private String name;
 private int id;
 private int age;
 
 // Constructor
 Person(String name, int id, int age){
   System.out.println("In Constructor with args");
   this.name = name;
   this.id = id;
   this.age = age; 
 }
 // no-arg Constructor
 Person(){
   System.out.println("no-arg constructor");
 }
 
 public String getName() {
  return name;
 }
  
 
 public int getAge() {
  return age;
 }
 public int getId() {
  return id;
 }
 /**
  * writeReplace method for the proxy pattern
  * @return
  */
 private Object writeReplace() {
  System.out.println("In writeReplace() method");
  return new PersonProxy(this);
 }
 // Nested static class - Proxy
 private static class PersonProxy implements Serializable {
  /**
   * 
   */
  private static final long serialVersionUID = -5965328891170223339L;
  private String name;
  private int id;
  private int age;
  PersonProxy(Person p) {
   this.name = p.name;
   this.id = p.id;
   this.age = p.age;
  }
  // readResolve method for Person.PersonProxy
  private Object readResolve() {
   System.out.println("In readResolve() method");
   return new Person(name, id, age); // Uses public constructor
  } 
 }
}

Util class

A util class with methods to serialize and deserialize.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Util {
  /**
   * Method used for serialization
   * @param obj
   * @param fileName
   */
  public static void serialzeObject(Object obj, String fileName){
   try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)))){
    oos.writeObject(obj);
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
  
  /**
   * Method used for deserializing
   * @param fileName
   * @return
   * @throws ClassNotFoundException
   */
  public static Object deSerialzeObject(String fileName) throws ClassNotFoundException{
   Object obj = null;
   try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)))){
    obj = ois.readObject();
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   return obj;
  }
}

SerializationDemo class

Using this class a Person class object will be serialized and later deserialized.

public class SerializationDemo {

 public static void main(String[] args) {
  // Creating and initializaing a Person object
    Person person = new Person("User1", 1, 22);
    // file name
    final String fileName = "F://person.ser";
    System.out.println("About to serialize ....");
    // serializing
    Util.serialzeObject(person, fileName);
    
    try {
     System.out.println("About to deserialize ....");
     // deserializing
     person = (Person)Util.deSerialzeObject(fileName);
     System.out.println("id " + person.getId() + " Name "+ person.getName() 
       + " Age " + person.getAge());
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
 }
}

Output

In Constructor with args
About to serialize ....
In writeReplace() method
About to deserialize ....
In readResolve() method
In Constructor with args
id 1 Name User1 Age 22

Here you can see that writeReplace() method is called when object is serialized. At the time of deserializing the object readResolve() method is called where object is created and initialized using the constructor of the class not just recreated using the byte stream.

Creating instance by getting data from DB

Another example, which you will see in many frameworks too is when you want your instance to be created using a DB call. In that case what you need to serialize is some identifier only and during deserialization using that identifier you will get the data to construct the object from DB.

Example Code

Here you have a Person class with fields as id, name etc. In writeReplace() method where you serialize the proxy you provide the id also.

Person class

public class Person implements Serializable {
     private int id;
 private String name;
 …
 private Object writeReplace() {
  return new PersonProxy(id);
 }
}

Proxy Class

In the readResolve() method of the proxy class you create the Person class instance using the id which you saved earlier.

public class PersonProxy implements Serializable {
 private int id;
 public PersonProxy(int id) {
  this.id = id;
 }
 public Object readResolve() {
  // DB call to get the person record by id
  return PersonDAO.findById(id);
 }
}

That's all for this topic Serialization Proxy pattern in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. serialVersionUID and versioning in Java Serialization
  2. Serialization in Java
  3. Externalizable interface in Java
  4. Transient in Java
  5. Insert\Update using NamedParameterJDBCTemplate in Spring framework
  6. Select query using NamedParameterJDBCTemplate in Spring framework

You may also like -

>>>Go to Java advance topics page

No comments:

Post a Comment