Lorsque vous créez une application Web en Java, vous pouvez créer la classe JavaBeans suivante.
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;
}
}
Comprenez-vous ce que signifient ces ʻimplements Serializable et
serialVersionUID = 1L; `?
Cet article a été écrit pour les personnes qui disent "Je ne sais pas!" Les gens qui disent "je comprends!" N'ont pas besoin de lire cet article.
La sortie d'une instance Java sous forme de tableau d'octets s'appelle Serialize, et vice versa s'appelle Deserialize. Comme vous pouvez le voir dans la figure ci-dessous, l'enregistrement d'une instance dans un fichier, une mémoire, une base de données, etc. s'appelle Serialize.
La figure est extraite de cet article
Donc déclarer ʻimplements Serializable` signifie déclarer "Cette instance peut être sauvegardée sur le disque, etc.!".
J'ai une application web que j'ai faite un peu personnellement, donc je vais l'expérimenter.
TestServlet.java
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
//・ ・ ・
}
Le TestServlet
ci-dessus étendHttpServlet
, HttpServlet
étend GenericServlet
et GenericServlet
implémenteSerializable
, donc TestServlet
estSerializable
.
Démarrez le serveur (Tomcat) pour cette application Web. Il n'y a encore rien sous le dossier localhost de la destination de déploiement.
Après cela, ouvrez le navigateur ou faites quelque chose pour que TestServlet
fonctionne et arrêtez le serveur. Ensuite, un fichier mystérieux appelé SESSIONS.ser
sera créé directement sous le dossier localhost de la destination de déploiement.
Redémarrez le serveur. Puis «SESSIONS.ser» disparaît.
SESSIONS.ser
est une information de session sérialisée par Tomcat. Grâce à cela, les informations de session ne sont pas perdues même si le serveur est redémarré, et les informations avant le redémarrage peuvent être restaurées (désérialiser) en y accédant après le redémarrage. Inversement, pour cette spécification, les objets stockés dans la session doivent implémenter Serializable.
Cela semble pratique à première vue, mais savez-vous ce qui se passe si le programme est modifié avant et après le redémarrage?
Créez une application avec la configuration ci-dessous et faites-la sérialiser / désérialiser.
Tout d'abord, celui qui sérialise. Créez «SampleBean.java» et «WriteObject.java» pour le sérialiser dans n'importe quel dossier. WriteObject.java crée une instance SampleBean, définit deux propriétés sur 100 et la sérialise. Créez sample.ser
en exécutant WriteObject. sample.ser est une version sérialisée de l'instance SampleBean.
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();
}
}
}
Vient ensuite la personne qui désérialise.
Créez une classe (JspTest.java) qui lit le sample.ser
généré ci-dessus.
La classe SampleBean utilisée dans JspTest.java est la même que la précédente, copiée dans un autre dossier.
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 pour l'afficher
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>Propriété D</th>
<th>Propriété 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>
Résultat de l'exécution (affichage du navigateur)
J'ai pu désérialiser l'instance sérialisée et l'afficher dans le navigateur. C'est Serialize / Deserialize.
Vient ensuite serialVersionUID
. Si vous écrivez implements Serializable dans votre classe et ne déclarez pas serialVersionUID, vous obtiendrez l'avertissement La classe sérialisable XXXX ne déclare pas un champ serialVersionUID final statique de type long
.
serialVersionUID
est la version de l'instance. Je ne pense pas que ce soit très clair, alors je vais faire une petite expérience pour le comprendre plus concrètement.
Faisons le SampleBean.java utilisé lors de la sérialisation plus tôt et le serialVersionUID de SampleBean.java utilisé lors de la désérialisation de l'incompatibilité.
Remplacez SampleBean.java (celui qui désérialise) utilisé par JspTest.java par serialVersionUID = 2L;
. Le SampleBean.java sérialisé reste serialVersionUID = 1L;
.
Après avoir effectué la modification, si vous mettez à jour le navigateur avec F5, l'erreur suivante sera générée.
contenu de l'erreur
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)
J'obtiens l'erreur ʻInvalidClassException` et je sais que la cause est une discordance dans le serialVersionUID de SampleBean. Au contraire, que se passe-t-il si serialVersionUID a la même valeur mais que le contenu est différent? Ne modifiez pas le SampleBean de la personne qui sérialise, et seule la personne qui désérialise essaiera x100 dans le getter comme indiqué ci-dessous (dans getPropertyE ()).
SampleBean.java (modification uniquement du côté désérialisation)
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; //Essayez x100 dans getter
}
public void setPropertyE(int propertyE) {
this.propertyE = propertyE;
}
}
Résultat de l'exécution (affichage du navigateur)
Vous pouvez voir qu'aucune erreur ne s'est produite et la valeur x100 est affichée. Bien qu'il se soit terminé normalement, Ce comportement est-il vraiment celui que vous attendiez? </ font> Avez-vous déjà envisagé le comportement lorsque le contenu du programme est modifié entre le moment de Serialize et le moment de Deserialize? Ce que Deserialize restaure est la valeur de la variable d'instance. Au moment de la sérialisation, le type double 100 est défini pour propertyD et le type int 100 est défini pour propertyE, de sorte que Deserialize restaure cette valeur. Je me fiche de ce que je fais dans le setter / getter.
Ainsi, par exemple, même si vous vérifiez dans le setter qu'une erreur se produira si la valeur de l'argument est 10 ou plus, la valeur qui l'ignore sera restaurée.
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) {
//Erreur si la valeur de l'argument est égale ou supérieure à 10
if (propertyD > 10) {
throw new IllegalArgumentException();
}
this.propertyD = propertyD;
}
public int getPropertyE() {
return propertyE;
}
public void setPropertyE(int propertyE) {
this.propertyE = propertyE;
}
}
Résultat de l'exécution (affichage du navigateur)
S'il est de 10 ou plus dans le setter, il peut être affiché sans aucune erreur même s'il est vérifié comme erreur. Plus tôt dans cet article, j'ai mentionné que les informations de session sont sérialisées / désérialisées. Le serveur Web sérialise les informations de session dans les cas suivants.
--Lorsque le serveur est arrêté --Lorsque la zone (mémoire) de stockage de la session est pleine
Désérialiser au moment opposé. Même si le programme est modifié entre le moment où il est sérialisé et le moment où il est désérialisé, le changement sera ignoré et le programme sera désérialisé. Si cela vous dérange, vous devez changer le serialVersionUID.
Les spécifications Java pour Serialize / Deserialize sont résumées ci-dessous. 5.6 Modifications de type affectant la sérialisation Ce dont je voudrais que vous soyez particulièrement conscient est "5.6.2 Modifications compatibles", et même si les spécifications Java sont compatibles (= Désérialisation possible), elles sont compatibles avec le système que vous utilisez réellement. Qu'il y ait ou non une autre histoire. Est-il vraiment acceptable de désérialiser si elle est compatible en tant que spécification Java? Si quelque chose d'étrange se produit si vous n'utilisez pas à nouveau l'écran, il peut être plus sûr de changer le serialVersionUID et de générer une erreur.
Vous pouvez le modifier vous-même, mais vous pouvez également laisser l'EDI le générer. Pour Eclipse, la procédure est la suivante.
(Si vous utilisez IntelliJ IDEA, ici)
Supprimez private static final long serialVersionUID = 1L;
de la classe. Ensuite, un avertissement apparaîtra dans la classe, alors placez le curseur dessus (voir la figure ci-dessous).
Un serialVersionUID de private static final long serialVersionUID = 2779728212248637579L;
a été généré. Cette valeur changera à mesure que la structure de la classe change. La structure d'une classe consiste à ajouter une variable d'instance, à renommer une variable, à ajouter une méthode, etc.
En ajoutant le modificateur transitoire à la variable d'instance, il peut être exclu de Serialize / Deserialize. À titre de test, ajoutez le modificateur transitoire à propertyE comme indiqué ci-dessous.
SampleBean.java
import java.io.Serializable;
public class SampleBean implements Serializable {
private static final long serialVersionUID = 4798282072280430232L;
private double propertyD;
private transient int propertyE; //Accorder transitoire
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;
}
}
Défini sur 100 lors de la sérialisation.
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();
}
}
}
Le résultat du chargement du fichier sample.ser généré ici et de son affichage sur le navigateur est le suivant.
Résultat de l'exécution (affichage du navigateur)
En ajoutant transitoire à propertyE, vous pouvez voir que la valeur de 100 définie au moment de la sérialisation n'est pas définie au moment de la désérialisation. 0 est affiché car la valeur par défaut de int est 0. À propos, si vous ajoutez transitoire, la valeur de serialVersionUID générée par l'EDI changera également.
C'est à peu près comme ça. Je ne comprends pas grand-chose, mais j'ai senti qu'il n'y avait pas beaucoup de gens autour de moi qui ont écrit ʻimplements Serializable ou
serialVersionUID = 1L après avoir bien compris ce domaine, alors je l'ai écrit. Je l'ai essayé. J'espère que le plus grand nombre de personnes possible après avoir lu cet article comprendra la signification de Serialize et écrira ʻimplements Serializable
ou serialVersionUID = 1L
.
c'est tout.
officiel
Sérialiser: sérialiser et désérialiser: désérialiser
Sérialisation et session
serialVersionUID
Gestion de session Tomcat
Recommended Posts