ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot3外部化配置与AOP实战:智慧社区报修平台踩坑总结

SpringBoot3外部化配置与AOP实战:智慧社区报修平台踩坑总结 项目标题听起来像是个标准的企业级练手项目但真正做起来才发现坑全在细节里。SpringBoot3出来之后很多人还停留在Boot2的思维定式里外部化配置和AOP这两块看似基础放到真实业务场景里却有一堆值得掰扯的地方。我这篇文章就结合自己落地智慧社区报修平台的经验把原理讲透把代码贴全再把踩过的坑一个个列出来希望能帮你少走弯路。1. 从一次真实改造说起为什么用SpringBoot3重构报修平台1.1 报修平台在小区里到底要解决什么问题先交代一下项目背景。智慧社区报修平台说白了就是服务小区业主、物业、维修工三方的工单系统。业主通过小程序或App提交报修需求灯泡坏了、水管漏水、电梯异常物业端收到工单后进行派单维修工接单、上门、处理、反馈最后业主确认完成并评价。这个业务模型听起来不复杂但真正落地时涉及的场景不少工单状态机流转待指派、待接单、处理中、已完成、已取消、已评价、消息通知微信模板消息、短信、站内信、图片上传破损部位照片、费用结算部分维修项目需要收费、评价体系。还有一个经常被忽略的点不同小区的物业公司对报修流程的管理规则不一样有的小区要求先审批再派单有的小区维修工可以自己抢单这就需要系统在流程配置层面有足够的灵活性。我选择SpringBoot3来重构这套系统主要看中两个点一是SpringBoot3基于Jakarta EE 9标准全面拥抱Java 17及以上版本长期来看更符合技术演进方向二是SpringBoot3底层是Spring Framework 6在配置绑定、AOTAhead-of-Time处理、可观测性方面都做了大量增强。对于一个要持续迭代的报修平台来说这些特性是实打实有用的。1.2 技术选型SpringBoot3带来的核心增量先说说SpringBoot3到底比2.x强在哪这直接决定了你的老旧代码迁移成本。最直观的变化是javax包名全部换成了jakarta。比如javax.servlet.http.HttpServletRequest变成了jakarta.servlet.http.HttpServletRequest这个改动虽然机械但涉及面极广第三方库不兼容的地方特别多项目里凡是引入的老版本依赖都可能报ClassNotFoundException。SpringBoot3还默认使用Spring Framework 6这带来一个关键能力对GraalVM Native Image的原生支持。虽然报修平台暂时不需要演示级启动速度但考虑到社区版可能要部署到边缘网关这类资源受限的设备上原生镜像这个选项留着了。配置方面SpringBoot3延续并强化了外部化配置机制配置优先级、ConfigurationProperties绑定校验都有增强这在多环境部署时非常关键。AOP方面SpringBoot3默认依旧用Spring AOP基于代理和AspectJ的集成方式与Boot2差不多但内部代理机制在JDK动态代理和CGLIB实际上是ByteBuddy之间的选择策略更清晰了——SpringBoot2.x时代默认支持proxyTargetClass trueSpringBoot3基本全程走CGLIB式代理这对处理接口代理和类代理的差异很重要。给个结论如果是从零开始做新项目直接用SpringBoot3如果是从Boot2老项目升级可以分步走先升级到SpringBoot2.7兼容层再一步切换到3.x别跨大版本硬跳。2. 外部化配置把报修平台从“写死”变成“可运营”2.1 配置项规划哪些该进配置哪些不该外部化配置的核心思想是把应用运行时行为相关的参数从代码里剥离出去放到环境变量、配置中心或外部配置文件中。报修平台这种面向多小区的SaaS型系统配置项尤其多我建议按以下维度分类。第一类是部署级配置包括服务端口、数据库连接串、Redis地址、消息队列地址。这些配置在不同环境开发、测试、生产下完全不同且往往涉及敏感信息必须放在环境变量或K8s ConfigMap/Secret中。第二类是业务级配置包括工单超时时间、自动取消时长、免费维修金额阈值、通知重试次数、图片上传大小限制。这些配置需要能动态调整且不重启应用最好走配置中心Nacos、Apollo。第三类是功能开关配置比如是否开启短信通知、是否允许业主自助取消待处理工单、维修抢单还是派单模式。这类配置如果用Value硬编码的方式管理改一次就要重新发版非常痛苦。我实际规划报修平台配置时在application.yml里只留了应用名和环境标识其余全部外部化。具体做法是遵循SpringBoot默认的配置优先级优先使用环境变量和外部/config目录下的配置文件。这里有个容易踩的坑SpringBoot的配置优先级是命令行参数 Java系统属性 操作系统环境变量 application-{profile}.ymlapplication.yml 内嵌默认配置。如果你的application.yml里写了server.port8080但在K8s的Deployment里通过环境变量SERVER_PORT设置了9090实际生效值取决于你用的是哪种部署方式。实际操作中我习惯在镜像里不打包任何环境相关的配置文件全部由部署平台注入。2.2 ConfigurationProperties绑定与多环境支撑外部化配置不只是把值放到配置文件里更重要的是让代码在运行时拿到结构化的配置对象。Value注解虽然简单但如果配置项多代码会变得极其散乱而且无法做类型校验和复杂结构绑定。我更推荐用ConfigurationProperties做配置绑定。报修平台里有大量业务参数比如工单规则Data Component ConfigurationProperties(prefix repair.order) public class RepairOrderProperties { /** 超时未接单自动取消时间分钟 */ private Integer autoCancelTimeout 60; /** 免费维修金额阈值超过后需要业主确认 */ private BigDecimal freeRepairThreshold new BigDecimal(50.00); /** 允许业主取消的工单状态 */ private ListString cancellableStatus List.of(PENDING_ASSIGN, PENDING_ACCEPT); /** 同小区24小时内重复报修风控阈值 */ private Integer duplicateWithinHours 24; /** 维修工每日最大接单数 */ private Integer maxDailyOrders 20; }这样写的好处是类型安全写配置文件时IDE有提示启动时如果类型不对会直接报错结构清晰所有工单规则都在一个类里维护支持校验可以直接加Validated和NotNull等校验注解。多环境配置我用了两套方案配合。一套是SpringBoot原生的application-{profile}.yml机制适用于环境差异较小的场景。比如application-dev.yml里数据库连本地application-prod.yml里数据库连云上RDS。另一套是配置中心方案适用于配置经常变动的场景。报修平台在小区数量多起来之后每个小区可能有独立的免费维修金额阈值或取消策略这种配置再放到jar包里就不合适了。我的做法是在application.yml里通过spring.config.import引入配置中心。SpringBoot3的spring.config.import机制比旧版的spring.cloud.config.enabled更直接比如spring: application: name: repair-platform config: import: nacos:repair-platform.yaml?groupDEFAULT_GROUPrefreshEnabledtrue这样配置中心里修改工单超时时间不需要重新发版业务侧能即时感知。2.3 部署级配置的加载优先级与安全处理部署级配置牵扯到安全问题这块在报修平台里特别容易翻车。数据库密码、Redis密码、对象存储的AccessKey、短信平台的API密钥这些全部是敏感信息。先说密码怎么管理。直接在application-prod.yml里写明文密码是绝对的禁忌。我当时的方案是数据库密码和短信密钥走环境变量注入应用代码中不做任何默认值兜底。同时在K8s环境里配合Secret进行挂载而不是直接在Deployment里明文写。spring.datasource.password这类敏感项在配置中心里也可以做加密存储。Nacos和Apollo都有插件机制入站时解密。但要注意如果配置中心本身做不了加密至少把配置中心放到内网安全区域通过网络安全策略隔离。再说配置优先级。我在生产环境调试时遇到过几次“改了半天配置不生效”的情况后来养成了一个习惯排查生效配置时直接查SpringBoot的ConfigDataEnvironmentPostProcessor行为或者用/actuator/env端点看配置来源。比如一个配置项可能在环境变量、外部配置文件和配置中心里都出现了到底谁生效光靠猜测会浪费时间。另外外部化配置也涉及编码问题。配置文件用UTF-8是常识但Windows环境上如果用了GBK编码的配置文件加载中文注释会直接乱码。这个问题在内部开发机上容易出现我建议团队统一采用UTF-8无BOM格式并在CI流水线里强制校验文件编码。3. AOP在报修平台里的落地从日志到工单通知3.1 AOP的核心原理代理、切点、通知AOP一直是Spring体系里的明星概念但很多人停留在“会用Around打日志”的层面上原理讲不清楚。要真正用好AOP至少得明白三件事代理机制、切点表达式、通知类型。代理机制上Spring AOP默认使用运行时增强也就是通过代理对象包装目标Bean。SpringBoot3在默认配置下如果目标类实现了接口走JDK动态代理还是CGLIB代理其实Boot2.x后期和Boot3的做法已经基本一致优先使用CGLIB式类代理也就是proxyTargetClasstrue默认开启。这意味着即使你的业务类没有接口也能被代理增强。切点表达式就是定位连接点的规则。报修平台里最常用的几个// 匹配某个包下所有类的方法 execution(* com.community.repair.service..*.*(..)) // 匹配自定义注解标注的方法 annotation(com.community.repair.aspect.annotation.OperationLog) // 匹配某个类的方法 within(com.community.repair.controller.RepairOrderController)通知类型有五种Before前置、AfterReturning返回后、AfterThrowing异常后、After最终、Around环绕。Around是功能最强的也是我用的最多的因为它能控制方法执行的整个过程包括获取入参、修改返回值、捕获异常、控制是否放行。报修平台里AOP有非常典型的应用场景用户操作日志切面、工单状态变更通知切面、接口耗时监控切面。这三个切面能用好AOP实战基本就过关了。3.2 自定义注解实现工单操作审计先看需求小区物业合规性要求很高谁在什么时间修改了工单状态、谁把工单指派给了哪个维修工、谁关闭了工单这些操作必须留痕。如果每个方法里手动写日志代码逻辑会侵入性很强而且容易漏。我的方案是自定义一个OperationLog注解配合AOP切面统一记录。先定义注解Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface OperationLog { /** 操作模块如工单管理、用户管理 */ String module() default ; /** 操作类型CREATE / UPDATE / CANCEL / ASSIGN */ String action() default ; }再写切面Aspect Component Slf4j public class OperationLogAspect { private final OperationLogService operationLogService; public OperationLogAspect(OperationLogService operationLogService) { this.operationLogService operationLogService; } Around(annotation(operationLog)) public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable { long start System.currentTimeMillis(); Object result; boolean success true; String errorMsg null; try { result joinPoint.proceed(); return result; } catch (Throwable throwable) { success false; errorMsg throwable.getMessage(); throw throwable; } finally { long cost System.currentTimeMillis() - start; saveOperationLog(joinPoint, operationLog, success, errorMsg, cost); } } private void saveOperationLog(ProceedingJoinPoint joinPoint, OperationLog operationLog, boolean success, String errorMsg, long cost) { // 这里通过RequestContextHolder拿到当前登录人信息 // 通过解析joinPoint.getArgs()拿到工单ID等信息 // 构造OperationLogDTO异步写入数据库或消息队列 } }用起来的时候在Service或Controller方法上加一行注解就行OperationLog(module 工单管理, action ASSIGN) public RepairOrder assignOrder(Long orderId, Long workerId) { // 核心业务逻辑 }这里有几个细节需要注意。第一日志和主流程要解耦。我在写操作日志时用的方法在finally块里执行且内部做了异常捕获确保日志写入失败不能影响主业务。更稳妥的做法是发送到MQ或线程池异步写入反正操作日志允许秒级延迟。第二入参解析要小心。joinPoint.getArgs()拿到的是所有参数数组如果方法参数里有MultipartFile这类大对象直接序列化会内存溢出。我的做法是先判断参数类型白名单类才做JSON序列化其他类型的只记录类型名。第三切面本身不能有状态。AOP切面默认是单例的如果你在切面里定义了可变的成员变量并发情况下会出问题。所有状态都通过方法参数传递和局部变量存储这算是基本功但依然值得强调。3.3 AOP增强报修通知与耗时监控报修平台通知链路比较长业主提交工单 - 通知物业管理员物业派单 - 通知维修工维修工处理完成 - 通知业主验收。如果这些通知逻辑直接散落在Service代码里改一处就要动一大片。用AOP可以做一个比较干净的“旁路”通知中心。我的想法是用Spring的事件机制配合AOP把业务和通知彻底分离。Service层只管工单状态流转状态变更后通过自定义事件发布事件AOP切面监听关键方法在方法执行成功后发布事件再由事件监听器异步发送通知。举例来说工单状态变更为已完成时系统要给业主推送“维修完成请评价”的模板消息。业务流程代码里不用写任何通知逻辑只要在状态流转方法上加注解NotifyEvent(eventId ORDER_FINISHED)由切面统一处理Aspect Component public class NotifyEventAspect { AfterReturning(pointcut annotation(notifyEvent), returning result) public void afterReturning(JoinPoint joinPoint, NotifyEvent notifyEvent, Object result) { // 根据notifyEvent.eventId()查找对应的通知配置 // 从result中提取必要的通知参数如orderId、业主phone // 调用NotifyExecutor执行短信、微信、站内信发送 } }这样做的好处是通知的目标接收入可以根据业务配置动态调整比如有的小区微信通知为主有的必须短信为主这些都能通过外部化配置控制而不用改Service代码。耗时监控也比较直接。业主提交报修后工单列表查询接口如果慢体验会很差。我在Controller层的查询方法上加了MonitorTime(threshold 800)注解超过阈值就记录慢请求日志并将traceId关联到整个调用链。Aspect Component Slf4j public class TimeMonitorAspect { Around(annotation(monitorTime)) public Object around(ProceedingJoinPoint joinPoint, MonitorTime monitorTime) throws Throwable { long start System.currentTimeMillis(); try { return joinPoint.proceed(); } finally { long cost System.currentTimeMillis() - start; if (cost monitorTime.threshold()) { log.warn([SLOW-API] {} cost {}ms, threshold {}ms, joinPoint.getSignature().toShortString(), cost, monitorTime.threshold()); } } } }这套AOP组合拳落地后代码里几乎没有重复的日志和通知逻辑新增一个工单操作只需要保证方法上加对注解、方法返回值包含必要信息即可。4. 常见问题排查与避坑实录4.1 外部化配置不生效的几种典型情况做这个项目时我在外部化配置上栽过几个跟头挑典型的说说。第一个坑是配置中心配置刷新不生效。SpringBoot3配合Nacos时如果ConfigurationProperties绑定的是Bean需要在配置类上加上RefreshScope注解才能动态刷新。但问题是加了RefreshScope后Beand的生命周期会变某些情况下会导致代理失效。我的处理方式是只对需要动态刷新的配置类加RefreshScope其他配置保持单例。第二个坑是环境变量命名规则。SpringBoot的环境变量绑定遵循“宽松绑定”规则repair.order.auto-cancel-timeout对应的环境变量是REPAIR_ORDER_AUTOCANCELTIMEOUT下划线有时候会把你绕晕。我遇到过在K8s Deployment里配置了REPAIR_ORDER_AUTO_CANCEL_TIMEOUT连字符和下划线混用后绑定不上实际值永远是默认值。排查半天才发现是环境变量拼写问题。第三个坑是配置文件编码和格式。YAML文件里如果有特殊字符比如中文引号、JSON字符串值里的大括号很容易在覆盖或者合并时出现解析错误。我的经验是复杂配置值用单引号包裹避免被YAML解析成嵌套对象。第四个坑是配置校验不到位。报修平台的生产环境曾经出现过一次配置错误运维把免费维修金额阈值误配成了500元导致原本应该收费的维修订单全部被当成免费处理。这个问题的根源是配置加载时没有做校验。加上Validated之后启动时就会校验配置值是否在合理范围内而不是等到运行期才暴露问题。4.2 AOP不生效自调用、代理类型、类加载器问题AOP不生效是Spring开发者最常见的噩梦。报修平台里我总结出的原因基本就这几类。第一类自调用导致切面失效。同一个类里的方法A调用方法B如果B是被AOP增强的直接this.methodB()调用时走的是this对象而不是代理对象切面逻辑不会执行。比如在工单Service里assignOrder()内部调用了sendNotifyMessage()即使sendNotifyMessage()标注了自定义切点也不会触发切面。解冑办法有两种一是在需要被切面拦截的方法所在类中注入自己的代理对象Lazy注入或AopContext.currentProxy()最好的方式是把需要走代理的方法放到另一个独立的Bean中通过Spring容器加载目标对象自然走代理。第二类切点表达式没匹配上。报修平台上这个坑最隐蔽。execution(* com.community.repair.service..*.*(..))这个表达式里的..只匹配子包不匹配当前包本身。如果你的Service直接放在com.community.repair.service包下而切点表达式用的是service..*不会匹配到。用service.*.*才能匹配当前包。这类问题通过启动时打印切面匹配日志会容易排查。第三类代理类型导致ClassCastException。如果目标类代理方式变了JDK动态代理转CGLIB代码里强转类型时可能出问题。SpringBoot3默认是基于类代理如果代码里对接口强转为实现类在代理模式下会抛异常。我的建议是业务代码永远依赖于抽象接口设计最小化代理类路径的直接类型转换。第四类类加载器问题。在SpringBoot的Fat Jar模式下如果引入了自定义ClassLoader切面类可能和目标类在不同的类加载器加载导致切点匹配不到。这个在常规业务系统里不常见但如果用了热部署插件如JRebel或自定义插件体系大概率会遇到。4.3 性能与维护上的几个坑AOP也不是零成本。报修平台在刚上线时出现过接口响应时间暴涨的问题。排查后发现其中一个切面对所有Controller方法都做了Around增强且在切面里同步调用了MQ生产者发送消息导致每个请求都要等待MQ响应。这个教训说明切面越通用触发次数越多性能影响就越明显。优化方案有两个方向。一是缩小切点范围。能用annotation限定就绝不用execution扫描大范围包。比如只对标注了OperationLog的方法做日志切面而不是对整个Controller包做切面。二是把耗时操作异步化。切面里如果必须执行IO操作写日志、发消息、调用外部接口一定要异步执行。我最终把日志写入全部改成发送到内存中的队列由独立线程池消费接口链路彻底不感知日志写入时间。还有一个维护层面的坑AOP切面开发和调试时建议在开发环境开启Spring AOP日志日志会打印每个Bean被哪些切面代理。高版本Spring Boot中可以通过配置logging.level.org.springframework.aopDEBUG和logging.level.org.springframework.context.annotationTRACE。版本兼容问题也必须提。SpringBoot3的AOP依赖是spring-boot-starter-aop它默认引入的是AspectJ Weaver 1.9.x。如果你项目里还有其他依赖也引用了旧版AspectJ可能出现版本冲突导致切点表达式解析异常。我在报修平台里就碰到过接了某个第三方SDK后项目里同时存在AspectJ 1.8和1.9启动时直接报NoSuchMethodError。解决方式是统一使用SpringBoot3 BOM管理的AspectJ版本移除多余的旧版依赖。5. 配置与AOP联动让报修规则可编排5.1 用外部配置控制AOP切面行为既然讲完了外部化配置和AOP不妨说说两者怎么联动。报修平台里有一个很实际的需求不同的小区对操作审计的严格程度不一样有的小区只需要记录工单交接和完成操作有的小区要求所有操作全记录。如果只靠写死的注解不太好做到按租户动态调整。我的做法是给OperationLogAspect增加一个配置项定义哪些操作类型需要走审计逻辑。切面在执行保存日志之前先查询配置中心里的策略根据策略白名单判断当前操作类型是否需要记录。repair: audit: enabled: true actions: CREATE,ASSIGN,ACCEPT,FINISH,CANCEL audit-mode: strict切面逻辑调整为if (!auditProperties.isActionEnabled(operationLog.action(), currentCommunityId)) { return; }这样灵活度就高了一些。就算后续加了新的操作类型只要在配置中心加上动作标识不需要动代码。5.2 动态开关与降级设计配置中心和AOP联动还能做另一件事故障降级。报修平台有一次短信服务商接口出现抖动导致通知切面里发短信的线程大量堆积。我后来给通知切面加了配置项repair.notify.sms.enabled短信服务异常时运维在配置中心把这个开关置为false通知切面就不再发短信只走微信和站内信。整个过程不需要重启服务切面会根据配置实时判断是否执行。这个设计背后的思路是外部化配置不只是为了多环境部署更重要的是给系统增加运行时动态调整的能力。AOP切面作为业务逻辑的旁路最适合接入这类开关控制。主流程不受影响附加能力可分可合。类似的开关还可以用数据库慢查询时临时关闭耗时的XLSX导出切面运营大促期间临时关闭某些操作类型的审计日志流量减轻数据库压力对接第三方接口时临时修改拦截规则放行或拦截指定类型的请求。5.3 配置漂移与切面行为一致性配置和AOP联动的坑在于配置漂移。比如配置中心里说audit-mode: strict但开发环境、测试环境、生产环境配置不一致导致切面行为在不同环境不一样测试没发现问题一上生产就出了岔子。我的解决办法是引入配置一致性检查。在应用启动时通过ConfigurationProperties绑定的配置对象做一次校验如果配置为空或不在合法枚举范围内直接启动失败而不是静默使用兜底默认值。这样配置错误能尽早暴露。另外一个细节把配置值传入切面时注意不要直接在切面类里Autowired配置对象后缓存到本地字段。因为配置中心刷新后原对象会重新绑定但如果你在切面里缓存了副本就感知不到配置变化。正确的做法是每次使用配置时都从配置对象中实时获取确保拿到的是最新值。6. 部署层面的配置与切面验证6.1 使用Actuator验证配置生效配置和AOP都做完之后部署上线前的验证很关键。SpringBoot3的Actuator组件提供了/actuator/env端点可以查看当前应用运行时每一个配置项的来源和值。我在报修平台的部署流水线里加了一步默认开启management.endpoints.web.exposure.includeenv,health,metrics部署完成后通过/actuator/env/repair.order.autoCancelTimeout这样的子端点直接检查配置是否生效。还有一个很实用的小工具/actuator/beans。如果AOP代理失败你会发现Bean的类型不是代理类型而是原始类型。通过这个端点可以快速判断某个Service是否被正确代理。6.2 切面日志链路追踪报修平台上线后排查问题时发现一个痛点一个工单操作从Controller到Service到MQ消费链条很长如果每个模块都单独打日志没法把同一笔操作的日志串起来。我的方案是在AOP切面里统一生成traceId并放入SLF4J的MDCMapped Diagnostic Context这样所有日志输出都自动携带traceId。Around(annotation(operationLog)) public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable { String traceId UUID.randomUUID().toString().replace(-, ); MDC.put(traceId, traceId); try { return joinPoint.proceed(); } finally { MDC.remove(traceId); } }设置Logback日志格式时加上%X{traceId}即可pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId}] %msg%n/pattern这个设计让团队在排查线上问题时效率明显提升。之前是用户报了个工单ID问东问西半天查不到完整操作链路现在只要拿到traceId一条日志拉出来操作人、时间、耗时、出入参、异常信息全部在一个上下文里。6.3 CI/CD中的配置与代码管理最后谈一下配置和代码在CI/CD中的协作。报修平台的代码仓库里我强制规定只保留application.yml和application-dev.yml其余环境配置文件一律不提交到代码仓库。测试环境和生产环境配置通过配置中心管理并在部署时以外部文件形式挂载。这样做的原因是版本管理的边界问题。数据库密码、RDS地址这些信息属于基础设施配置应该归运维管而工单超时时间、免费维修金额这些属于业务配置可以由开发维护。把两者混在一个文件里提交容易出现敏感信息泄露。配置模板文件倒是可以放到代码库里比如application-prod.example.yml里面用占位符代替真实值方便新增的开发者了解一个完整的环境配置需要哪些项。在实际部署脚本中我会用这样一个原则镜像里不放任何环境相关的配置所有配置由部署平台注入。这样同一个镜像无论部署到哪个环境行为都是确定的。配置错误时快速回滚也容易只需要切换到上一套配置不用重新打包镜像。AOP切面的验证则放在集成测试阶段。我写了一个专门的切面测试用例使用真实Spring容器启动断言目标方法被调用后切面生效比如Mock的日志Service收到了一条日志。这一步不能省因为静态代码分析替代不了真实容器行为。写在最后回到开始说的那几块绊脚石。外部化配置的核心不是“把application.yml拆开”而是想清楚配置的分层、来源、校验和动态刷新机制让部署环境负责自身差异让业务代码通过类型安全的组件访问配置。AOP的核心不是“写几个注解和切面就完事”而是理解Spring容器管理的对象边界管理好代理边界、并发边界和性能边界。报修平台这个项目做到现在给我的最大体会是大量繁琐的重复逻辑是AOP的展示舞台而真正让系统稳定运行的是每次配置变更前多想一步校验和回滚方案。如果你也在做类似的项目建议先从配置项分类和AOP切面清单入手把规则定清楚再动手编码。代码写起来很快坑摸清不容易。
返回列表