做金融類(lèi)網(wǎng)站西安企業(yè)seo外包服務(wù)公司
假ArrayList導(dǎo)致的線(xiàn)上事故…
線(xiàn)上事故回顧
- 晚飯時(shí),當(dāng)我正沉迷于排骨煲肉質(zhì)鮮嫩,湯汁濃郁時(shí),產(chǎn)研溝通群內(nèi)發(fā)出一條消息,顯示用戶(hù)存在可用劵,但進(jìn)去劵列表卻什么也沒(méi)有,并附含了一個(gè)視頻。于是我一邊吃了排骨,一邊查看消息點(diǎn)開(kāi)了視頻,en~,視頻跟描述一樣。但沒(méi)有系統(tǒng)告警,用戶(hù)界面也沒(méi)有明顯的報(bào)錯(cuò)提示,懷疑是小部分特殊情況導(dǎo)致的,查看消息后幾秒,我直接被@來(lái)處理問(wèn)題,擦,只好把外賣(mài)盒重新蓋好,先去處理問(wèn)題。
處理經(jīng)過(guò)
-
通過(guò)群內(nèi)產(chǎn)品發(fā)的用戶(hù)郵箱查到了用戶(hù)id,再根據(jù)接口的相關(guān)日志結(jié)合uid在日志平臺(tái)進(jìn)行關(guān)聯(lián)查詢(xún),查到日志后,再拿到traceId進(jìn)行鏈路查詢(xún),果不其然,發(fā)現(xiàn)了異常日志,如下部分日志所示
-
java.lang.UnsupportedOperationException: nullat java.util.AbstractList.add(AbstractList.java:148) ~[na:1.8.0_151]at java.util.AbstractList.add(AbstractList.java:108) ~[na:1.8.0_151]
-
這
UnsupportedOperationException
是個(gè)什么玩意 -
@Slf4j @SpringBootTest public class Demo {public void test(Context context) { context.getList().add("皮皮蝦");}}@Data class Context {private List<String> list;}
-
基本操作就是拿到上下文中的List,然后再add一個(gè)元素
-
講道理,add操作是不會(huì)有問(wèn)題的,有問(wèn)題的還得是List,追根溯源,讓我康康這個(gè)List是怎么來(lái)的于是我一頓狂點(diǎn),來(lái)到了set這個(gè)list的位置
-
@Slf4j @SpringBootTest public class Demo {public void test(Context context) {context.setList(Arrays.asList("皮皮蝦"));}}@Data class Context {private List<String> list;}
-
context.setList(Arrays.asList("Code皮皮蝦"));
這行看起來(lái)好像沒(méi)問(wèn)題啊Arrays.asList(T... a)
我們平時(shí)也會(huì)用,傳入一個(gè)數(shù)組,返回出一個(gè)List
沒(méi)啥問(wèn)題呀 -
那我再試試
add
方法 ,擦,問(wèn)題復(fù)現(xiàn)了,還真是Arrays.asList(T... a)
生成的List
的add方法報(bào)錯(cuò),由于線(xiàn)上存在問(wèn)題,則先修改為以下代碼上線(xiàn),也就是修改為我們平時(shí)正常的寫(xiě)法, 上線(xiàn)后,觀察了下日志,群里回復(fù)已解決問(wèn)題,也讓用戶(hù)重試,發(fā)現(xiàn)沒(méi)問(wèn)題,自此問(wèn)題解決。
追根溯源
-
進(jìn)入
asList
方法,發(fā)現(xiàn)底層new
了一個(gè)ArrayList
,并將數(shù)組傳入作為List
的元素 -
@SafeVarargs @SuppressWarnings("varargs") public static <T> List<T> asList(T... a) {return new ArrayList<>(a); }
-
emm,看起來(lái)很簡(jiǎn)單啊,沒(méi)問(wèn)題啊,咋會(huì)報(bào)錯(cuò)呢?別著急,咱們?cè)邳c(diǎn)開(kāi)這個(gè)
ArrayList
瞅瞅 -
private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable {private static final long serialVersionUID = -2764017481108945198L;private final E[] a;ArrayList(E[] array) {a = Objects.requireNonNull(array);}// ... 省略 }
-
擦,這
ArrayList
是Arrays
類(lèi)的一個(gè)靜態(tài)內(nèi)部類(lèi),不是我們經(jīng)常用的java.util.ArrayList
繼續(xù)看,這個(gè)靜態(tài)內(nèi)部類(lèi)ArrayList
繼承了AbstractList
,而且默認(rèn)是沒(méi)有實(shí)現(xiàn)add
方法的.也就是說(shuō)調(diào)用add
方法會(huì)直接調(diào)用父類(lèi),也就是AbstractList
的add
方法,源碼點(diǎn)開(kāi)一看,真相大白了.AbstractList
的add
方法直接拋出UnsupportedOperationException
異常,跟線(xiàn)上報(bào)錯(cuò)一模一樣!!