在项目开发中,动态注册和卸载控制器可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,本文将介绍如何在 Spring Boot 中实现控制器的动态注册和卸载。
项目结构
css
src
└── main
├── java
│ └── com
│ └── example
│ ├── DynamicControllerApplication.java
│ ├── controller
│ │ ├── DemoController.java
│ │ └── DynamicControllerRegistry.java
│ │ └── DynamicController.java
│ │ └── ControllerManagement.java
│ └── config
│ └── WebConfig.java
└── resources
└── application.properties
1. 创建 Spring Boot 启动类
首先,创建一个启动类 DynamicControllerApplication.java
。
typescript
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DynamicControllerApplication {
public static void main(String[] args) {
SpringApplication.run(DynamicControllerApplication.class, args);
}
}
2. 创建一个测试控制器
接下来,我们创建一个控制器 DemoController.java
,该控制器将返回简单的字符串响应。
kotlin
package com.example.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/demo")
@DynamicController(startupRegister = false)
public class DemoController {
@GetMapping("/hello")
public String hello() {
return "Hello from Dynamic Controller!";
}
}
3. 创建动态控制器注册类
然后,我们创建一个 DynamicControllerRegistry.java
类,用于动态注册和卸载控制器。
ini
package com.example.controller;
import cn.hutool.core.util.ReflectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@Component
@Slf4j
public class DynamicControllerRegistry {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
private final Map<String, String> registeredControllers = new HashMap<>();
public String registerController(Object controller,String methodName) {
Class<?> controllerClass = controller.getClass();
String key = key(controllerClass,methodName);
String url = "";
if (!registeredControllers.containsKey(key)) {
Method method = ReflectUtil.getMethod(controllerClass,methodName);
RequestMapping methodMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
RequestMapping requestMapping = controllerClass.getAnnotation(RequestMapping.class);
url = getMethodUrl(requestMapping,methodMapping);
// 注册控制器
requestMappingHandlerMapping.registerMapping(
RequestMappingInfo.paths(url).methods(methodMapping.method()).build(),
controller,method
);
registeredControllers.put(key,url);
}else{
url = registeredControllers.get(key);
log.warn("controller already registered:{}",url);
}
return url;
}
public String unregisterController(Class<?> controllerClass,Method method) {
RequestMapping methodMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
RequestMapping requestMapping = controllerClass.getAnnotation(RequestMapping.class);
String url = getMethodUrl(requestMapping,methodMapping);
RequestMappingInfo mappingInfo = RequestMappingInfo.paths(url).methods(methodMapping.method()).build();
requestMappingHandlerMapping.unregisterMapping(mappingInfo);
registeredControllers.remove(key(controllerClass,method.getName()));
log.info("unregister controller:{}", url);
return url;
}
public String unregisterController(Class<?> controllerClass,String methodName) {
Method method = ReflectUtil.getMethod(controllerClass,methodName);
return unregisterController(controllerClass,method);
}
private String key(Class<?> controllerClass, String method){
return controllerClass.getName() + "." + method;
}
private String getMethodUrl(RequestMapping requestMapping,RequestMapping methodMapping){
String baseUrl = "";
String url = "";
if(requestMapping != null){
baseUrl = requestMapping.value()[0];
}
if(methodMapping != null) {
String[] values = methodMapping.value();
if (values.length > 0) {
url = baseUrl + values[0];
}
}
return url;
}
}
4. 创建 Web 配置类
我们还需要一个配置类 WebConfig.java
来配置动态控制器的注册和卸载。
ini
package com.example.config;
import cn.hutool.core.util.ClassUtil;
import com.example.controller.DynamicController;
import com.example.controller.DynamicControllerRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
@Configuration
@Slf4j
public class WebConfig {
@Autowired
private DynamicControllerRegistry dynamicControllerRegistry;
@Bean
public Void unregisterDynamicController() {
Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation("com.example", DynamicController.class);
for(Class<?> clazz : classes) {
DynamicController dynamicController = clazz.getAnnotation(DynamicController.class);
boolean needRegister = dynamicController.startupRegister();
if(needRegister) {
continue;
}
// 默认不需要注册的controller,需要在启动时注销掉
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods) {
dynamicControllerRegistry.unregisterController(clazz,method);
}
}
return null;
}
}
5. 创建动态控制器注册和卸载控制器
我们创建一个新的控制器 ControllerManagement.java
,用于处理控制器的注册和卸载请求。
less
package com.example.controller;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/controller")
public class ControllerManagement {
@Autowired
private DynamicControllerRegistry dynamicControllerRegistry;
@PostMapping("/register")
public String registerController(@RequestParam String controllerBeanName, @RequestParam String methodName) {
Object controller = SpringUtil.getBean(controllerBeanName);
String url = dynamicControllerRegistry.registerController(controller, methodName);
return "Controller registered at: " + url;
}
@DeleteMapping("/unregister")
public String unregisterController(@RequestParam String controllerBeanName, @RequestParam String methodName) {
Object controller = SpringUtil.getBean(controllerBeanName);
String url = dynamicControllerRegistry.unregisterController(controller.getClass(),methodName);
return "Controller unregistered from: " + url;
}
}
6. 测试注册控制器
-
启动服务
-
访问 http://localhost:8080/demo/hello ,此时应该是404,因为没有注册控制器
-
POST http://localhost:8080/controller/register?methodName=hello&controllerBeanName=demoController 注册控制器
7. 卸载控制器
- POST http://localhost:8080/controller/unregister?methodName=hello&controllerBeanName=demoController 卸载控制器
- 再次访问 http://localhost:8080/demo/hello,此时应该是404,因为控制器已被卸载
结论
通过以上步骤,我们实现了在 Spring Boot 中动态注册和卸载控制器的功能。这样的实现能够根据实际需求动态增减功能。