项目中使用了策略模式来根据不同的数据类型调用不同的处理策略。每个策略类实现了如下通用接口:
public interface SaveRawDataStrategy<T> {
int saveRawData(List<T> data, String batchId);
}
策略实现类通过 @Component 注解注册为 Spring Bean,并在 Spring 启动时统一注入并放入 strategyMap<Class<?>, SaveRawDataStrategy> 中,按泛型参数分类。
@Component
public class XxxxStrategyImpl implements SaveRawDataStrategy<XXXFeatureDTO> {
@Resource
private XxxService service;
@Override
@LogInfo
public int saveRawData(List<XXXFeatureDTO> data, String batchId) {
return batchSaveUtil.batchSave(data, batchId, service::batchXxxSave);
}
}
问题描述:
在某个策略类的方法上加上自定义注解 @LogInfo(配合 AOP 实现日志拦截)之后,该策略类未能成功注册进 strategyMap 中,导致获取不到具体的策略实现类,调用不到策略实现类中的方法。
在策略工厂中,通过下面的这个方法实现 bean 的注册:
@Autowired
public SaveRawDataStrategyFactory(List<SaveRawDataStrategy> strategies) {
for (SaveRawDataStrategy<?> handler : strategies) {
Class<?> dataClass = resolveGenericType(handler.getClass());
if (dataClass != null) {
strategyMap.put(dataClass, handler);
log.info("注册 SaveDataStrategy:{} => {}", dataClass.getSimpleName(), handler.getClass().getSimpleName());
} else {
log.warn("未能识别泛型类型的处理器:{}", handler.getClass().getName());
}
}
}
通过日志发现了handler.getClass().getName()打印出来的是AxspaFeatureStrategyImpl$$EnhancerBySpringCGLIB$$xxx这样的实现类。这就说明了 Spring AOP 在方法上生成了 CGLIB 代理。
泛型解析失败的原因:关键的泛型解析就是Class<?> dataClass = resolveGenericType(handler.getClass());这个方法,方法如下:
private Class<?> resolveGenericType(Class<?> handlerClass) {
Type[] genericInterfaces = handlerClass.getGenericInterfaces();
for (Type type : genericInterfaces) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
if (rawType instanceof Class && SaveHospitalRawDataStrategy.class.isAssignableFrom((Class<?>) rawType)) {
Type actualType = parameterizedType.getActualTypeArguments()[0];
if (actualType instanceof Class<?>) {
return (Class<?>) actualType;
}
}
return null;
}
}
泛型解析的方法通过 handler.getClass().getGenericInterfaces()获取泛型参数;
但是由于handler.getClass()返回的是代理类,代理类本身并不包含泛型信息,导致方法的返回值为 null,因此导致注册失败。
解决方案
将方法改为传入对象实例,而非 Class 类型,并通过 Spring 的 AopProxyUtils 获取原始类:
private Class<?> resolveGenericType(Object handler) {
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(handler); // 获取原始类
for (Type type : targetClass.getGenericInterfaces()) {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
if (pt.getRawType() instanceof Class &&
SaveHospitalRawDataStrategy.class.isAssignableFrom((Class<?>) pt.getRawType())) {
return (Class<?>) pt.getActualTypeArguments()[0];
}
}
}
return null;
}
调用上面的方法也同样需要修改
@Autowired
public SaveHospitalRawDataStrategyFactory(List<SaveHospitalRawDataStrategy> strategies) {
for (SaveHospitalRawDataStrategy<?> handler : strategies) {
Class<?> dataClass = resolveGenericType(handler);
if (dataClass != null) {
strategyMap.put(dataClass, handler);
log.info("注册 SaveHospitalRawDataStrategy:{} => {}", dataClass.getSimpleName(), handler.getClass().getSimpleName());
} else {
log.warn("未能识别泛型类型的处理器:{}", handler.getClass().getName());
}
}
}