實(shí)戰(zhàn))
1. 現(xiàn)場(chǎng)還原Kimi 交出來(lái)的那份“泛型后處理器”1.1 一個(gè)很常見(jiàn)但“差一步”的需求這周的第二次我盯著 IDE 里那道紅色波浪線嘆了口氣。業(yè)務(wù)側(cè)最近在做訂單域的敏感信息治理Order實(shí)體里除了訂單號(hào)、金額還有手機(jī)號(hào)、身份證號(hào)這類(lèi)不能明文落庫(kù)的字段。我當(dāng)時(shí)在兩種方案之間猶豫AOP 切面適合“某個(gè)注解標(biāo)記的方法或字段”而 BeanPostProcessor 適合“每個(gè) Bean 實(shí)例在容器里初始化完成后統(tǒng)一做手腳”比如自動(dòng)替換字段值、生成代理對(duì)象。我這次選了后者期望很簡(jiǎn)單Spring 容器創(chuàng)建好Order這個(gè) Bean 之后、交給業(yè)務(wù)代碼之前順手把敏感字段加密掉。于是我很自然地打開(kāi)對(duì)話框?qū)?Kimi 說(shuō)幫我寫(xiě)一個(gè) BeanPostProcessor在 Bean 初始化完成之后對(duì)Order類(lèi)型的實(shí)例做敏感字段加密。它秒回了一段“非常完整”的代碼類(lèi)名、注解、注釋一應(yīng)俱全唯獨(dú)開(kāi)頭的繼承聲明讓人眼前一黑。這段代碼從遠(yuǎn)處看沒(méi)有任何問(wèn)題項(xiàng)目里見(jiàn)慣了FactoryBeanT、ApplicationListenerE的人甚至?xí)X(jué)得“加個(gè)泛型挺合理”。但恰恰是這里埋了雷。1.2 初版答案的兩處硬傷Kimi 當(dāng)時(shí)給出的核心代碼長(zhǎng)這樣Component public class OrderFieldEncryptProcessor implements BeanPostProcessorOrder { Override public Order postProcessAfterInitialization(Order bean, String beanName) throws BeansException { encryptSensitiveFields(bean); return bean; } }第一處硬傷BeanPostProcessorOrder。Spring 里根本沒(méi)有帶泛型參數(shù)的 BeanPostProcessor 接口定義。把鼠標(biāo)挪到紅色波浪線上IDE 會(huì)提示 “Type BeanPostProcessor does not have type parameters”javac 的原話則是 “type BeanPostProcessor does not take parameters”。無(wú)論哪個(gè)版本這一步都過(guò)不了編譯。但這里要區(qū)分清楚報(bào)錯(cuò)只發(fā)生在繼承聲明那一行類(lèi)名和業(yè)務(wù)方法本身沒(méi)有錯(cuò)不需要把整段代碼推翻重來(lái)。第二處硬傷更陰險(xiǎn)發(fā)生在你嘗試“手動(dòng)去掉泛型”之后。如果把Order刪掉但方法參數(shù)仍然寫(xiě)Order bean代碼會(huì)變成Component public class OrderFieldEncryptProcessor implements BeanPostProcessor { public Order postProcessAfterInitialization(Order bean, String beanName) { encryptSensitiveFields(bean); return bean; } }這段代碼能通過(guò)編譯因?yàn)閜ostProcessAfterInitialization(Order, String)和接口里的postProcessAfterInitialization(Object, String)是參數(shù)類(lèi)型不同的兩個(gè)方法——這是一個(gè)普通的重載而不是重寫(xiě)。Spring 容器運(yùn)行時(shí)調(diào)的是(Object, String)版本你的實(shí)現(xiàn)根本不會(huì)被調(diào)用整個(gè)處理器靜默失效加密邏輯一次都不會(huì)執(zhí)行。所以我在糾正 AI 代碼時(shí)有一條鐵律凡是聲明要實(shí)現(xiàn)接口回調(diào)必須加Override。加不上去說(shuō)明簽名根本沒(méi)有覆寫(xiě)接口方法這時(shí)候繼續(xù)改比“看著沒(méi)問(wèn)題”重要得多。1.3 這類(lèi)錯(cuò)誤為什么反復(fù)出現(xiàn)這不是我第一次從 Kimi 那里收到“看起來(lái)對(duì)但簽名錯(cuò)”的 Spring 代碼。早前它給過(guò)一個(gè)關(guān)于Transactional內(nèi)調(diào)用生效問(wèn)題的錯(cuò)誤解釋也把ComponentScan的 basePackages 寫(xiě)錯(cuò)過(guò)。共性不是模型不聰明而是它對(duì) API 簽名的記憶是概率性的接口名、方法名、參數(shù)表是分開(kāi)召回的三件事任何一個(gè)環(huán)節(jié)記岔了就會(huì)生成“結(jié)構(gòu)上很像、簽名完全不對(duì)”的代碼。容易中招的命名簇恰恰是 BeanPostProcessor、FactoryBean、BeanFactoryPostProcessor 這種“單詞相同、職責(zé)不同”的族譜。理解這一點(diǎn)就不難理解為什么要在提示詞里做防呆設(shè)計(jì)也就能順理成章地問(wèn)出那個(gè)最根本的問(wèn)題Spring 為什么不讓 BeanPostProcessor 帶泛型2. 為什么 Spring 堅(jiān)持讓 BeanPostProcessor 保持無(wú)泛型2.1 它真實(shí)的接口簽名長(zhǎng)這樣先去依賴 jar 里把源碼翻出來(lái)接口定義是這樣的package org.springframework.beans.factory.config; public interface BeanPostProcessor { Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }有兩個(gè)容易忽略的細(xì)節(jié)。第一方法簽名是 Object 進(jìn)、Object 出沒(méi)有 Type Parameter。第二從 Spring Framework 5.3 開(kāi)始這兩個(gè)接口方法都提供了默認(rèn)實(shí)現(xiàn)默認(rèn)行為就是“原樣返回 bean”。這意味著實(shí)現(xiàn)類(lèi)可以只重寫(xiě)你需要的那一個(gè)回調(diào)而不是像老版本那樣兩個(gè)都得寫(xiě)。很多教程還停留在“必須實(shí)現(xiàn)倆方法”的舊印象AI 生成代碼時(shí)也容易受舊資料干擾所以當(dāng)你看到它把兩個(gè)方法都寫(xiě)全時(shí)不一定錯(cuò)但看到其他“所謂的新寫(xiě)法”時(shí)要能判斷來(lái)源。2.2 Object 進(jìn)、Object 出是設(shè)計(jì)使然BeanPostProcessor 是什么層面上的擴(kuò)展點(diǎn)是整個(gè) BeanFactory 的全局后處理器。Spring 容器每創(chuàng)建一個(gè) bean 實(shí)例都會(huì)拿著這個(gè)實(shí)例挨個(gè)過(guò)一遍已注冊(cè)的 BeanPostProcessor。接口在設(shè)計(jì)時(shí)的輸入集合是“工廠里所有可能的 bean”——數(shù)量無(wú)限、類(lèi)型未知還包括用戶自己定義的和框架內(nèi)部的 BeanDefinition。在這種開(kāi)放性面前接口簽名里寫(xiě)T沒(méi)有意義寫(xiě)任何一個(gè)具體的 T 都會(huì)把其他 bean 擋在門(mén)外而寫(xiě)一個(gè)T泛型又無(wú)法在這個(gè)“什么類(lèi)型都可能出現(xiàn)”的入口做分派。從 JVM 角度看更直白。泛型信息在編譯后會(huì)被擦除BeanPostProcessorOrder最終在字節(jié)碼里還是BeanPostProcessor方法簽名仍然是(Object, String) - Object。就算 Spring 真的把接口定義成BeanPostProcessorT容器在反射回調(diào)時(shí)也不可能知道你要的 T 是什么類(lèi)型檢查完全無(wú)從談起。接口層面的“泛型”只會(huì)是一種虛假的安全感。那真正按類(lèi)型分流怎么做就是我在修正版里用的方式在回調(diào)方法內(nèi)部用instanceof判斷if (bean instanceof Order order) { ... }。Spring 的意圖很明確回調(diào)給你一個(gè)最普適的 Object由你決定是否關(guān)心、如何處理。類(lèi)似的還有BeanFactoryPostProcessor它接收ConfigurableListableBeanFactory處理的不是某個(gè)類(lèi)型而是整個(gè)工廠的配置元數(shù)據(jù)同樣沒(méi)有泛型參數(shù)。只要你看到“接口方法是 Object 入?yún)ⅰ⒒蛎嫦蛉S對(duì)象”基本可以斷定它不是泛型接口。2.3 與真正泛型的回調(diào)接口對(duì)比把“產(chǎn)出類(lèi)型”和“事件類(lèi)型”分清楚Spring 生態(tài)里確實(shí)有一批“真泛型”回調(diào)接口它們和 BeanPostProcessor 的區(qū)別非常清晰。FactoryBeanT是泛型接口因?yàn)樗穆氊?zé)是“為某一個(gè)確定類(lèi)型生產(chǎn) Bean”getObject()返回 T泛型是接口的內(nèi)在語(yǔ)義ApplicationListenerE extends ApplicationEvent也是泛型接口因?yàn)槭录O(jiān)聽(tīng)面對(duì)的是一個(gè)相對(duì)封閉的聲明式類(lèi)型體系Spring 可以用ResolvableType推斷監(jiān)聽(tīng)器關(guān)注的事件類(lèi)型從而在分發(fā)時(shí)做精確過(guò)濾。這兩類(lèi)接口的共性是有一個(gè)“確定的產(chǎn)出類(lèi)型或目標(biāo)類(lèi)型”。反觀 BeanPostProcessor它不生產(chǎn)某一類(lèi)型也不消費(fèi)某一事件它被放置在所有 Bean 的生命周期路徑上面向的是開(kāi)放集合。泛型的收益是編譯期類(lèi)型檢查而這里根本沒(méi)得可查。“無(wú)泛型”不是偷懶是誠(chéng)實(shí)。還有一個(gè)小細(xì)節(jié)值得注意Java 允許協(xié)變返回類(lèi)型你可以把重寫(xiě)后的返回類(lèi)型寫(xiě)成Order但入?yún)⑦€是只能寫(xiě)Object因?yàn)檎瘏?shù)類(lèi)型會(huì)被當(dāng)成重載處理這正是上一節(jié)那個(gè)“靜默失效”陷阱的語(yǔ)法根源。2.4 修正后的實(shí)現(xiàn)長(zhǎng)什么樣把簽名改對(duì)之后正確的實(shí)現(xiàn)大概是這樣的Component public class OrderFieldEncryptProcessor implements BeanPostProcessor, Ordered { Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Order order) { encryptSensitiveFields(order); } return bean; } Override public int getOrder() { return 100; } }三個(gè)要點(diǎn)。其一接口方法寫(xiě)全或只寫(xiě)一個(gè)都行但寫(xiě)出來(lái)的必須帶OverrideIDE 能通過(guò)說(shuō)明簽名真的對(duì)上了。其二instanceof判斷放在回調(diào)內(nèi)部只處理自己關(guān)心的類(lèi)型其余原樣返回。bean instanceof Order order這種模式匹配寫(xiě)法需要 Java 16如果項(xiàng)目還在 Java 8/11就用傳統(tǒng)寫(xiě)法if (bean instanceof Order) { Order order (Order) bean; ... }。其三如果實(shí)現(xiàn)了Ordered或標(biāo)注Order多個(gè)后處理器之間就有了執(zhí)行順序getOrder 值越小越先執(zhí)行。你如果同時(shí)有加密處理器和代理生成處理器排序錯(cuò)亂會(huì)導(dǎo)致某些 Bean 沒(méi)被加密就進(jìn)了代理。注意BeanPostProcessor 本身是容器啟動(dòng)早期就被實(shí)例化的基礎(chǔ)組件它會(huì)在絕大多數(shù)業(yè)務(wù) Bean 之前完成創(chuàng)建和依賴注入。如果你在它的字段里直接注入一個(gè)普通業(yè)務(wù)服務(wù)比如Autowired OrderRepository這個(gè)倉(cāng)庫(kù)也會(huì)被提前拉起來(lái)可能繞過(guò)本該由其他后處理器施加的增強(qiáng)邏輯造成時(shí)序怪病。需要依賴時(shí)就地通過(guò)ApplicationContext延遲獲取或者改成ObjectProvider別在構(gòu)造階段硬引業(yè)務(wù) Bean。這個(gè)經(jīng)驗(yàn)不寫(xiě)在官方文檔里但排查一次就忘不了。3. 從報(bào)錯(cuò)到修正一個(gè) 5 分鐘自查鏈路3.1 讓報(bào)錯(cuò)文本替你說(shuō)話別憑感覺(jué)改糾正 AI 代碼的第一原則把編譯器的報(bào)錯(cuò)文本當(dāng)作對(duì)話入口而不是憑“API 應(yīng)該長(zhǎng)什么樣”的感覺(jué)直接動(dòng)手。IDE 的紅色波浪線和 javac 的輸出是最權(quán)威的簽名裁判。當(dāng)你看到 “type BeanPostProcessor does not take parameters”問(wèn)題范圍已經(jīng)縮到很小不是方法名錯(cuò)了是尖括號(hào)用錯(cuò)了。把這行英文原樣貼回給 Kimi它下一輪生成的準(zhǔn)確率會(huì)明顯提高——我反復(fù)試過(guò)大白話描述“你寫(xiě)的是泛型吧”遠(yuǎn)不如貼報(bào)錯(cuò)有效。3.2 用三種渠道交叉驗(yàn)證接口簽名最順手的方式是在 IDE 里按住 CtrlMac 是 Cmd點(diǎn)擊BeanPostProcessor接口名直接跳到依賴 jar 里的源碼。如果不方便打開(kāi) IDE或者你想在 CI 容器、純命令行環(huán)境里做快速核實(shí)javap是很好的工具。假設(shè)本地 Maven 倉(cāng)庫(kù)里有 spring-beans 的 jarjavap -classpath ~/.m2/repository/org/springframework/spring-beans/6.x.x/spring-beans-6.x.x.jar \ org.springframework.beans.factory.config.BeanPostProcessorjavap 輸出會(huì)直接顯示接口的所有方法簽名關(guān)鍵信息一目了然public interface org.springframework.beans.factory.config.BeanPostProcessor { public default java.lang.Object postProcessBeforeInitialization(java.lang.Object, java.lang.String) throws org.springframework.beans.BeansException; public default java.lang.Object postProcessAfterInitialization(java.lang.Object, java.lang.String) throws org.springframework.beans.BeansException; }沒(méi)有尖括號(hào)接口名后面干干凈凈。第三種渠道是官方 API 文檔搜索 BeanPostProcessor 的 Javadoc接口名旁邊同樣沒(méi)有任何類(lèi)型參數(shù)。三個(gè)渠道任一命中就能確認(rèn)但建議養(yǎng)成“兩個(gè)渠道互相印證”的習(xí)慣因?yàn)?AI 給出的代碼有時(shí)會(huì)連 import 都省掉單看文本很難判斷它到底引自哪個(gè)版本的 Spring。3.3 重寫(xiě)、編譯、跑一個(gè)只驗(yàn)證行為的測(cè)試簽名確認(rèn)后按第 2.4 節(jié)的寫(xiě)法重寫(xiě)。然后把“編譯通過(guò)”設(shè)成驗(yàn)收線沒(méi)有一個(gè)Override的回調(diào)實(shí)現(xiàn)默認(rèn)就是錯(cuò)的。接下來(lái)別只停留在“能啟動(dòng)”補(bǔ)一個(gè)最小測(cè)試去驗(yàn)證處理器真的工作過(guò)SpringBootTest class OrderFieldEncryptProcessorTest { Autowired private ApplicationContext context; Test void orderBeanShouldBeEncryptedAfterInitialization() { Order order context.getBean(Order.class); assertNotEquals(raw-mobile-number, order.getMobile()); } }如果測(cè)試?yán)锇l(fā)現(xiàn)字段還是明文再回到斷點(diǎn)在postProcessAfterInitialization第一行打上斷點(diǎn)條件寫(xiě)成bean instanceof Order。每次容器創(chuàng)建一個(gè) Bean 都會(huì)路過(guò)這個(gè)方法如果一次都沒(méi)命中斷點(diǎn)說(shuō)明你的處理器根本沒(méi)有被注冊(cè)如果命中了但字段沒(méi)變說(shuō)明加密邏輯寫(xiě)錯(cuò)地方了。這一條鏈路走下來(lái)絕大多數(shù)“AI 代碼不生效”的問(wèn)題都能在十分鐘內(nèi)定位比反復(fù)讀代碼猜原因快得多。4. 真假泛型對(duì)照AI 在 Spring 回調(diào)接口上最常翻車(chē)的幾個(gè)點(diǎn)4.1 一張表看清哪些是真泛型我把自己踩過(guò)的、以及身邊人踩過(guò)的幾類(lèi)回調(diào)接口整理成了對(duì)照表方便你下次生成代碼后直接比對(duì)接口是否泛型關(guān)鍵方法/簽名AI 常見(jiàn)翻車(chē)方式BeanPostProcessor否postProcessAfterInitialization(Object, String)想當(dāng)然寫(xiě)成 BeanPostProcessor 或把參數(shù)窄化為具體類(lèi)型BeanFactoryPostProcessor否postProcessBeanFactory(ConfigurableListableBeanFactory)誤加泛型或與 BeanPostProcessor 混為同一種回調(diào)FactoryBean是getObject() 返回 T混淆“生產(chǎn)某一類(lèi)型”和“后處理所有類(lèi)型”的職責(zé)ApplicationListener是onApplicationEvent(E)漏寫(xiě)事件類(lèi)型導(dǎo)致監(jiān)聽(tīng)范圍被放大ConverterS, T是convert(S) 返回 TS/T 順序?qū)懛碿onvert 方法簽名編譯不過(guò)HttpMessageConverter是read/write 操作 T把 T 和具體的請(qǐng)求響應(yīng)類(lèi)型搞混InitializingBean否afterPropertiesSet()編造帶參數(shù)的 afterPropertiesSet(bean)Aware 系列否setXxx(...) 由具體接口決定接口名相近導(dǎo)致張冠李戴4.2 表里每行背后的判斷標(biāo)準(zhǔn)把這張表背下來(lái)不如掌握判斷標(biāo)準(zhǔn)接口方法有沒(méi)有“明確的產(chǎn)出類(lèi)型或目標(biāo)類(lèi)型”。FactoryBean、Converter、HttpMessageConverter 都是“我要把某一種東西轉(zhuǎn)/生成/讀寫(xiě)為另一種東西”所以泛型直接寫(xiě)在接口級(jí)ApplicationListener 是因?yàn)槭录w系是聲明式的、封閉的泛型能用來(lái)過(guò)濾和綁定事件類(lèi)型。而 BeanPostProcessor、BeanFactoryPostProcessor、InitializingBean、Aware 系列回調(diào)發(fā)生時(shí)對(duì)象已經(jīng)存在接口并不聲明“產(chǎn)出類(lèi)型”它只是在路徑上插一腳所以統(tǒng)統(tǒng)不帶泛型。記憶錨點(diǎn)就一句話后處理不帶泛型生產(chǎn)/轉(zhuǎn)換/監(jiān)聽(tīng)事件才帶泛型。4.3 BeanXxxProcessor 家族識(shí)別指南順帶把名字最像的幾位請(qǐng)到一張桌上。BeanPostProcessor管的是 Bean 實(shí)例初始化前后的回調(diào)處理對(duì)象是已經(jīng)創(chuàng)建出來(lái)的實(shí)例BeanFactoryPostProcessor管的是容器早期對(duì) BeanDefinition 配置元數(shù)據(jù)的修改比如改屬性占位符、調(diào) scopeInstantiationAwareBeanPostProcessor是 BeanPostProcessor 的擴(kuò)展可以在實(shí)例化前后、屬性填充前后介入甚至能決定要不要走默認(rèn)實(shí)例化流程MergedBeanDefinitionPostProcessor則在 BeanDefinition 合并之后回調(diào)。這些接口全都沒(méi)有泛型。AI 只需要把這些名字里的 “PostProcessor” 記憶成“帶類(lèi)型的回調(diào)”就會(huì)產(chǎn)出你遇到的這類(lèi)錯(cuò)誤代碼。自己核對(duì)時(shí)先看一遍接口名的英文語(yǔ)義再?zèng)Q定要不要繼續(xù)用。5. 和 AI 結(jié)對(duì)寫(xiě) Spring 代碼提示詞里的“防呆設(shè)計(jì)”與糾錯(cuò)閉環(huán)5.1 別只給意圖把約束條件寫(xiě)進(jìn)去同樣是讓 AI 寫(xiě)后處理器兩個(gè) prompt 的效果差別很大。只寫(xiě)“幫我寫(xiě)一個(gè) BeanPostProcessor 處理 Order 加密”它大概率會(huì)憑概率記憶生成“看似合理”的接口如果寫(xiě)成這樣實(shí)現(xiàn) org.springframework.beans.factory.config.BeanPostProcessor。注意該接口不是泛型接口方法簽名為 postProcessAfterInitialization(Object bean, String beanName)返回 Object。我要對(duì) Order 類(lèi)型的 Bean 做敏感字段加密請(qǐng)?jiān)诜椒▋?nèi)用 instanceof 判斷類(lèi)型。生成的代碼請(qǐng)帶 Override并說(shuō)明你參考的 Spring 版本。結(jié)果幾乎不會(huì)再出現(xiàn)尖括號(hào)。約束越具體模型可發(fā)揮的想象空間越小出錯(cuò)率自然就下來(lái)了。5.2 把報(bào)錯(cuò)原文貼回去讓 AI 自己糾錯(cuò)“繼續(xù)幫助 Kimi 糾正錯(cuò)誤”這件事我把它做成一個(gè)固定循環(huán)AI 生成 → 本地編譯/跑測(cè)試 → 把報(bào)錯(cuò)原文原樣貼回 → AI 修正 → 再編譯。這個(gè)閉環(huán)的精髓是決不把 AI 的第一次輸出當(dāng)作答案也決不把自己的口頭轉(zhuǎn)述當(dāng)作輸入。報(bào)錯(cuò)是確定性的歧義少模型對(duì)“糾正一個(gè)編譯錯(cuò)誤”的任務(wù)完成率比“從一個(gè)籠統(tǒng)目標(biāo)直接寫(xiě)出正確代碼”高得多因?yàn)樗蔷植啃薷牟皇菑牧闵?。我之前低估了這條路線的成功率只要把 IDE 里的英文報(bào)錯(cuò)整段丟回去模型通常能定位到簽名問(wèn)題自己口頭復(fù)述反而容易觸發(fā)二次腦補(bǔ)。5.3 關(guān)鍵骨架自己先畫(huà)讓 AI 只填空對(duì)于 Spring 回調(diào)這類(lèi)“簽名敏感”的代碼我更推薦先用腳手架固定住結(jié)構(gòu)再讓 AI 填方法體。比如自己先把類(lèi)聲明和空方法寫(xiě)好Component public class OrderFieldEncryptProcessor implements BeanPostProcessor { // 待補(bǔ)充postProcessAfterInitialization 的邏輯 }然后讓 AI 只補(bǔ)方法內(nèi)部邏輯。有人覺(jué)得這樣“不夠 AI”但實(shí)踐中把簽名這種高錯(cuò)誤率的部分留在人類(lèi)手里把業(yè)務(wù)邏輯這種高自由度部分交給模型錯(cuò)誤率會(huì)顯著下降。這本質(zhì)上是在跟模型的概率記憶做風(fēng)險(xiǎn)隔離。5.4 測(cè)試是發(fā)現(xiàn)“偽正確”的唯一手段每次讓 AI 給 Spring 代碼我都要求附一個(gè)能驗(yàn)證行為的測(cè)試。沒(méi)有測(cè)試就直接編譯通過(guò)只說(shuō)明“語(yǔ)法對(duì)”不說(shuō)明“職責(zé)對(duì)”。前文的重載陷阱是最好的例子代碼能編譯、能啟動(dòng)、日志里一切正常但處理器一次都沒(méi)被調(diào)用。只有測(cè)試會(huì)把這種“偽正確”揪出來(lái)。所以我對(duì) AI 生成代碼的態(tài)度可以總結(jié)成一句編譯過(guò)了只是起點(diǎn)行為驗(yàn)證過(guò)了才算數(shù)。6. 這次糾錯(cuò)之后我給自己定的三條規(guī)矩第一條AI 生成任何實(shí)現(xiàn)接口的類(lèi)先看接口簽名再看業(yè)務(wù)實(shí)現(xiàn)順序不能反。第二條回調(diào)接口的代碼必須帶Override必須本地編譯通過(guò)沒(méi)有例外。第三條每次糾錯(cuò)沉淀一條“報(bào)錯(cuò)原文 → 修正代碼 → 原因說(shuō)明”的短記錄下次生成前把相關(guān)條目作為上下文丟給模型。這三條看起來(lái)樸素卻讓我的 AI 結(jié)對(duì)編程從碰運(yùn)氣變成了可復(fù)盤(pán)的流程。最后分享一個(gè)我自己的習(xí)慣如果哪天我發(fā)現(xiàn) Kimi 又給出了帶尖括號(hào)的 BeanPostProcessor我不會(huì)再急著嫌棄它記性差而是把這次的排查鏈路整理成一段文字直接發(fā)過(guò)去讓它基于上下文產(chǎn)出下一版。模型會(huì)忘記流程不會(huì)只要糾錯(cuò)閉環(huán)還在它那些“想象力豐富的簽名”就永遠(yuǎn)只是給我遞代碼的草稿而不是直接上線的結(jié)果。