第一部分
互动
- Q1
从一次 lock_market_pay_order 请求进入开始,完整讲清这条链路怎么走。
要求你回答到:
MarketTradeController 里先做了哪些前置判断,为什么先查未支付订单、为什么还要查拼团进度,试算结果里的哪些字段最终进入了锁单聚合对象,最后 repository 层理
论上需要一次事务落哪些核心数据。
Answer:拼团这个业务我把他划分了三层链路,第一层是在domin.activit的锁单链路在IndexGroupBuyMarketServiceImpl中调用,具体逻辑由DefaultActivityStrategyFactory工厂创建头结点,通过实现mappper和hander的模版方法实现链路的调用和整条执行。第二个链路是锁单在 domain.trade.service.lock下,第三条链路在domain.trade.service.settlement中做结算。MarketTradeController 里做了人群标签限定前置判断,先查未支付订单防止重复下单、为什么还要查拼团进度为了做成团的校验好走后续通知外部订单的流程。试算结果里的用户实体,支付活动实体,支付优惠实体,已参与量最终进入了锁单聚合对象。 repository 层需要一次事务落用户商品等核心数据 - Q2
你简历里说“责任链模式构建风控安检门”。请你结合代码说明这个责任链在这里到底过滤了什么、上下文怎么传递、为什么不是简单 if-else。
要求你回答到:
TradeRuleFilterFactory.DynamicContext 的作用是什么;当前代码里已经落地了哪些 filter;如果你现在要新增“渠道黑名单”规则,应该放在哪一层、怎么接入、怎样避免
破坏已有链路。
Answer:当前锁单责任链是由 TradeRuleFilterFactory 组装的通过TradeLockRuleCommandEntity 来负责传入本次校验的命令参数,比如活动 ID、用户 ID;上下文通过工厂里的动态上下文DynamicContext传递中间态。简单 if-else不好维护,需要写大量的机械重复代码,后续如果增加节点则需要修改原来的类违反开闭原则。 - Q3
你简历里说“本地消息表保证最终一致性”。请你基于结算链路讲清楚:什么时候写 notify_task,什么时候立即通知,什么时候靠 job 补偿,状态是怎么流转的。
要求你回答到:
settlementMarketPayOrder 和 execSettlementNotifyJob 之间的关系;isNotify 为什么存在;notifyCount < 5 这一段逻辑你怎么看,它的命名和状态迁移是否有歧义;如
果外部系统一直失败,这套方案最后会怎样收敛。
Answer:如果拼团的数量等于目标数则在在notify_task写入一条数据,通过定时任务如果到达指定时间来立即通知,如果外部系统挂了则通过job 补偿 - Q4
你简历里说你做了 DCC 热更新和大促降级。请你直接解释 DCCValueBeanFactory 的工作机制,以及它有哪些风险。
要求你回答到:
Bean 初始化阶段做了什么,为什么要处理 AOP 代理,Redis topic 收到消息后如何定位并修改字段;这套实现对字段类型、线程安全、配置一致性、异常隔离分别有什么问
题;如果让你在线上继续演进,你会怎么改。
Answer:自定义一个注解类DCCValue,DCCValueBeanFactory本身是一个spring中的Configuration类 会在启动时被加载,然后他会在 Bean 初始化阶段扫描所有类是否有DCCValue注解,为了得到真实的类的信息 必须剥去AOP 代理,启动时将 配置Key → Bean对象 存入 dccObjGroup Map
收到消息时通过 Key 反查 Bean,再通过字段名反射修改,对于问题我没有发现什么
反馈:
Findings
5. Q1 回答和代码不一致,主链路顺序说偏了。
你把“第一层链路”说成 domain.activity 的锁单链路,但实际 lock_market_pay_order 入口先在 /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-
trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:50 做参数校验,再查未支付订单 /D:/develop/javaproject/my-group-buy-market/my-
group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:71,再查拼团进度 /D:/develop/javaproject/my-group-buy-
market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:90,然后才调用试算 /D:/develop/javaproject/my-
group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:102,最后进入锁单服务 /D:/develop/
javaproject/my-group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:121。你没把这条控制层到领
域层的真实顺序讲出来。
6. Q1 对“为什么查拼团进度”的解释不对。
代码里的拦截条件是 targetCount == lockCount,直接目的是阻止已达锁单目标的团继续占座,不是“为了走后续通知外部订单流程”,证据在 /D:/develop/javaproject/my-
group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:91。
7. Q1 对聚合对象内容回答不完整。
锁单聚合实际是 userEntity + payActivityEntity + payDiscountEntity + userTakeOrderCount,其中 userTakeOrderCount 不是试算结果直接给的,而是责任链过滤结果
返回的 /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/lock/
TradeLockOrderService.java:46。另外 payActivityEntity 和 payDiscountEntity 的字段来源你也没展开,比如 activityName/startTime/endTime/validTime/
targetCount 来自试算中的 groupBuyActivityDiscountVO,/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/
trigger/http/MarketTradeController.java:124;goodsName/originalPrice/deductionPrice/payPrice/outTradeNo/notifyUrl 在 /D:/develop/javaproject/my-group-
buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:133。
8. Q2 只回答了模式层面的好处,没有落到当前实现。
当前工厂明确只装配了两个 filter:ActivityUsabilityRuleFilter 和 UserTakeLimitRuleFilter,证据在 /D:/develop/javaproject/my-group-buy-market/my-group-buy-
market-domain/src/main/java/com/SadSunset/domain/trade/service/lock/factory/TradeRuleFilterFactory.java:29。你没有回答“现在实际落地了哪些 filter”,这在真
实面试里会被认为是没有看过自己代码。
9. Q2 对 DynamicContext 的理解还停留在抽象层,没有结合字段。
这个上下文当前只有一个 groupBuyActivity 字段 /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/
trade/service/lock/factory/TradeRuleFilterFactory.java:43,说明它至少承担了过滤节点间传递活动信息的职责。你没有说出具体字段,说明回答不够贴代码。
10. Q3 回答过于空泛,没有解释代码真实行为。
settlementMarketPayOrder 里先走结算规则链,再构造聚合,再调用 repository 结算,返回 isNotify 后如果为真,会立即执行指定 teamId 的通知任务 /D:/develop/
javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/settlement/
TradeSettlementOrderService.java:38 /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/
service/settlement/TradeSettlementOrderService.java:74 /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/
SadSunset/domain/trade/service/settlement/TradeSettlementOrderService.java:77。而兜底 job 走的是扫全部未执行任务 /D:/develop/javaproject/my-group-buy-
market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/settlement/TradeSettlementOrderService.java:93。你没有区分“即时通
知”和“定时补偿”这两条路径。
11. Q3 没指出一处非常应该指出的实现风险。
notifyCount < 5 时调用的是 updateNotifyTaskStatusError,否则调用的是 updateNotifyTaskStatusRetry /D:/develop/javaproject/my-group-buy-market/my-group-
buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/settlement/TradeSettlementOrderService.java:122。从命名看,这很容易让人怀疑 error 和
retry 的语义反了,至少命名存在歧义。你没有识别出这个点,和你简历里“后端负责人”的定位不匹配。
12. Q4 回答前半段基本对,后半段失分严重。
你说“没有发现什么问题”,但这个实现至少有几类明显风险:
/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:35 的 HashMap 在监听回调
与 Bean 初始化并发场景下不安全;
/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:73 和 /D:/develop/
javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:132 全部按 String 反射赋值,字段不是
String 时会出问题;
/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:45 直接 split 取下标,没
有报文格式保护;
/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:77 监听线程里直接抛
RuntimeException,异常隔离差。
这题你如果答“没发现问题”,真实面试里会直接扣分。
How To Answer Better
Q1 这题你应该按真实调用顺序答:参数校验,幂等/重复锁单查询,参团团进度拦截,试算,人群可见和可用校验,组装 UserEntity/PayActivityEntity/PayDiscountEntity,
进入 TradeLockOrderService,责任链拿到 userTakeOrderCount,组装 GroupBuyOrderAggregate,最后 repository 事务落锁单相关记录。
Q2 要明确说出当前已经落地的是 ActivityUsabilityRuleFilter 和 UserTakeLimitRuleFilter,DynamicContext 当前持有 groupBuyActivity 供节点复用;新增渠道黑名单就
应该新增一个 filter Bean,然后在工厂装配顺序里接进去。
Q3 要把“结算事务内写本地消息表”和“结算后尝试立即回调”分开说,并指出 job 是兜底补偿,不是主路径。
Q4 不能只会描述机制,必须主动指出类型转换、并发容器、消息格式校验、监听异常隔离这些工程风险,再给出改法。
第二部分
互动
- 重新回答 Q1,只允许按代码顺序讲,且必须点名这几个字段来源:activityName、validTime、payPrice、notifyUrl、userTakeOrderCount。
- 继续追问 Q2。
如果我现在要求你加一个 ChannelBlacklistRuleFilter,你具体会改哪两个文件?
要求你回答到类名、接入点、放在责任链前还是后、为什么。 - 继续追问 Q3。
isNotify 在这里更可能表示“拼团已完成,需要发货通知”还是“只要支付成功就通知”?你必须结合 /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-
domain/src/main/java/com/SadSunset/domain/trade/service/settlement/TradeSettlementOrderService.java:74 的语义做判断,不要泛答。 - 继续追问 Q4。
如果 @DCCValue 标注的是 Boolean 或 Integer 字段,当前实现会发生什么?你会怎么改,给出一个可落地方案。
答案
- Q1 重新回答
lock_market_pay_order 这条链路是从 MarketTradeController.lockMarketPayOrder 进入的。第一步先从请求里取出 userId、source、channel、goodsId、activityId、
outTradeNo、teamId、notifyUrl,然后做必填参数校验,如果这些关键字段缺失就直接返回非法参数。
第二步,用 userId + outTradeNo 调 tradeOrderService.queryNoPayMarketPayOrderByOutTradeNo 查询是否已经存在未支付订单。如果存在,就直接返回已有锁单结果,这一
步是为了防止重复提交导致重复锁单。
第三步,如果 teamId 不为空,说明是参团场景,就调 tradeOrderService.queryGroupBuyProgress(teamId) 查当前团进度;如果 targetCount == lockCount,说明这个团已经
达到锁单目标,就直接拦截,不再继续占座。
第四步,调用 indexGroupBuyMarketService.indexMarketTrial 进入营销试算,传入的是 userId/source/channel/goodsId/activityId 这几个字段。试算返回
TrialBalanceEntity 后,还会检查 isVisible 和 isEnable,如果用户不在人群范围内或者当前活动不可用,就直接返回。
第五步,Controller 从试算结果里取出 GroupBuyActivityDiscountVO,然后开始组装锁单所需的领域对象。
activityName 来自 trialBalanceEntity.getGroupBuyActivityDiscountVO().getActivityName()。
validTime 来自 trialBalanceEntity.getGroupBuyActivityDiscountVO().getValidTime()。
payPrice 来自 trialBalanceEntity.getPayPrice()。
notifyUrl 不来自试算结果,而是直接来自请求参数 requestDTO.getNotifyUrl()。
然后组装出 UserEntity、PayActivityEntity、PayDiscountEntity。
第六步,进入 TradeLockOrderService.lockMarketPayOrder。这里不会直接落库,而是先执行交易规则链 tradeRuleFilter.apply(...),传入 activityId 和 userId。规则链
执行完之后,会返回 TradeLockRuleFilterBackEntity。
userTakeOrderCount 就是从这里来的,具体是 tradeLockRuleFilterBackEntity.getUserTakeOrderCount(),不是试算给的。
第七步,服务层把 UserEntity、PayActivityEntity、PayDiscountEntity 和 userTakeOrderCount 组装成 GroupBuyOrderAggregate,最后调用 repository 的
lockMarketPayOrder(groupBuyOrderAggregate) 完成锁单事务。
- Q2 继续追问答案
如果现在要加一个 ChannelBlacklistRuleFilter,我会改两个文件:
第一个文件是新增类:
my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/lock/filter/ChannelBlacklistRuleFilter.java
这个类作为新的责任链节点,职责就是根据命令对象里的 channel 或相关渠道信息做黑名单校验。如果命中黑名单,就直接拦截并返回异常;如果通过,就放行到下一个节点。
第二个文件是:
/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/lock/factory/
TradeRuleFilterFactory.java
接入点就是 tradeRuleFilter(...) 这个 bean 装配方法。在这里把新的 ChannelBlacklistRuleFilter 注入进来,然后加入 LinkArmory 的组装顺序。
我会把 ChannelBlacklistRuleFilter 放在责任链前面,优先于 ActivityUsabilityRuleFilter 和 UserTakeLimitRuleFilter。
原因是渠道黑名单属于低成本、快速失败的规则,应该尽早拦截,这样可以减少后续活动查询、用户参与次数校验等开销,也更符合“先做最便宜、最强约束的校验”这个思路。
严格一点说,当前命令对象 TradeLockRuleCommandEntity 里只看到传了 activityId 和 userId,如果黑名单校验必须依赖 channel,那还需要把 channel 一并补进命令对象并
在 TradeLockOrderService.lockMarketPayOrder 传进去。
所以从真实可落地角度看,核心改动文件是这两个,但如果要真正跑通,TradeLockRuleCommandEntity 和调用处也要补字段。
- Q3 继续追问答案
isNotify 在这里更可能表示“拼团已完成,需要发货通知”,而不是“只要支付成功就通知”。
判断依据就是 TradeSettlementOrderService 第 74 行附近的语义:
boolean isNotify = repository.settlementMarketPayOrder(groupBuyTeamSettlementAggregate);
这里 repository 方法不是单纯“支付成功更新订单”,而是“结算拼团交易”。结算完成后返回一个布尔值,再决定是否执行:
if (isNotify) { execSettlementNotifyJob(teamId); }
这说明 isNotify 是结算结果中的一个业务判断开关,表示当前这次结算后,团队状态已经满足外部通知条件了。
如果它的含义是“只要支付成功就通知”,那就没有必要让 repository 返回这个布尔值,服务层直接每次支付成功都发通知就行了。
现在的实现明显不是这样,而是“支付成功后先结算,只有当结算结果判断应该通知时,才按 teamId 立即触发一次通知”。所以它更像“成团完成或达到通知条件”的标志。
- Q4 继续追问答案
如果 @DCCValue 标注的是 Boolean 或 Integer 字段,当前实现大概率会在反射赋值时出问题。
因为现在无论是在初始化阶段,还是收到 Redis topic 消息后,代码里拿到的值本质上都是 String。然后直接:
field.set(targetBeanObject, setValue)
或者
field.set(objBean, value)
如果字段真实类型是 Boolean、Integer,这里把 String 直接塞进去,就会抛 IllegalArgumentException,典型错误就是“Can not set java.lang.Boolean field xxx to
java.lang.String”。
可落地改法:
第一步,增加一个统一的类型转换方法,比如 convertValue(Field field, String rawValue)。
根据 field.getType() 判断目标类型,把字符串转成对应对象。
示例思路:
- String.class 直接返回原值
- Boolean.class / boolean.class 用 Boolean.parseBoolean(rawValue)
- Integer.class / int.class 用 Integer.parseInt(rawValue)
- Long.class / long.class 用 Long.parseLong(rawValue)
第二步,在两个赋值位置都改成先转换再反射赋值:
- postProcessAfterInitialization 里初始化字段时
- Redis topic listener 收到更新消息时
第三步,转换失败不能直接把监听线程打崩。
我会在监听逻辑里 catch 转换异常,打印带 key、字段名、原始值的错误日志,然后拒绝这次更新,不覆盖旧值。
第四步,最好把本地缓存从 HashMap 换成 ConcurrentHashMap,避免并发问题。
如果让我给一个更完整但仍然轻量的方案,我会这样做:
- Map<String, Object> 换成 ConcurrentHashMap<String, Object>
- 新增 private Object convertValue(Field field, String rawValue) 方法
- 初始化和监听更新统一走这个转换方法
- 转换失败只记录日志,不抛出未捕获运行时异常
- 对消息格式先校验,再 split,避免脏消息直接数组越界
你面试时可以这样答:
当前实现默认把 Redis 里的配置值按字符串直接反射写回字段,所以如果字段是 Boolean 或 Integer,会在 field.set 时发生类型不匹配异常。我的改法是加一个统一类型转
换层,按字段类型把字符串转换成目标类型后再赋值,并把这套逻辑同时用在初始化和消息监听两个入口。另外我会把本地缓存改成 ConcurrentHashMap,并把转换失败做成日志
告警而不是直接打崩监听线程。
逐题复盘
- Q1
candidateAnswerSummary: 说到了三段链路和部分目的,但没按真实代码顺序回答,也没把字段来源讲清。
evaluation: partial
whatWasMissing:
- 控制层前置校验顺序
- 为什么先查未支付订单
- 为什么查拼团进度
- PayActivityEntity / PayDiscountEntity 的字段来源
- userTakeOrderCount 来自责任链而不是试算
evidence: - /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:64
- /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:71
- /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:90
- /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/MarketTradeController.java:121
- /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/lock/TradeLockOrderService.java:46
standardAnswer: - 入口先做必填参数校验
- 用 userId + outTradeNo 查未支付订单,避免重复锁单
- 如果是参团,查团队进度,已到锁单目标则直接拦截
- 调用试算服务拿活动优惠和价格信息
- 校验人群是否可见、是否可参与
- 组装 UserEntity、PayActivityEntity、PayDiscountEntity
- 进入锁单领域服务,责任链补齐 userTakeOrderCount
- 组装 GroupBuyOrderAggregate,交给 repository 做事务落库
answerTemplate: 这条链路先在 Controller 做参数校验,然后先查未支付订单做幂等保护,再对参团场景检查团进度,避免目标人数已满还继续锁单。之后调用试算服务拿到
活动和价格信息,校验人群是否可见可参与,再组装用户、活动、优惠三个实体进入锁单领域服务。领域服务内部先走责任链,拿到用户已参与次数,再和前面的实体一起组装
成聚合对象,最后由 repository 在一次事务里完成锁单相关数据落库。
- Q2
candidateAnswerSummary: 说对了责任链比 if-else 更利于扩展,但没说出当前具体节点和上下文字段。
evaluation: partial
whatWasMissing:
- 当前已经落地的 filter 名称
- DynamicContext 当前具体持有什么
- 新增规则如何接入工厂装配
evidence: - /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/lock/factory/
TradeRuleFilterFactory.java:29 - /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/lock/factory/
TradeRuleFilterFactory.java:43
standardAnswer: - 当前工厂装配了 ActivityUsabilityRuleFilter 和 UserTakeLimitRuleFilter
- 命令对象负责入参,上下文负责节点间共享中间态
- 当前上下文里至少有 groupBuyActivity
- 新规则新增 filter 类,再在工厂装配顺序中接入
answerTemplate: 这里用责任链不是为了炫技,而是把每条校验规则拆成独立节点。命令对象只带本次请求的必要参数,比如活动 ID 和用户 ID;上下文对象负责在节点之间
传递中间态,当前代码里就有 groupBuyActivity。现在已经落地的是活动可用性校验和用户参与次数校验。如果我要加渠道黑名单,我会新增一个
ChannelBlacklistRuleFilter,然后在 TradeRuleFilterFactory 里把它接到链上,避免把原有逻辑揉回一个大 if-else。
- Q3
candidateAnswerSummary: 知道有本地消息表和 job 补偿,但没讲清即时通知与定时补偿的关系,也没识别状态命名歧义。
evaluation: weak
whatWasMissing:
- settlementMarketPayOrder 内是否会立即触发通知
- execSettlementNotifyJob(teamId) 和全量 job 的区别
- isNotify 的业务含义
- notifyCount < 5 对应状态命名有歧义
evidence: - /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/settlement/
TradeSettlementOrderService.java:74 - /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/settlement/
TradeSettlementOrderService.java:77 - /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/settlement/
TradeSettlementOrderService.java:93 - /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/settlement/
TradeSettlementOrderService.java:122
standardAnswer: - 结算完成后 repository 返回是否需要通知
- 需要通知时会先尝试按 teamId 立即执行一次
- 定时任务负责扫未完成任务做补偿
- 成功、失败、重试终态需要清晰状态机
- 当前 error/retry 命名可能有歧义,需要收敛
answerTemplate: 这套方案不是只有 job 补偿,而是结算后先根据 isNotify 判断是否要立即通知。如果需要,会先按当前 teamId 执行一次通知;如果失败或者当时没成
功,再由定时任务扫未完成任务补偿。这样主链路和补偿链路是并存的。当前实现里 notifyCount < 5 对应的状态更新命名有歧义,我会进一步统一状态语义,比如
waiting、retrying、success、dead。
- Q4
candidateAnswerSummary: 前半段机制说明基本正确,后半段没有风险意识。
evaluation: weak
whatWasMissing:
- 非字符串字段赋值风险
- HashMap 线程安全问题
- 消息格式校验问题
- 监听线程异常隔离问题
evidence: - /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:35
- /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:45
- /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:73
- /D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/config/DCCValueBeanFactory.java:77
standardAnswer: - 启动后扫描 @DCCValue 字段并初始化默认值
- AOP 代理要剥离,否则拿不到真实字段
- Redis Topic 收到消息后通过 key 找 bean,再用反射改字段
- 风险包括类型转换、并发安全、消息格式、异常隔离、配置一致性
answerTemplate: 这个组件在 Bean 初始化后扫描带 @DCCValue 的字段,把配置 key 和真实 bean 建立映射,同时从 Redis 取默认值或已有值写入字段。之所以要处理
AOP,是因为代理类本身没有真实业务字段。收到 Redis 广播后,它通过 key 找到 bean,再反射更新字段。问题在于当前实现默认所有字段都按字符串写入,HashMap 也不是
并发安全的,消息格式校验和监听异常隔离也不够,线上我会补类型转换器、并发容器、报文校验和失败降级机制。
高频薄弱点
- 回答容易停在概念层,落不到代码细节。
- 对字段来源、状态流转、方法顺序不够敏感。
- 风险意识偏弱,不会主动挑实现问题。
- 用词有点大,和当前支撑证据不完全匹配。
立刻可执行的改进建议
- 只练这一个项目,按“接口入口 -> service -> factory/filter -> repository -> 状态变化”写一份自己的口述稿。
- 每条主链路至少能回答清楚 5 个点:入口参数、前置校验、核心实体、事务落点、失败处理。
- 把这几个类反复讲熟:/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-trigger/src/main/java/com/SadSunset/trigger/http/
MarketTradeController.java、/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-domain/src/main/java/com/SadSunset/domain/trade/service/lock/
service/settlement/TradeSettlementOrderService.java、/D:/develop/javaproject/my-group-buy-market/my-group-buy-market-app/src/main/java/com/SadSunset/
config/DCCValueBeanFactory.java。 - 简历里把“后端负责人”改成更稳的表达,除非你真的能把关键实现完全扛住。
- 下次面试遇到不会的,不要硬泛化,直接说“这段我主要负责 X,Y 我知道大概机制,当前代码里落在某某类”。