Spring AOP 调用自身失效
钟意
·
2025-03-23
·
via 钟意博客
错误情景
- 环境:
- 操作:
- 顶层方法分析所选数据源
- 切换数据源
- 调用对应查询
- AOP代理失效导致多数据源切换失败
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| /** * 数据库元数据服务实现 */ @Service public class DataBaseMetaService implements IDataBaseMetaService { /** * 获取输入库表在数据库里的字段注释 */ @Override public List<DataBaseMateField> selectTableFieldsByScope(String dbName, String tableName) { List<DataBaseMateField> dataBaseMetaFields = ListUtil.empty(); switch (dbName) { case "xxx": dataBaseMetaFields = byMaster(tableName); break; case "xxxxxx": dataBaseMetaFields = byMonitorHm(tableName); if (CollectionUtils.isEmpty(dataBaseMetaFields)) dataBaseMetaFields = byMonitorWce(tableName); break; default: break; } return dataBaseMetaFields.stream().peek(field -> field.setFieldName(StringUtil.toCamelCase(field.getFieldName())) ).collect(Collectors.toList()); } @DS("a") public List<DataBaseMateField> byMaster(String tableName) {} @DS("b-a") public List<DataBaseMateField> byMonitorHm(String tableName) {} @DS("b-b") public List<DataBaseMateField> byMonitorWce(String tableName) {} }
|
错误诱因
Spring AOP 原理:@DS 依赖 AOP 代理,而 this.xxx() 直接调用自身方法,不会经过代理对象。
JDK 代理与 CGLIB 代理的区别:默认情况下,@Transactional 和 @DS 这种 AOP 机制都是基于代理的,需要从代理对象调用方法才能生效。
解决方案
方式 1:使用 @Lazy 注解注入自身(推荐)
1 2 3
| @Lazy @Autowired private DataBaseMetaService self;
|
这样 self.byXXX() 实际是从代理对象调用,从而触发 AOP,确保 @DS 切换数据源生效。
方式 2:通过 AopContext 获取代理对象
1 2
| DataBaseMetaService proxy = (DataBaseMetaService) AopContext.currentProxy(); proxy.byMaster(tableName);
|
需要开启 exposeProxy = true,在 application.yml 配置:
1 2 3 4
| spring: aop: proxy-target-class: true expose-proxy: true
|
但这种方式代码侵入性较强,不如方式 1 优雅。
方式 3:将 byXXX() 方法抽取到另一个 @Service。这样 @DS 标注的方法始终在被代理对象上执行,避免 this 调用导致 AOP 失效。
结尾
我这里选择第一种解决方案,注入自身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| /** * 数据库元数据服务实现 */ @Service public class DataBaseMetaService implements IDataBaseMetaService { // 延迟注入自身,确保 @DS 生效 @Lazy @Autowired private DataBaseMetaService self; private final DataBaseMetaMapper dataBaseMetaMapper; public DataBaseMetaService(DataBaseMetaMapper dataBaseMetaMapper) { this.dataBaseMetaMapper = dataBaseMetaMapper; } /** * 获取输入库表在数据库里的字段注释 */ @Override public List<DataBaseMateField> selectTableFieldsByScope(String dbName, String tableName) { List<DataBaseMateField> dataBaseMetaFields = ListUtil.empty(); switch (dbName) { case "xxx": dataBaseMetaFields = self.byMaster(tableName); break; case "xxxxxx": dataBaseMetaFields = self.byMonitorHm(tableName); if (CollectionUtils.isEmpty(dataBaseMetaFields)) dataBaseMetaFields = self.byMonitorWce(tableName); break; default: break; } return dataBaseMetaFields.stream().peek(field -> field.setFieldName(StringUtil.toCamelCase(field.getFieldName())) ).collect(Collectors.toList()); } @DS("a") public List<DataBaseMateField> byMaster(String tableName) {} @DS("b-a") public List<DataBaseMateField> byMonitorHm(String tableName) {} @DS("b-b") public List<DataBaseMateField> byMonitorWce(String tableName) {} }
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。