关于 java.io.ObjectInputStream 内部类的部分笔记,里面涉及到了HandleTable、ValidationList、GetField及其实现等几个内部类的声明和实现。本文演示代码段的执行环境基于JDK版本1.7 。
概述 HandleTable是一个非同步表,用来跟踪对象映射的连接句柄,以及反序列对象过程中发生的ClassNotFoundException异常。这个类实现了一个异常传播算法来决定哪些对象应该被认为是属于异常ClassNotFoundException的。
关于该表的使用场景,大致可分为如下几种:在反序列过程中,一个给定对象通过assign方法第一次被分配了一个句柄,此时该句柄属于“开放”状态。其中对其他句柄的异常状态的依赖关系可以通过调用markDependency方法注册,或者通过调用markException直接与句柄关联一个异常。如果当前句柄被指向了一个异常,那么根据异常传播机制和当前句柄有关联的其他对象都会被标记为异常态。一旦所有的异常信息/依赖都被注册了,那么当前句柄就可以通过close()被“关闭”了。通过finish()方法允许异常传播算法删除相关的依赖链,减少性能和内存损耗。
ValidationList是一个优先级队列集合,用来存储在对象引用图谱被完全反序列化恢复之后需要执行的回调方法。
GetFieldImpl实现了GetField(ObjectInputStream的静态抽象类)声明的所有方法。提供了从输入流中读取字段数据内容的方法。
HandleTable Constructor Summary HandleTable(int initialCapacity) 1 2 3 4 5 HandleTable(int initialCapacity) { status = new byte [initialCapacity]; entries = new Object[initialCapacity]; deps = new HandleList[initialCapacity]; }
初始化一个句柄表。status维护了句柄->对象状态信息,entries维护了句柄->对象/异常实体信息(该过程依赖于status),deps维护了句柄->依赖的句柄列表信息。
部分方法 int assign(Object obj) 1 2 3 4 5 6 7 8 int assign (Object obj) { if (size >= entries.length) { grow(); } status[size] = STATUS_UNKNOWN; entries[size] = obj; return size++; }
分配下一个可用的句柄给入参对象obj,并返回分配的句柄信息。一旦对象彻底完成了反序列操作,句柄应该通过finish()方法置为“结束”状态。
如果数组空间不够,就执行扩容处理。
private void grow() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private void grow () { int newCapacity = (entries.length << 1 ) + 1 ; byte [] newStatus = new byte [newCapacity]; Object[] newEntries = new Object[newCapacity]; HandleList[] newDeps = new HandleList[newCapacity]; System.arraycopy(status, 0 , newStatus, 0 , size); System.arraycopy(entries, 0 , newEntries, 0 , size); System.arraycopy(deps, 0 , newDeps, 0 , size); status = newStatus; entries = newEntries; deps = newDeps; }
按照当前数组容量的两倍进行扩容处理。
void markDependency(int dependent, int target) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void markDependency (int dependent, int target) { if (dependent == NULL_HANDLE || target == NULL_HANDLE) { return ; } switch (status[dependent]) { case STATUS_UNKNOWN: switch (status[target]) { case STATUS_OK: break ; case STATUS_EXCEPTION: markException(dependent, (ClassNotFoundException) entries[target]); break ; case STATUS_UNKNOWN: if (deps[target] == null ) { deps[target] = new HandleList(); } deps[target].add(dependent); if (lowDep < 0 || lowDep > target) { lowDep = target; } break ; default : throw new InternalError(); } break ; case STATUS_EXCEPTION: break ; default : throw new InternalError(); } }
完成句柄依赖绑定。dependent句柄必须是处于“开启”状态的句柄,如果dependent句柄或target句柄都为空,那么直接返回不做处理。
如果dependent句柄处于“开启”状态(status = 2),那么检查target句柄。如果target的状态为OK,不做处理,如果状态为STATUS_EXCEPTION,那么需要将target句柄指向的异常传递给dependent句柄,调用的是markException(int handle, ClassNotFoundException ex) 方法。如果状态为STATUS_UNKNOWN,那么就把dependent指向给target。
void markException(int handle, ClassNotFoundException ex) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void markException (int handle, ClassNotFoundException ex) { switch (status[handle]) { case STATUS_UNKNOWN: status[handle] = STATUS_EXCEPTION; entries[handle] = ex; HandleList dlist = deps[handle]; if (dlist != null ) { int ndeps = dlist.size(); for (int i = 0 ; i < ndeps; i++) { markException(dlist.get(i), ex); } deps[handle] = null ; } break ; case STATUS_EXCEPTION: break ; default : throw new InternalError(); } }
将ClassNotFoundException异常关联到当前活动句柄上。
void finish(int handle) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void finish (int handle) { int end; if (lowDep < 0 ) { end = handle + 1 ; } else if (lowDep >= handle) { end = size; lowDep = -1 ; } else { return ; } for (int i = handle; i < end; i++) { switch (status[i]) { case STATUS_UNKNOWN: status[i] = STATUS_OK; deps[i] = null ; break ; case STATUS_OK: case STATUS_EXCEPTION: break ; default : throw new InternalError(); } } }
将指定句柄标记为结束状态,标记完成后不会有新的依赖会指向到当前句柄上。
void setObject(int handle, Object obj) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void setObject (int handle, Object obj) { switch (status[handle]) { case STATUS_UNKNOWN: case STATUS_OK: entries[handle] = obj; break ; case STATUS_EXCEPTION: break ; default : throw new InternalError(); } }
将handle句柄指向的对象替换为obj。
Object lookupObject(int handle) 1 2 3 4 Object lookupObject (int handle) { return (handle != NULL_HANDLE && status[handle] != STATUS_EXCEPTION) ? entries[handle] : null ; }
根据handle句柄查找对象。如果存在就从entries数组中返回对应的对象,否则返回null。
ClassNotFoundException lookupException(int handle) 1 2 3 4 5 ClassNotFoundException lookupException (int handle) { return (handle != NULL_HANDLE && status[handle] == STATUS_EXCEPTION) ? (ClassNotFoundException) entries[handle] : null ; }
根据handle句柄查找对应的异常。如果存在就从entries数组中返回对应的异常,否则返回null。
void clear() 1 2 3 4 5 6 7 void clear () { Arrays.fill(status, 0 , size, (byte ) 0 ); Arrays.fill(entries, 0 , size, null ); Arrays.fill(deps, 0 , size, null ); lowDep = -1 ; size = 0 ; }
将所有内容全部清空。
int size() 1 2 3 int size () { return size; }
返回表中存储注册的句柄数量。
private static class HandleList 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private static class HandleList { private int [] list = new int [4 ]; private int size = 0 ; public HandleList () { } public void add (int handle) { if (size >= list.length) { int [] newList = new int [list.length << 1 ]; System.arraycopy(list, 0 , newList, 0 , list.length); list = newList; } list[size++] = handle; } public int get (int index) { if (index >= size) { throw new ArrayIndexOutOfBoundsException(); } return list[index]; } public int size () { return size; } }
一个内部使用的长度可增长的句柄List集合实现。在HandleTable中用来维护句柄依赖信息。
ValidationList 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 private static class ValidationList { private static class Callback { final ObjectInputValidation obj; final int priority; Callback next; final AccessControlContext acc; Callback(ObjectInputValidation obj, int priority, Callback next, AccessControlContext acc) { this .obj = obj; this .priority = priority; this .next = next; this .acc = acc; } } private Callback list; ValidationList() { } void register (ObjectInputValidation obj, int priority) throws InvalidObjectException { if (obj == null ) { throw new InvalidObjectException("null callback" ); } Callback prev = null , cur = list; while (cur != null && priority < cur.priority) { prev = cur; cur = cur.next; } AccessControlContext acc = AccessController.getContext(); if (prev != null ) { prev.next = new Callback(obj, priority, cur, acc); } else { list = new Callback(obj, priority, list, acc); } } void doCallbacks () throws InvalidObjectException { try { while (list != null ) { AccessController.doPrivileged( new PrivilegedExceptionAction<Void>() { public Void run () throws InvalidObjectException { list.obj.validateObject(); return null ; } }, list.acc); list = list.next; } } catch (PrivilegedActionException ex) { list = null ; throw (InvalidObjectException) ex.getException(); } } public void clear () { list = null ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static abstract class GetField { public abstract ObjectStreamClass getObjectStreamClass () ; public abstract boolean defaulted (String name) throws IOException ; public abstract boolean get (String name, boolean val) throws IOException ; public abstract byte get (String name, byte val) throws IOException ; public abstract char get (String name, char val) throws IOException ; public abstract short get (String name, short val) throws IOException ; public abstract int get (String name, int val) throws IOException ; public abstract long get (String name, long val) throws IOException ; public abstract float get (String name, float val) throws IOException ; public abstract double get (String name, double val) throws IOException ; public abstract Object get (String name, Object val) throws IOException ; }
GetFieldImpl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 private class GetFieldImpl extends GetField { private final ObjectStreamClass desc; private final byte [] primVals; private final Object[] objVals; private final int [] objHandles; GetFieldImpl(ObjectStreamClass desc) { this .desc = desc; primVals = new byte [desc.getPrimDataSize()]; objVals = new Object[desc.getNumObjFields()]; objHandles = new int [objVals.length]; } public ObjectStreamClass getObjectStreamClass () { return desc; } public boolean defaulted (String name) throws IOException { return (getFieldOffset(name, null ) < 0 ); } public boolean get (String name, boolean val) throws IOException { int off = getFieldOffset(name, Boolean.TYPE); return (off >= 0 ) ? Bits.getBoolean(primVals, off) : val; } public byte get (String name, byte val) throws IOException { int off = getFieldOffset(name, Byte.TYPE); return (off >= 0 ) ? primVals[off] : val; } public char get (String name, char val) throws IOException { int off = getFieldOffset(name, Character.TYPE); return (off >= 0 ) ? Bits.getChar(primVals, off) : val; } public short get (String name, short val) throws IOException { int off = getFieldOffset(name, Short.TYPE); return (off >= 0 ) ? Bits.getShort(primVals, off) : val; } public int get (String name, int val) throws IOException { int off = getFieldOffset(name, Integer.TYPE); return (off >= 0 ) ? Bits.getInt(primVals, off) : val; } public float get (String name, float val) throws IOException { int off = getFieldOffset(name, Float.TYPE); return (off >= 0 ) ? Bits.getFloat(primVals, off) : val; } public long get (String name, long val) throws IOException { int off = getFieldOffset(name, Long.TYPE); return (off >= 0 ) ? Bits.getLong(primVals, off) : val; } public double get (String name, double val) throws IOException { int off = getFieldOffset(name, Double.TYPE); return (off >= 0 ) ? Bits.getDouble(primVals, off) : val; } public Object get (String name, Object val) throws IOException { int off = getFieldOffset(name, Object.class); if (off >= 0 ) { int objHandle = objHandles[off]; handles.markDependency(passHandle, objHandle); return (handles.lookupException(objHandle) == null ) ? objVals[off] : null ; } else { return val; } } void readFields () throws IOException { bin.readFully(primVals, 0 , primVals.length, false ); int oldHandle = passHandle; ObjectStreamField[] fields = desc.getFields(false ); int numPrimFields = fields.length - objVals.length; for (int i = 0 ; i < objVals.length; i++) { objVals[i] = readObject0(fields[numPrimFields + i].isUnshared()); objHandles[i] = passHandle; } passHandle = oldHandle; } private int getFieldOffset (String name, Class type) { ObjectStreamField field = desc.getField(name, type); if (field != null ) { return field.getOffset(); } else if (desc.getLocalDesc().getField(name, type) != null ) { return -1 ; } else { throw new IllegalArgumentException("no such field " + name + " with type " + type); } } }
涉及基础知识点
NIL
参考文献
NIL