Java Object Serialization RMI

Source: Internet
Author: User
Tags object serialization

For an object that exists in a Java virtual machine, its internal state remains in memory only. After the JVM has stopped, these states are lost. In many cases, the internal state of an object needs to be persisted. When it comes to persistence, the most straightforward approach is to save it to a file system or database. This approach typically involves custom storage formats and cumbersome data transformations. Object Relational Mapping (object-relational mapping) is a typical way of persisting objects with relational databases, and there are many object databases that store objects directly. Object serialization is an object persistence method built into the Java language that makes it easy to convert between active objects and byte arrays (streams) within the JVM. In addition to the simple implementation of persistence, another important use of the serialization mechanism is to shield the developer from the underlying implementation details in a remote method invocation.

Basic object serialization

Because Java provides good default support, it is relatively simple to implement basic object serialization. The Java class to be serialized only needs to implement the serializable interface. Serializable is only a markup interface and does not contain any specific methods that need to be implemented. The interface is implemented only to declare that the object of the Java class can be serialized. The actual serialization and deserialization work is done through Objectouputstream and ObjectInputStream. The WriteObject method of ObjectOutputStream can write a Java object to the stream, and ObjectInputStream's ReadObject method can read a Java object from the stream. While writing and reading, the parameter or return value used is a single object, but is actually manipulated by an object graph, including other objects referenced by the object, and other objects referenced by those objects. Java will automatically help you iterate through the object graph and serialize it individually. In addition to objects, basic types and arrays in Java can also be serialized by ObjectOutputStream and ObjectInputStream.

1 Try {2User User =NewUser ("Alex", "Cheng");3ObjectOutputStream output =NewObjectOutputStream (NewFileOutputStream ("User.bin"));4 output.writeobject (user);5 output.close ();6}Catch(IOException e) {7 e.printstacktrace ();8 }9  Ten Try { OneObjectInputStream input =NewObjectInputStream (NewFileInputStream ("User.bin")); AUser User =(User) input.readobject (); - System.out.println (user); -}Catch(Exception e) { the e.printstacktrace (); -}

The code above gives a typical basic way to save a Java object to disk and read it from disk. The user class simply declares the implementation of the serializable interface.

In the default serialization implementation, both non-static and non-instantaneous fields in the Java object are included, and there is no relationship to the visibility declaration of the domain. This may cause certain fields that should not appear to be included in the serialized byte array, such as private information such as passwords. Since the formatting of the Java object after serialization is fixed, it is easy for others to parse the various information from it. One solution to this situation is to declare the domain as instantaneous, that is, to use the transient keyword. Another way is to add a serialpersistentfields? Domain to declare the domain to include when serializing. From here you can see this contract that is defined only on a written level in the Java serialization mechanism. A domain that declares serialization must use a fixed name and type. Other contracts like this can be seen later. Although serializable is just a markup interface, it actually contains a number of implicit requirements. The following code gives an example of the Serialpersistentfields declaration, where only the FirstName field is to be serialized.

1 Private Static Final objectstreamfield[] Serialpersistentfields =2     new Objectstreamfield ("FirstName", String. class  3 };  

Custom Object serialization

The basic object serialization mechanism allows developers to customize which domains are included. If you want finer-grained control over the serialized process, you need to add writeobject and the corresponding ReadObject method to the class. These two methods are part of the implicit contract of the previously mentioned serialization mechanism. When writing to an object through the WriteObject method of ObjectOutputStream, if the WriteObject method is defined in the class of the object, the method is called and the current ObjectOutputStream object is passed in as a parameter. The WriteObject method typically contains custom serialization logic, such as modifying the value of a field before writing, or writing additional data. For the logic added in WriteObject, it needs to be reversed in the corresponding readobject, corresponding to it.

Before adding your own logic, it is recommended that you call the Java default implementation first. In the WriteObject method through the objectoutputstream of the Defaultwriteobject to complete, The ReadObject method is implemented by ObjectInputStream's defaultreadobject. The following code writes an extra string in the serialized stream of the object.

