如何基于自定义注解与通用控制器为Model生成CRUD控制器?
解决动态配置CRUD控制器请求路径的问题
问题根源
Spring注解(如@RequestMapping、@GetMapping)的属性值要求是编译时常量,直接用annotAttributeMap传递运行时动态生成的路径值,Spring容器无法识别这种动态注入的元数据,导致控制器映射失效。
可行解决方案
1. 用BeanDefinitionRegistryPostProcessor手动注册控制器Bean
这个处理器能在Spring容器加载Bean定义后、实例化前,动态新增或修改Bean定义,适合批量生成CRUD控制器:
@Component public class CRUDControllerRegistry implements BeanDefinitionRegistryPostProcessor { private final ClassPathScanningCandidateComponentProvider scanner; public CRUDControllerRegistry() { scanner = new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter(new AnnotationTypeFilter(CRUDForm.class)); } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // 扫描所有标注了CRUDForm的Model类 Set<BeanDefinition> candidates = scanner.findCandidateComponents("com.yourpackage.model"); for (BeanDefinition candidate : candidates) { try { Class<?> modelClass = Class.forName(candidate.getBeanClassName()); CRUDForm crudForm = modelClass.getAnnotation(CRUDForm.class); // 生成基础路径:优先用注解配置,默认取Model类名小写 String basePath = crudForm.basePath().isEmpty() ? "/" + modelClass.getSimpleName().toLowerCase() : crudForm.basePath(); // 构建通用控制器的Bean定义 GenericBeanDefinition controllerDef = new GenericBeanDefinition(); controllerDef.setBeanClass(GenericMVCController.class); // 注入Model类和基础路径到通用控制器 MutablePropertyValues props = controllerDef.getPropertyValues(); props.add("modelClass", modelClass); props.add("basePath", basePath); // 注册控制器Bean,命名规则:Model类名+Controller String beanName = modelClass.getSimpleName() + "Controller"; registry.registerBeanDefinition(beanName, controllerDef); } catch (ClassNotFoundException e) { throw new RuntimeException("加载Model类失败", e); } } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 无需额外处理 } }
2. 修改通用控制器,动态注册请求映射
放弃在类/方法上硬写注解,改用RequestMappingHandlerMapping手动注册请求规则:
public class GenericMVCController { private Class<?> modelClass; private String basePath; // 提供setter让Spring注入属性 public void setModelClass(Class<?> modelClass) { this.modelClass = modelClass; } public void setBasePath(String basePath) { this.basePath = basePath; } @Autowired private RequestMappingHandlerMapping handlerMapping; @PostConstruct public void registerCRUDHandlers() throws NoSuchMethodException { // 注册列表查询接口 Method listMethod = this.getClass().getMethod("list"); RequestMappingInfo listMapping = RequestMappingInfo .paths(basePath + "/list") .methods(RequestMethod.GET) .build(); handlerMapping.registerMapping(listMapping, this, listMethod); // 注册新增接口 Method saveMethod = this.getClass().getMethod("save", Object.class); RequestMappingInfo saveMapping = RequestMappingInfo .paths(basePath + "/save") .methods(RequestMethod.POST) .build(); handlerMapping.registerMapping(saveMapping, this, saveMethod); // 同理注册删除、修改等接口 } // 实际CRUD业务方法 public String list() { // 通用列表逻辑 return modelClass.getSimpleName() + "_list"; } public String save(Object model) { // 通用保存逻辑 return "success"; } }
3. 优化CRUDForm注解,增加可配置属性
让用户能在Model类上直接指定路径等配置:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface CRUDForm { // 自定义基础请求路径,默认空值 String basePath() default ""; // 可选:是否启用删除接口 boolean enableDelete() default true; }
关键说明
放弃用annotAttributeMap传递动态值的核心原因:Spring注解的属性值必须是编译期就能确定的常量(字符串字面量、枚举、静态常量等),运行时动态生成的Map无法被Spring的注解解析器识别,必须通过Bean定义修改或手动注册请求映射的方式实现动态配置。
内容的提问来源于stack exchange,提问作者mohammad shahbodaghi
相关产品推荐
相关产品推荐

