
一.基礎(chǔ)概念
UUID:Universally Unique Identifier,通用唯一識(shí)別碼。是一種軟件建構(gòu)的標(biāo)準(zhǔn),亦為開放軟件基金會(huì)組織在分布式計(jì)算環(huán)境領(lǐng)域的一部分。UUID的目的,是讓分布式系統(tǒng)中的所有元素,都能有唯一的辨識(shí)信息,而不需要通過中央控制端來做辨識(shí)信息的指定。
歷史
UUID最初被應(yīng)用在Apollo Network Computing System,隨后被開放軟件基金會(huì)(OSF)應(yīng)用在分布式計(jì)算環(huán)境領(lǐng)域。
后來,IETF(國(guó)際互聯(lián)網(wǎng)工程任務(wù)組)將UUID作為一種標(biāo)準(zhǔn)發(fā)布在RFC 4122。
格式
標(biāo)準(zhǔn)的UUID格式如下:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
除連字符-外,上面每個(gè)字符都是一個(gè)十六進(jìn)制的數(shù)字,共有5個(gè)部分組成,第一部分8個(gè),第二部分4個(gè),第三部分4個(gè),第四部分4個(gè),第五部分12個(gè),8-4-4-4-12,一共32個(gè)十六進(jìn)制字符,因此一共是128位。
其中,M表示UUID的版本,N表示UUID的變體。
變體
為了能兼容過去的UUID,以及應(yīng)對(duì)未來的變化,因此有了變體(Variants)這一概念。目前已知的變體有如下幾種:
variant 0:N的格式為0xxx。為了向后兼容預(yù)留。
variant 1:10xx。當(dāng)前正在使用的。
variant 2:11xx。為早期微軟GUID預(yù)留。
variant 3:111x。為將來擴(kuò)展預(yù)留。目前暫未使用。
因此,可以認(rèn)為,目前正在使用的UUID都是variant1,取值是8,9,a,b中的一個(gè)。
版本
版本用于定義UUID的形成方法:
Version 1:基于時(shí)間和MAC地址。由于使用了MAC地址,因此能夠確保唯一性,但是同時(shí)也暴露了MAC地址,私密性不夠好。
Version 2:DCE安全的UUID。該版本在規(guī)范中并沒有仔細(xì)說明,因此并沒有具體的實(shí)現(xiàn)。
Version 3 :基于名字空間(MD5)。用戶指定一個(gè)名字空間和一個(gè)字符串,通過MD5散列,生成UUID。字符串本身需要是唯一的。
Version 4 :基于隨機(jī)數(shù)。雖然是基于隨機(jī)數(shù),但是重復(fù)的可能性可以忽略不計(jì),因此該版本也是被經(jīng)常使用的版本。
Version 5 : 基于名字空間(SHA1)。跟Version 3類似,但是散列函數(shù)編程了SHA1。
二.類定義
java sdk中提供了UUID的Version 3和Version 4的具體實(shí)現(xiàn)。我們來看一下具體的類定義:
public final class UUID
implements java.io.Serializable, Comparable<UUID>
該類被定義為final的,說明不希望被繼承。
類中定義了如下變量:
// 高64位
private final long mostSigBits;
// 低64位
private final long leastSigBits;
// 版本
private transient int version = -1;
// 變體
private transient int variant = -1;
// 時(shí)間戳,版本1專用
private transient volatile long timestamp = -1;
// 時(shí)鐘頻率,版本1專用
private transient int sequence = -1;
// mac地址,版本1專用
private transient long node = -1;
// hash值
private transient int hashCode = -1;
UUID是128位的,在Java的UUID中,是將這128分為高64位和低64位分別存儲(chǔ)的。
三.核心方法
1.構(gòu)造方法
共有兩個(gè)構(gòu)造方法:
// 通過字節(jié)數(shù)組來生成UUID,字節(jié)數(shù)組長(zhǎng)度必須是16個(gè)字節(jié)
private UUID(byte[] data) {
long msb = 0;
long lsb = 0;
assert data.length == 16;
// 將前8個(gè)字節(jié)賦值到高64位
for (int i=0; i<8; i++)
msb = (msb << 8) | (data[i] & 0xff);
// 將后8個(gè)字節(jié)賦值到低64位
for (int i=8; i<16; i++)
lsb = (lsb << 8) | (data[i] & 0xff);
this.mostSigBits = msb;
this.leastSigBits = lsb;
}
// 直接指定高64位和低64位的值
public UUID(long mostSigBits, long leastSigBits) {
this.mostSigBits = mostSigBits;
this.leastSigBits = leastSigBits;
}
2.randomUUID
該方法可以生成一個(gè)版本4的UUID。
// 生成版本4UUID
public static UUID randomUUID() {
// 偽隨機(jī)數(shù)生成器
SecureRandom ng = Holder.numberGenerator;
byte[] randomBytes = new byte[16];
// 生成16個(gè)字節(jié)共128位的偽隨機(jī)數(shù)
ng.nextBytes(randomBytes);
// 將帶有版本號(hào)的那個(gè)字節(jié)與00001111進(jìn)行按位與,表示版本號(hào)的4個(gè)bit將變成0000
randomBytes[6] &= 0x0f;
// 將帶有版本號(hào)的字節(jié)與01000000進(jìn)行按位或,表示版本號(hào)的4個(gè)bit將變成0100,說明是版本4
randomBytes[6] |= 0x40;
// 將帶有變體的字節(jié)與00111111進(jìn)行按位與,表示變體的4個(gè)bit將變成00xx
randomBytes[8] &= 0x3f; /* clear variant */
// 將帶有變體的字節(jié)與10000000進(jìn)行按位或,表示變體的4個(gè)bit將變成10xx,說明是變體2
randomBytes[8] |= 0x80; /* set to IETF variant */
return new UUID(randomBytes);
}
3.nameUUIDFromBytes
該方法將生成一個(gè)版本3的UUID。
public static UUID nameUUIDFromBytes(byte[] name) {
// MessageDigest是信息摘要類,提供md5,sha1等算法。
MessageDigest md;
try {
// 獲取提供MD5算法的MessageDigest實(shí)例
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsae) {
throw new InternalError("MD5 not supported");
}
// 利用md5算法對(duì)結(jié)合name,生成md5值,md5和UUID都是16個(gè)字節(jié)
byte[] md5Bytes = md.digest(name);
// 以下操作類似于randomUUID,只是會(huì)將版本復(fù)制為3
md5Bytes[6] &= 0x0f;
md5Bytes[6] |= 0x30;
md5Bytes[8] &= 0x3f;
md5Bytes[8] |= 0x80;
return new UUID(md5Bytes);
}
參考資料:
1.維基百科:Universally unique identifier
2.簡(jiǎn)書:關(guān)于UUID的二三事
3.How is an UUID / GUID made
本文已遷移至我的博客:http://ipenge.com/7413.html