1 Private voidWriteObject (ObjectOutputStream output)throwsIOException {2 Output.defaultwriteobject ();3Output.writeutf ("Hello World");4 }5 Private voidReadObject (ObjectInputStream input)throwsIOException, ClassNotFoundException {6 Input.defaultreadobject ();7String value =Input.readutf ();8 System.out.println (value);9}

Object substitution at serialization

In some cases, you might want to use another object in place of the current object when serializing. The motivation may be that the current object contains some fields that you do not want to serialize, such as those that derive from another domain, or that you want to hide the actual class hierarchy, and perhaps add custom object management logic, such as guaranteeing that a class has only one instance in the JVM. The use of object substitution is a better option than setting the unrelated domain to transient, providing more flexibility. The substitution object acts like a transport object (Transfer object) that is used in Java EE.

Consider the following example, where an order system needs to serialize the relevant information of an order and transmit it over the network. Order class orders refers to Customer class customers. In the case of the default serialization, when the Order class object is serialized, the referenced customer class object is also serialized, which may result in the disclosure of user information. In this case, you can create an additional object to replace the current order class object at serialization time and hide the user information.

1 Private Static classOrderreplaceImplementsSerializable {2     Private Static Final LongSerialversionuid = 4654546423735192613L;3     PrivateString orderId;4      PublicOrderreplace (Order order) {5          This. OrderId =Order.getid ();6     }7     PrivateObject Readresolve ()throwsobjectstreamexception {8         //finds the order object according to OrderID and returns9     }Ten}

This replacement object class Orderreplace only holds the ID of the order. A Orderreplace object is returned in the Writereplace method of the Order class. This object is written as an alternative to the stream. Similarly, you need to define a Readresolve method in the Orderreplace class to convert back to the Order class object at read time. This way, the presence of the replacement object is transparent to the caller.

1 Private throws objectstreamexception {2     return New Orderreplace (this); 3

Serialization and object creation

After reading an object through the ReadObject method of ObjectInputStream, the object is a new instance, but its construction method is not called, and the initialization code of the domain is not executed. For domains that are not serialized, the values in the newly created object are default. In other words, this object is not complete in some way. This has the potential to cause some implicit errors. The caller does not know whether the object was created by a generic new operator or is obtained by deserialization. The solution is to execute the required object initialization logic in the ReadObject method of the class. For a generic Java class, the constructor contains the initialization logic. You can extract these logic into a method and call this method in the ReadObject method.

Version update

After serializing a Java object, the resulting byte array is typically stored in a disk or database. After the save is complete, it is possible that the original Java class has been updated, such as adding additional domains. This time, from a compatibility point of view, requires that the old version of the serialized data be still readable. During the reading process, when ObjectInputStream discovers the definition of an object, it attempts to find its Java class definition in the current JVM. This lookup process cannot be judged solely on the full name of the Java class, because there may be Java classes with the same name but different meanings in the current JVM. This correspondence is implemented by a globally unique identifier, SERIALVERSIONUID. By defining the domain in a class that implements the Serializable interface, a unique serialized version number is declared for that Java class. The JVM determines whether two classes are compatible than the version number of the class that is derived from the byte array, and whether the version number of the class found in the JVM is the same. For developers, it is important to remember that a domain is defined in a class that implements the Serializable interface, and that the value remains the same during the version update process. Of course, if you don't want to maintain this backward compatibility, change the version number. The value of this field is typically a hash value computed by synthesizing the various attributes of the Java class, which can be generated using the Serialver command provided by Java. In Eclipse, if the Java class implements the Serializable interface, Eclipse prompts and helps you generate the Serialversionuid.

In the course of class version updates, some operations can break backward compatibility. If you want to maintain this backward compatibility, you need extra attention. In general, adding something to a new version does not create a problem, and removing some fields is not possible.

Serialization security

As mentioned earlier, the content format after the Java object serialization is public. So it's easy to extract a variety of information from it. From the perspective of implementation, the security of serialization can be enhanced from different levels.

    • Encrypts the stream after serialization. This can be achieved by CipherOutputStream.
    • Implement your own WriteObject and ReadObject methods to encrypt the value of the domain that you want to serialize before calling Defaultwriteobject.
    • Use a signedobject or sealedobject to encapsulate the current object and serialize it with Signedobject or sealedobject.
    • When deserializing from a stream, the implementation of the ObjectInputValidation interface can be added through the ObjectInputStream Registervalidation method to verify that the object obtained after deserialization is legitimate.
Rmi

RMI (remote Method invocation) is an implementation of a distributed Java application, which is implemented remotely by a procedure call (Procedure call,rpc) in Java. It is designed to shield developers from the details across different JVMs and network connections, making it easy to communicate with each other on different JVMs as if they were in a unified JVM. The introduction of RMI after the introduction of object serialization is mainly due to the fact that the object serialization mechanism makes RMI very simple. Calling a method on a remote server is not a difficult task. Developers can write their Web servers based on a framework such as Apache Mina or Netty, or they can use the rest architecture style to write HTTP services. But one of the unavoidable parts of these solutions is the orchestration and reconciliation of data (Marshal/unmarshal). There is a need to convert between Java objects and transport formats, and this part of the logic is beyond the developer's way of avoiding. The advantage of RMI is that it relies on the Java serialization mechanism to shield developers from the details of data orchestration and reconciliation, and to do very little. After JDK 5, RMI uses a dynamic proxy mechanism to remove the cumbersome ways in which earlier versions need to be code-generated from tools, making them easier to use.

RMI uses a typical client-server-side architecture. The first thing you need to define is the server side of the remote interface, this step is to design the server side need to provide what kind of services. The requirements for the remote interface are simple and only need to be inherited from the remoting interface in RMI. Remote and serializable are also labeled interfaces. Methods in the remote interface need to throw remoteexception. After you have defined the remote interface, implement the interface. As the following calculator is a simple remote interface.

 1publicInterfaceextends2 throws3 

An instance of a class that implements a remote interface is called a remote object. After you create the remote object, you need to register it in a registry. This is for the client to be able to locate the remote object and invoke it.

1  Public classCalculatorserverImplementsCalculator {2      Publicstring calculate (String expr)throwsRemoteException {3         returnexpr;4     }5      Public voidStart ()throwsRemoteException, alreadyboundexception {6Calculator stub = (Calculator) unicastremoteobject.exportobject ( This, 0);7Registry Registry =locateregistry.getregistry ();8Registry.rebind ("Calculator", stub);9     }Ten}

Calculatorserver is the Java class for remote objects. The current object is exposed by UnicastRemoteObject's exportobject in its start method, allowing it to receive call requests from the client. By registry The Rebind method to register, so that the client can be found.

The implementation of the client is to first find the implementation object of the remote interface from the registry, and then call the appropriate method. The actual invocation is done on the server side, but it appears to the client that the method in this interface is the same as in the current JVM. This is where RMI is powerful.

1  Public classcalculatorclient {2      Public voidCalculate (String expr) {3         Try {4Registry Registry = locateregistry.getregistry ("localhost");5Calculator Calculator = (Calculator) registry.lookup ("Calculator");6String result =calculator.calculate (expr);7 System.out.println (result);8}Catch(Exception e) {9 e.printstacktrace ();Ten         } One     } A}

At run time, the registry server used in RMI needs to be started with the Rmiregistry command first.

In order to transfer through the Java serialization mechanism, the parameters and return values of the methods in the remote interface are either basic Java types, either remote objects or Java classes that implement the Serializable interface. When a client finds a remote interface through the RMI registry, it is actually a dynamic proxy object for the remote interface. When the client invokes the method, the method's parameter object is transferred to the server side after serialization. After the server side is received, it is deserialized to get the parameter object. and use these parameter objects to invoke the actual method on the server side. The return value of the call the Java object is serialized and then sent back to the client. The client is then deserialized and then gets the Java object, which is returned to the caller. This intermediate serialization process is transparent to the consumer and is done automatically by the dynamic proxy object. In addition to serialization, RMI uses dynamic class loading techniques. When deserialization is required, if the object's class definition is not found in the current JVM, RMI attempts to download the required class file definition from the far end. You can specify the URL to dynamically download Java class files by using the JVM parameter java.rmi.server.codebase when the RMI program starts.

Resources
    • Specification for serialization of Java objects
    • RMI Specification

Java Object Serialization RMI

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.