如何将getAnnotation调用适配到实例变量类型?门面类注解读取问题
问题与解决方案
问题背景
我定义了如下接口:
public interface ExportDocumentFactory { Class<?>[] getSupportedDataRecordClasses(); }
实现类通过重写该方法声明支持的DataRecord类型:
@Override public Class<?>[] getSupportedDataRecordClasses() { Class<?>[] classes = new Class<?>[1]; classes[0] = CardRequestBatchComponent.class; return classes; }
服务原本通过以下代码读取支持类型:
sourceClasses = sourceFileClientConfig.getFileFactory().getSupportedDataRecordClasses();
这个方案运行正常,但我想改用注解实现,定义了@DataRecordSupport注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DataRecordSupport { Class<?>[] value() default {}; }
实现类只需添加注解即可声明支持类型:
@DataRecordSupport({CardRequestBatchComponent.class})
读取注解的代码如下:
sourceClasses = sourceFileClientConfig.getFileFactory().getClass().getAnnotation(DataRecordSupport.class).value();
但存在一个问题:其中一个实现类是门面类,它的方法实现是委托给底层工厂:
@Override public Class<?>[] getSupportedDataRecordClasses() { return config.getFileFactory().getSupportedDataRecordClasses(); }
针对这个门面类,直接调用getAnnotation无法找到注解,需要解决这个问题,优先不修改调用者代码,若必须修改则考虑新增getConfig方法的方案。
解决方案
方案一:无需修改调用者代码(推荐)
调整门面类的getSupportedDataRecordClasses方法,在内部直接读取底层工厂类的注解,既保留注解的简洁性,又兼容原调用逻辑:
@Override public Class<?>[] getSupportedDataRecordClasses() { ExportDocumentFactory delegateFactory = config.getFileFactory(); // 读取底层工厂的注解 DataRecordSupport supportAnnotation = delegateFactory.getClass().getAnnotation(DataRecordSupport.class); // 兜底返回空数组避免空指针 return supportAnnotation != null ? supportAnnotation.value() : new Class<?>[0]; }
调用者依然使用原有的getSupportedDataRecordClasses()方法即可,完全不需要修改。
方案二:通过工具类封装注解读取逻辑
如果不想修改门面类的方法实现,可以封装一个通用的工具类,自动处理门面类的穿透逻辑:
public class ExportDocumentHelper { public static Class<?>[] getSupportedClasses(ExportDocumentFactory factory) { // 先尝试读取当前类的注解 DataRecordSupport annotation = factory.getClass().getAnnotation(DataRecordSupport.class); if (annotation != null && annotation.value().length > 0) { return annotation.value(); } // 判断是否为门面类,穿透到底层工厂(需约定门面类的判断方式,比如 instanceof 或特定接口) if (factory instanceof FacadeExportDocumentFactory) { ExportDocumentFactory delegate = ((FacadeExportDocumentFactory) factory).getConfig().getFileFactory(); // 递归读取底层注解 return getSupportedClasses(delegate); } return new Class<?>[0]; } }
调用者只需将原注解读取代码替换为工具类调用:
sourceClasses = ExportDocumentHelper.getSupportedClasses(sourceFileClientConfig.getFileFactory());
方案三:修改调用者并依赖门面类结构(不推荐)
如果允许修改调用者代码,可以新增getConfig方法直接穿透到底层工厂读取注解,但这种方式耦合度高,依赖门面类的内部实现:
sourceClasses = sourceFileClientConfig.getFileFactory().getConfig().getFileFactory().getClass().getAnnotation(DataRecordSupport.class).value();
更优雅的方式是定义一个DelegateAware接口,让门面类实现该接口,避免依赖具体类:
// 定义通用的委托感知接口 public interface DelegateAware<T> { T getDelegate(); } // 门面类实现接口 public class FacadeExportDocumentFactory implements ExportDocumentFactory, DelegateAware<ExportDocumentFactory> { private Config config; @Override public Class<?>[] getSupportedDataRecordClasses() { return config.getFileFactory().getSupportedDataRecordClasses(); } @Override public ExportDocumentFactory getDelegate() { return config.getFileFactory(); } }
调用者代码改为:
ExportDocumentFactory factory = sourceFileClientConfig.getFileFactory(); DataRecordSupport annotation = factory.getClass().getAnnotation(DataRecordSupport.class); // 若当前类无注解,尝试获取委托对象的注解 if (annotation == null && factory instanceof DelegateAware) { ExportDocumentFactory delegate = ((DelegateAware<ExportDocumentFactory>) factory).getDelegate(); annotation = delegate.getClass().getAnnotation(DataRecordSupport.class); } sourceClasses = annotation != null ? annotation.value() : new Class<?>[0];
内容的提问来源于stack exchange,提问作者caduceus
相关产品推荐
相关产品推荐

