Java 21 JPMS模块下Micronaut动态注册/注销@Controller咨询
动态注册/注销Micronaut控制器及Groovy脚本热加载方案
一、JPMS模块下动态注册/注销@Controller类
动态注册可行,需适配JPMS规则
Micronaut的BeanContext支持手动注册Bean,包括带@Controller注解的类,只要满足JPMS模块的访问规则即可:
- JPMS模块配置:目标API模块需要在
module-info.java中exports控制器所在包给Micronaut核心模块(如io.micronaut.core、io.micronaut.web),或者用opens包允许Micronaut进行反射操作(因为控制器依赖注解扫描和反射实例化)。 - 注册示例:
BeanContext beanContext = BeanContext.current(); // 从独立JPMS模块加载控制器类 Class<?> controllerClass = Class.forName("com.example.user.api.UserController", true, getClass().getClassLoader()); // 注册为Singleton Bean,Micronaut自动识别@Controller并生成路由 beanContext.registerSingleton(controllerClass);
注销控制器的实现方式
Micronaut没有直接的注销API,但可以通过以下步骤完成:
- 销毁Bean实例:如果控制器实现了
DisposableBean或Closeable接口,调用对应的销毁方法清理资源。 - 移除路由:通过
MutableRouter过滤并移除目标控制器对应的路由:MutableRouter router = beanContext.getBean(MutableRouter.class); router.remove(route -> route.getTargetMethod().getDeclaringClass().equals(controllerClass)); - 移除Bean定义:调用
beanContext.removeBeanDefinition(controllerClass),注意需确保没有其他Bean依赖该控制器,避免引发依赖错误。
多JPMS模块的协作
每个独立API模块依赖共同父层模块,父层可以提供统一的注册服务,负责加载子模块的控制器类并注册到全局BeanContext。子模块需在module-info.java中声明对父模块的依赖,并开放控制器包给Micronaut。
二、监听目录动态加载Groovy脚本@Controller
该场景完全可行,核心是结合WatchService监听目录变化,通过Groovy类加载器加载脚本并注册到Micronaut:
1. 监听目录文件变化
使用WatchService监听目标目录的创建/修改事件,触发脚本加载逻辑:
WatchService watchService = FileSystems.getDefault().newWatchService(); Path controllerDir = Paths.get("./dynamic-groovy-controllers"); // 监听文件创建和修改事件 controllerDir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY); // 启动线程处理监听事件 new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { WatchKey key; try { key = watchService.take(); for (WatchEvent<?> event : key.pollEvents()) { if (event.kind() == StandardWatchEventKinds.OVERFLOW) continue; Path fileName = (Path) event.context(); if (fileName.toString().endsWith(".groovy")) { loadGroovyController(controllerDir.resolve(fileName)); } } key.reset(); } catch (InterruptedException | IOException e) { Thread.currentThread().interrupt(); break; } } }).start();
2. 加载Groovy脚本并注册
使用GroovyClassLoader解析脚本类,先注销旧Bean(如果存在)再注册新实例:
private void loadGroovyController(Path filePath) throws IOException { GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader()); Class<?> controllerClass = classLoader.parseClass(Files.newBufferedReader(filePath)); BeanContext beanContext = BeanContext.current(); // 先清理旧的Bean和路由 if (beanContext.containsBean(controllerClass)) { beanContext.removeBeanDefinition(controllerClass); MutableRouter router = beanContext.getBean(MutableRouter.class); router.remove(route -> route.getTargetMethod().getDeclaringClass().equals(controllerClass)); } // 注册新的控制器Bean beanContext.registerSingleton(controllerClass); }
3. 注意事项
- Groovy脚本必须包含
@Controller及对应的路由注解(如@Get、@Post),符合Micronaut控制器规范。 - 类加载器管理:频繁修改脚本时,建议为每个脚本创建独立类加载器,或使用Micronaut的
ClassLoaderScope隔离动态类,避免版本冲突。 - JPMS环境下需配置动态模块权限,确保Micronaut能加载外部目录的类。
内容的提问来源于stack exchange,提问作者mah454
相关产品推荐
相关产品推荐

