Wenn Sie eine Webanwendung in Java erstellen, können Sie eine JavaBeans-Klasse wie die folgende erstellen.
JavaBeans.java
import java.io.Serializable;
public class JavaBeans implements Serializable {
private static final long serialVersionUID = 1L;
private String aaa = null;
public String getAaa() {
return aaa;
}
public void setAaa(String aaa) {
this.aaa = aaa;
}
}
Verstehst du die Bedeutung dieser "implementiert Serializable" und "serialVersionUID = 1L;"? Dieser Artikel wurde für Leute geschrieben, die sagen "Ich weiß nicht!" Leute, die sagen "Ich verstehe!", Müssen diesen Artikel nicht lesen.
Die Ausgabe einer Java-Instanz als Byte-Array wird als Serialize und umgekehrt als Deserialize bezeichnet. Wie Sie in der folgenden Abbildung sehen können, wird das Speichern einer Instanz in einer Datei, einem Speicher, einer Datenbank usw. als Serialize bezeichnet.
Die Abbildung ist ein Auszug aus diesem Artikel.
Das Deklarieren von "implementiert Serializable" bedeutet also das Deklarieren von "Diese Instanz kann auf der Festplatte usw. gespeichert werden!".
Ich habe eine Webanwendung, die ich ein wenig persönlich erstellt habe, also werde ich damit experimentieren.
TestServlet.java
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
//・ ・ ・
}
Das obige TestServlet
erweitertHttpServlet
, HttpServlet
erweitertGenericServlet
und GenericServlet
implementiertSerializable
, sodass TestServlet``Serializable
ist.
Starten Sie den Server (Tomcat) für diese Webanwendung. Im Ordner localhost des Bereitstellungsziels befindet sich noch nichts.
Öffnen Sie danach den Browser oder tun Sie etwas, damit TestServlet funktioniert, und stoppen Sie den Server. Anschließend wird eine mysteriöse Datei mit dem Namen "SESSIONS.ser" direkt unter dem Ordner "localhost" des Bereitstellungsziels erstellt.
Starten Sie den Server erneut. Dann verschwindet SESSIONS.ser
.
SESSIONS.ser
ist eine serialisierte Sitzungsinformation von Tomcat. Dank dessen gehen die Sitzungsinformationen auch beim Neustart des Servers nicht verloren, und die Informationen vor dem Neustart können wiederhergestellt (deserialisiert) werden, indem nach dem Neustart darauf zugegriffen wird. Umgekehrt müssen für diese Spezifikation die in der Sitzung gespeicherten Objekte Serializable implementieren.
Auf den ersten Blick scheint es praktisch, aber wissen Sie, was passiert, wenn das Programm vor und nach dem Neustart geändert wird?
Erstellen Sie eine Anwendung mit der unten gezeigten Konfiguration und lassen Sie sie tatsächlich serialisieren / deserialisieren.
Zuallererst derjenige, der serialisiert. Erstellen Sie "SampleBean.java" und "WriteObject.java", um es in einem beliebigen Ordner zu serialisieren. WriteObject.java erstellt eine SampleBean-Instanz, setzt zwei Eigenschaften auf 100 und serialisiert sie. Erstellen Sie "sample.ser", indem Sie WriteObject ausführen. sample.ser ist eine serialisierte Version der SampleBean-Instanz.
SampleBean.java
import java.io.Serializable;
public class SampleBean implements Serializable {
private static final long serialVersionUID = 1L;
private double propertyD;
private int propertyE;
public double getPropertyD() {
return propertyD;
}
public void setPropertyD(double propertyD) {
this.propertyD = propertyD;
}
public int getPropertyE() {
return propertyE;
}
public void setPropertyE(int propertyE) {
this.propertyE = propertyE;
}
}
WriteObject.java
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class WriteObject {
public static void main (String args[]) {
SampleBean sample = new SampleBean();
sample.setPropertyD(100);
sample.setPropertyE(100);
try {
FileOutputStream fout = new FileOutputStream("sample.ser");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(sample);
oos.close();
System.out.println("Done");
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
Als nächstes kommt die Person, die deserialisiert.
Erstellen Sie eine Klasse (JspTest.java), die den oben generierten sample.ser
liest.
Die in JspTest.java verwendete SampleBean-Klasse ist dieselbe wie die vorherige, die in einen anderen Ordner kopiert wurde.
JspTest.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/JspTest")
public class JspTest extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SampleBean sample = null;
List<SampleBean> list = new ArrayList<>();
try {
FileInputStream fin = new FileInputStream("c:\\sample.ser");
ObjectInputStream ois = new ObjectInputStream(fin);
sample = (SampleBean) ois.readObject();
ois.close();
list.add(sample);
} catch(Exception ex) {
ex.printStackTrace();
}
request.setAttribute("samplelist", list);
String path = "/WEB-INF/jsp/jsptest.jsp";
RequestDispatcher disp = request.getRequestDispatcher(path);
disp.forward(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Jsp, um es anzuzeigen
jsptest.jsp
<%@ page language="java" contentType="text/html;charset=Windows-31J"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page isELIgnored="false" %>
<html><body>
<table border="1">
<thead>
<tr>
<th>Eigenschaft D.</th>
<th>Eigenschaft E.</th>
</tr>
</thead>
<tbody>
<c:forEach items="${samplelist}" var="item">
<tr>
<td><c:out value="${item.propertyD}" /></td>
<td><c:out value="${item.propertyE}" /></td>
</tr>
</c:forEach>
<tbody>
</table>
</body></html>
Ausführungsergebnis (Browseranzeige)
Ich konnte die serialisierte Instanz deserialisieren und im Browser anzeigen. Dies ist Serialize / Deserialize.
Als nächstes kommt serialVersionUID
. Wenn Sie Implementierungen Serializable in Ihre Klasse schreiben und keine serialVersionUID deklarieren, wird die Warnung "Die serialisierbare Klasse XXXX deklariert kein statisches endgültiges serialVersionUID-Feld vom Typ long" angezeigt.
serialVersionUID
ist die Version der Instanz. Ich denke nicht, dass es sehr klar ist, also werde ich ein kleines Experiment machen, um es konkreter zu verstehen.
Lassen Sie uns die SampleBean.java, die beim früheren Serialisieren verwendet wird, und die serialVersionUID von SampleBean.java, die beim Deserialisieren verwendet wird, nicht übereinstimmen.
Ändern Sie SampleBean.java (dasjenige, das deserialisiert), das von JspTest.java verwendet wird, in "serialVersionUID = 2L;". Die serialisierte SampleBean.java bleibt "serialVersionUID = 1L;".
Wenn Sie nach der Änderung den Browser mit F5 aktualisieren, wird der folgende Fehler ausgegeben.
Fehlerinhalt
java.io.InvalidClassException: SampleBean; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:715)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1997)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1866)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2159)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1685)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:499)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:457)
at JspTest.doGet(JspTest.java:27)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:832)
Ich erhalte den Fehler "InvalidClassException" und weiß, dass die Ursache eine Nichtübereinstimmung in der serialVersionUID der SampleBean ist. Was passiert im Gegenteil, wenn die serialVersionUID den gleichen Wert hat, der Inhalt jedoch unterschiedlich ist? Ändern Sie nicht die SampleBean der Person, die serialisiert, und nur die Person, die deserialisiert, versucht x100 im Getter wie unten gezeigt (in getPropertyE ()).
SampleBean.Java (nur auf der Deserialize-Seite ändern)
import java.io.Serializable;
public class SampleBean implements Serializable {
private static final long serialVersionUID = 1L;
private double propertyD;
private int propertyE;
public double getPropertyD() {
return propertyD;
}
public void setPropertyD(double propertyD) {
this.propertyD = propertyD;
}
public int getPropertyE() {
return propertyE * 100; //Versuchen Sie x100 in Getter
}
public void setPropertyE(int propertyE) {
this.propertyE = propertyE;
}
}
Ausführungsergebnis (Browseranzeige)
Sie können sehen, dass kein Fehler aufgetreten ist und der Wert x100 angezeigt wird. Obwohl es normal endete, Ist dieses Verhalten wirklich das, was Sie erwartet haben? </ font> Haben Sie jemals über das Verhalten nachgedacht, wenn der Inhalt des Programms zwischen dem Zeitpunkt der Serialisierung und dem Zeitpunkt der Deserialisierung geändert wird? Was Deserialize wiederherstellt, ist der Wert der Instanzvariablen. Zum Zeitpunkt der Serialisierung wird für propertyD der Doppeltyp 100 und für propertyE der Int-Typ 100 festgelegt, sodass Deserialize diesen Wert wiederherstellt. Es ist mir egal, was ich in Setter / Getter mache.
Selbst wenn Sie beispielsweise im Setter einchecken, dass ein Fehler auftritt, wenn der Argumentwert 10 oder mehr beträgt, wird der Wert, der ihn ignoriert, wiederhergestellt.
SampleBean.java
import java.io.Serializable;
public class SampleBean implements Serializable {
private static final long serialVersionUID = 1L;
private double propertyD;
private int propertyE;
public double getPropertyD() {
return propertyD;
}
public void setPropertyD(double propertyD) {
//Fehler, wenn der Argumentwert 10 oder mehr beträgt
if (propertyD > 10) {
throw new IllegalArgumentException();
}
this.propertyD = propertyD;
}
public int getPropertyE() {
return propertyE;
}
public void setPropertyE(int propertyE) {
this.propertyE = propertyE;
}
}
Ausführungsergebnis (Browseranzeige)
Wenn der Setter 10 oder mehr beträgt, kann er fehlerfrei angezeigt werden, obwohl er als Fehler markiert ist. Zu Beginn dieses Artikels habe ich erwähnt, dass Sitzungsinformationen serialisiert / deserialisiert sind. Der Webserver serialisiert die Sitzungsinformationen in den folgenden Fällen.
--Wenn der Server gestoppt ist --Wenn der Bereich (Speicher) zum Speichern der Sitzung voll ist
Zum entgegengesetzten Zeitpunkt deserialisieren. Selbst wenn das Programm zwischen dem Zeitpunkt der Serialisierung und dem Zeitpunkt der Deserialisierung geändert wird, wird die Änderung ignoriert und das Programm wird deserialisiert. Wenn Sie das stört, müssen Sie die serialVersionUID ändern.
Die Java-Spezifikationen für Serialize / Deserialize sind unten zusammengefasst. 5.6 Typänderungen, die sich auf die Serialisierung auswirken Ich möchte, dass Sie besonders auf "5.6.2 Kompatible Änderungen" aufmerksam werden. Auch wenn die Java-Spezifikationen kompatibel sind (= Deserialisieren möglich), sind sie mit dem System kompatibel, das Sie tatsächlich verwenden. Ob es eine andere Geschichte gibt oder nicht. Ist es wirklich in Ordnung zu deserialisieren, wenn es als Java-Spezifikation kompatibel ist? Wenn etwas Seltsames passiert, wenn Sie den Bildschirm nicht erneut bedienen, ist es möglicherweise sicherer, die serialVersionUID zu ändern und einen Fehler zu generieren.
Sie können es selbst ändern, aber Sie können es auch von der IDE generieren lassen. Für Eclipse ist die Vorgehensweise wie folgt. (Wenn Sie IntelliJ IDEA verwenden, hier) Entfernen Sie "private static final long serialVersionUID = 1L;" aus der Klasse. Dann erscheint eine Warnung in der Klasse. Setzen Sie den Cursor darauf (siehe Abbildung unten).
Eine serialVersionUID von "private static final long serialVersionUID = 2779728212248637579L;" wurde generiert. Dieser Wert ändert sich, wenn sich die Struktur der Klasse ändert. Die Struktur einer Klasse besteht darin, eine Instanzvariable hinzuzufügen, eine Variable umzubenennen, eine Methode hinzuzufügen usw.
Durch Hinzufügen des Transientenmodifikators zur Instanzvariablen kann er von Serialize / Deserialize ausgeschlossen werden. Fügen Sie als Test den Transientenmodifikator wie unten gezeigt zu propertyE hinzu.
SampleBean.java
import java.io.Serializable;
public class SampleBean implements Serializable {
private static final long serialVersionUID = 4798282072280430232L;
private double propertyD;
private transient int propertyE; //Transient gewähren
public double getPropertyD() {
return propertyD;
}
public void setPropertyD(double propertyD) {
this.propertyD = propertyD;
}
public int getPropertyE() {
return propertyE;
}
public void setPropertyE(int propertyE) {
this.propertyE = propertyE;
}
}
Beim Serialisieren auf 100 setzen.
WriteObject.java
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class WriteObject {
public static void main (String args[]) {
SampleBean sample = new SampleBean();
sample.setPropertyD(100);
sample.setPropertyE(100);
try {
FileOutputStream fout = new FileOutputStream("sample.ser");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(sample);
oos.close();
System.out.println("Done");
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
Das Ergebnis des Ladens der hier generierten sample.ser und ihrer Anzeige im Browser ist wie folgt.
Ausführungsergebnis (Browseranzeige)
Durch Hinzufügen von Transient zu propertyE können Sie sehen, dass der zum Zeitpunkt der Serialisierung festgelegte Wert 100 zum Zeitpunkt der Deserialisierung nicht festgelegt ist. 0 wird angezeigt, da der Standardwert von int 0 ist. Wenn Sie transient hinzufügen, ändert sich übrigens auch der Wert der von der IDE generierten serialVersionUID.
Es ist ungefähr so. Es gibt einen Teil, den ich nicht gut verstehe, aber ich hatte das Gefühl, dass es nicht viele Leute um mich gibt, die "implementiert Serializable" oder "serialVersionUID = 1L" schreiben, nachdem sie diesen Bereich richtig verstanden haben, also habe ich ihn geschrieben. Ich versuchte es. Ich hoffe, dass so viele Menschen wie möglich nach dem Lesen dieses Artikels die Bedeutung von Serialize verstehen und "implementiert Serializable" oder "serialVersionUID = 1L" schreiben.
das ist alles.
offiziell
Serialisieren: Serialisieren und Deserialisieren: Deserialisieren
Serialisieren und Sitzung
serialVersionUID
Tomcat-Sitzungsverwaltung
Recommended Posts