SpringBoot 控制器的动态注册与卸载

在项目开发中,动态注册和卸载控制器可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,本文将介绍如何在 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. 测试注册控制器

  1. 启动服务

  2. 访问 http://localhost:8080/demo/hello ,此时应该是404,因为没有注册控制器

  3. POST http://localhost:8080/controller/register?methodName=hello&controllerBeanName=demoController 注册控制器

  4. 再次访问 http://localhost:8080/demo/hello

7. 卸载控制器

  1. POST http://localhost:8080/controller/unregister?methodName=hello&controllerBeanName=demoController 卸载控制器
  2. 再次访问 http://localhost:8080/demo/hello,此时应该是404,因为控制器已被卸载

结论

通过以上步骤,我们实现了在 Spring Boot 中动态注册和卸载控制器的功能。这样的实现能够根据实际需求动态增减功能。

相关推荐
涡能增压发动积1 天前
同样的代码循环 10次正常 循环 100次就抛异常?自定义 Comparator 的 bug 让我丢尽颜面
后端
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Wenweno0o1 天前
0基础Go语言Eino框架智能体实战-chatModel
开发语言·后端·golang
于慨1 天前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
swg3213211 天前
Spring Boot 3.X Oauth2 认证服务与资源服务
java·spring boot·后端
tyung1 天前
一个 main.go 搞定协作白板:你画一笔,全世界都看见
后端·go
gelald1 天前
SpringBoot - 自动配置原理
java·spring boot·后端
@yanyu6661 天前
07-引入element布局及spring boot完善后端
javascript·vue.js·spring boot
殷紫川1 天前
深入理解 AQS:从架构到实现,解锁 Java 并发编程的核心密钥
java
一轮弯弯的明月1 天前
贝尔数求集合划分方案总数
java·笔记·蓝桥杯·学习心得