/**
* 获取合同 Code 管理策略 <br>
* 获取失败抛出异常 <br>
*
* @param contractTemplateCode 合同模板 Code
* @return 合同 Code 管理策略
*/
private ContractCodeManageStrategy requireStrategy(ContractTemplateCode contractTemplateCode) {
if (!manageStrategyMap.containsKey(contractTemplateCode)) {
throw new SystemException(
String.format(
"无法找到指定合同模板的合同 Code 管理策略, contractTemplateCode = %s", contractTemplateCode.name()));
}
ContractCodeManageStrategy strategy = manageStrategyMap.get(contractTemplateCode);
return strategy;
}
同事求助说调用生成合同时报错,异常原因是因为manageStrategyMap数据为空才导致抛出异常。以下为本次排查步骤:
1.该map只有一处初始化地方,如下:
2.register()是实现于Registry接口,此外接口上有一个自定义注解RegistryConf,注解使用是在RegisterApplicationListener中的verifyRepeatableScope()
3.断点该函数,发现collect后的数据为空,导致后续填充数据的循环没有执行,继续向前断点,发现registrantClass.getAnnotationsByType(RegisterTo.class)竟然获取不到任何数据,通过查看代码RegisterTo.class在该类有配置,开始以为是java8的重复注解的问题,遂更换getAnnotationsByType为getDeclaredAnnotationsByType,仍然获取不到,通过直接执行PolyContractCodeManageStrategy.class.getDeclaredAnnotationsByType(RegisterToa.class);能够获取到,遂判断是registrantClass的问题,打印后发现为cglib动态代理类,打印如下:com.jeeplus.exhhall.contract.number.strategy.PolyContractCodeManageStrategy$$EnhancerBySpringCGLIB$$621614dd,
然后寻找为什么会被代理,回想到该同事近期有添加一个切面用于添加日志,查看该切面配置如下@Pointcut("execution(* com.jeeplus.*.*(..))")。注解类确实在该切面扫描包下,故而导致注解类被动态代理,继而导致无法获取到注解,修改注解范围后验证通过。
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
// 仅在 Root Application Context 完成初始化时进行注册,其他情况下直接返回
if (!Objects.isNull(applicationContext.getParent())) {
return;
}
merge(
// 将 RegisterFrom 注解配置转换为 RegisterConfig
applicationContext.getBeansWithAnnotation(RegisterFrom.class).values().stream()
.flatMap(
registry -> {
// 校验注册中心
verifyRegistry(registry);
Class<?> registryClass = registry.getClass();
return Stream.of(registryClass.getAnnotationsByType(RegisterFrom.class))
.map(from -> RegisterConfig.of(from, registryClass));
}),
// 将 RegisterFroms 注解配置转换为 RegisterConfig
applicationContext.getBeansWithAnnotation(RegisterFroms.class).values().stream()
.flatMap(
registry -> {
// 校验注册中心
verifyRegistry(registry);
Class<?> registryClass = registry.getClass();
return Stream.of(registryClass.getAnnotationsByType(RegisterFroms.class))
.map(RegisterFroms::value)
.flatMap(Stream::of)
.map(from -> RegisterConfig.of(from, registryClass));
}),
// 将 RegisterTo 注解配置转换为 RegisterConfig
applicationContext.getBeansWithAnnotation(RegisterTo.class).values().stream()
.flatMap(
registrant -> {
Class<?> registrantClass = registrant.getClass();
return Stream.of(registrantClass.getAnnotationsByType(RegisterTo.class))
.map(to -> RegisterConfig.of(to, registrantClass));
}),
// 将 RegisterTos 注解配置转换为 RegisterConfig
applicationContext.getBeansWithAnnotation(RegisterTos.class).values().stream()
.flatMap(
registrant -> {
Class<?> registrantClass = registrant.getClass();
return Stream.of(registrantClass.getAnnotationsByType(RegisterTos.class))
.map(RegisterTos::value)
.flatMap(Stream::of)
.map(to -> RegisterConfig.of(to, registrantClass));
}))
.flatMap(stream -> stream)
.collect(Collectors.groupingBy(RegisterConfig::getRegistryClass))
.forEach(
(registryClass, configs) ->
configs.stream()
// 按照 order 排序
.sorted(
(a, b) -> Objects.compare(a.getOrder(), b.getOrder(), Integer::compareTo))
.forEach(
config -> {
// 注册中心
Registry<Object, Object> registry =
convertRegistry(applicationContext.getBean(registryClass));
// 注册者
Object registrant =
applicationContext.getBean(config.getRegistrantClass());
// 校验注册者的类型是否与注册中心定义的一致
verifyRegistrantType(registry, registrant);
// 校验是否满足注册中心的可重复范围设置
verifyRepeatableScope(registry, registrant, config.getGroup());
// 注册
registry.register(registry.parseGroup(config.getGroup()), registrant);
}));
}
@Component
@Documented
@Target(ElementType.TYPE)
@Repeatable(RegisterTos.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface RegisterTo
@Component
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RegisterTos