基于配置动态生成SpringBoot重复Controller及避免代码重复方案咨询
现有基础实现代码
动态父控制器(Dynamic Parent Controller)
@CrossOrigin("*") public abstract class RealtimeController<T> { public abstract RealtimeService<T> getService(); @PostMapping(value = "/find") public ResponseEntity<Iterable<T>> find(@RequestBody T entity) { return ResponseEntity.ok(getService().findAllByKey(entity)); } }
动态服务抽象类(Dynamic Service)
public abstract class RealtimeService<T> { public abstract RealtimeRepository<T> getRepository(); public Iterable<T> findAllByKey(T entity) { return getRepository().findAll( Example.of( entity, ExampleMatcher.matching().withIgnorePaths("_class")) ); } }
子控制器1(Child Controller1)
@Api(tags = "Child1") @RestController @RequestMapping("/api/v1/child1") @Slf4j public class Child1Controller extends RealtimeController<Child1> { @Autowired private Child1Service child1Service; @Override public RealtimeService<Child1> getService(){ return child1Service; } }
子控制器2(Child Controller2)
@Api(tags = "Child2") @RestController @RequestMapping("/api/v1/child2") @Slf4j public class Child2Controller extends RealtimeController<Child2> { @Autowired private Child2Service child2Service; @Override public RealtimeService<Child2> getService(){ return child2Service; } }
application.yaml配置示例
controllers: - name: Child1 endpoint: /api/v1/child1 model: Child1 - name: Child2 endpoint: /api/v1/child2 model: Child2
问题1:动态生成控制器方案可行性
该方案完全可以落地实现,Spring框架支持运行时动态注册Bean实例,具体实现逻辑如下:
- 自定义配置类绑定
application.yaml中的controllers配置列表,读取每个子控制器的名称、端点、模型类全限定名 - 实现
BeanDefinitionRegistryPostProcessor接口,在Spring上下文初始化阶段遍历配置列表,通过ByteBuddy、CGLIB等字节码生成工具动态生成继承RealtimeController<T>的子类,运行时给子类添加@RestController、@RequestMapping、@Api注解,同时注入对应业务Service实例 - 将生成的子类BeanDefinition注册到Spring容器中,后续SpringMVC会自动识别这些控制器并绑定路由
该方案存在以下劣势:
- 动态生成的类调试难度高,断点排查、异常栈定位都比手写类麻烦
- 泛型擦除、Swagger注解的动态解析容易出现兼容问题
- 若后续需要给单个控制器扩展个性化接口,灵活度低于手写实现
问题2:更优实现方案推荐
推荐两种比动态生成更稳妥的方案,都能实现零重复代码:
方案1:注解模板简化手写(最稳妥)
自定义组合注解封装重复的注解逻辑:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @RestController @Slf4j public @interface RealtimeRestController { String tag(); String endpoint(); }
之后每个子控制器只需要写少量代码,无任何重复逻辑:
@Api(tags = "Child1") @RealtimeRestController(tag = "Child1", endpoint = "/api/v1/child1") public class Child1Controller extends RealtimeController<Child1> { @Autowired private Child1Service child1Service; @Override public RealtimeService<Child1> getService() { return child1Service; } }
该方案兼顾灵活性和简洁性,调试、扩展都很方便,适合业务后续可能有个性化需求的场景。
方案2:统一通用控制器(代码量最少)
只写一个控制器,通过路由参数匹配业务类型,完全不需要写多个子类:
@Api(tags = "通用查询接口") @RestController @RequestMapping("/api/v1") @CrossOrigin("*") public class CommonRealtimeController { // 容器启动时自动注入所有RealtimeService实现类,key为业务标识 @Autowired private Map<String, RealtimeService<?>> serviceMap; // 提前配置bizType和对应Model类的映射关系 @Autowired private Map<String, Class<?>> bizTypeModelConfig; @PostMapping("/{bizType}/find") public ResponseEntity<Iterable<?>> find(@PathVariable String bizType, @RequestBody JSONObject entity) { RealtimeService<?> service = serviceMap.get(bizType); Object model = entity.toJavaObject(bizTypeModelConfig.get(bizType)); return ResponseEntity.ok(service.findAllByKey(model)); } }
只需要维护bizType和Model、Service的映射配置即可,适合所有业务控制器逻辑完全一致、后续没有个性化扩展需求的场景。
内容的提问来源于stack exchange,提问作者spattanaik75
相关产品推荐
相关产品推荐

