必须定义契约、加标识、做异常兜底,否则反射报错即崩;统一执行器接口是多态前提,需显式实现Executor;注解标记比路径扫描更可控;反射实例化前须检查无参构造及权限;转型前须用isAssignableFrom校验类型。

能用,但必须先定义好契约、加好标识、做好异常兜底——否则反射一报错就崩,多态也救不回来。
定义统一执行器接口是多态前提
所有业务执行器必须实现同一个接口,比如 Executor,里面声明 execute(Map<string object> context)</string> 和 supports(String type) 这类通用方法。不能只靠“看起来像”,JVM 只认 implements Executor 或 extends AbstractExecutor 这种显式关系。
常见错误是把逻辑写在抽象类里却忘了声明为 abstract,或者让子类继承了多个父类导致无法向上转型;还有的直接用 Object 当返回类型,后续调用时强转失败抛 ClassCastException。
- 接口比抽象类更稳妥:避免子类无意覆盖关键模板方法
-
supports()方法必须轻量,别在里面查数据库或远程调用,它会在每次路由前被调用 - 不要在接口里定义静态字段或默认方法做状态管理,容易引发类加载顺序问题
用注解标记可加载的执行器类
比起包路径扫描(如 com.xxx.executors.*),用自定义注解(如 @BusinessExecutor)更可控。反射阶段只处理带该注解、且实现了 Executor 的类,其他一概跳过。
示例注解定义:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface BusinessExecutor {
String value() default "";
}
扫描时用 ClassPathScanningCandidateComponentProvider 配合 addIncludeFilter(new AnnotationTypeFilter(BusinessExecutor.class)) 即可精准捕获。
- 别用类名后缀匹配(如 “Executor” 结尾),IDE 重命名时容易漏改,运行时报
ClassNotFoundException - 注解上别加
@Inherited,除非你真需要子类自动继承父类的注解语义 - 如果执行器需区分环境(dev/test/prod),建议在注解里加
env = "prod"字段,反射时过滤
反射实例化时必须检查构造函数和权限
调用 clazz.getDeclaredConstructor().newInstance() 前,得确认这个类有可见的无参构造函数。很多执行器为了依赖注入写了带参构造,又没保留无参构造,反射直接抛 NoSuchMethodException。
正确做法是:
- 先用
clazz.getDeclaredConstructors()拿到所有构造器 - 遍历找一个参数为空、且
isAccessible() == false的,就用setAccessible(true)打开权限 - 捕获
InstantiationException、IllegalAccessException、InvocationTargetException三类异常,记录日志并 continue,别让一个执行器失败阻断全部加载 - 如果真需要带参构造(比如要传入
DataSource),建议走工厂模式,反射只负责加载工厂类
多态调度的关键在类型擦除后的安全转型
反射创建出来的对象是 Object 类型,必须显式转型为 Executor 才能调用 execute()。但 JVM 在泛型擦除后不校验实际类型,所以这步转型看似成功,运行时仍可能出问题。
安全做法是加一层校验:
if (Executor.class.isAssignableFrom(clazz)) {
Executor executor = (Executor) instance;
// 后续使用
}
而不是直接 (Executor) clazz.newInstance()。
- 别用
instanceof判断,因为instance是刚 new 出来的,还没赋值给任何变量,instanceof无法提前介入 - 如果执行器内部用了 Spring Bean(比如注入了
@Autowired Service),纯反射创建的对象不会触发依赖注入,得手动用ApplicationContext.getBean()替代 - 热加载场景下,注意类加载器隔离问题:同一个类名,不同
ClassLoader加载的类互不兼容,转型会失败
最易被忽略的是类加载器一致性——反射加载、Spring 容器、模块插件各自用的 ClassLoader 很可能不同,isAssignableFrom 返回 false 不一定是代码写错了,八成是类被重复加载了两次。

















