SpringBean和Controller实现动态注册与注销过程详细讲解
部分场景下可能需要下载远程jar包,然后注册jar包中的Bean和Controller
说明
这里的Bean 一般特指 Service层的服务类,Controller本质上也是Bean
注册和注销工具类
这里用了一些 hutool的工具类,hutools是一个不错的基础工具集。
package cn.guzt.utils; import cn.hutool.extra.spring.SpringUtil; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.lang.reflect.Method; /** * 动态注册注销Spring Bean * * @author guzt */ @SuppressWarnings("unused") public class DynamicRegistUtil { /** * 动态注册Bean * * @param beanName bean名称 * @param targetClass bean对应的类 */ public static void registerBeanDefinition(String beanName, Class<?> targetClass) { ApplicationContext applicationContext = SpringUtil.getApplicationContext(); //获取BeanFactory DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); //创建bean信息. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(targetClass); //动态注册bean. defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition()); } /** * 动态卸载Bean * * @param beanName bean名称 */ public static void unRegisterBeanDefinition(String beanName) { ApplicationContext applicationContext = SpringUtil.getApplicationContext(); if (!applicationContext.containsBean(beanName)) { return; } //获取BeanFactory DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); defaultListableBeanFactory.removeBeanDefinition(beanName); } /** * 动态注册Controller * * @param controllerBeanName controller的beanName * @throws Exception 反射异常 */ public static void registerController(String controllerBeanName) throws Exception { final RequestMappingHandlerMapping requestMappingHandlerMapping = SpringUtil.getBean(RequestMappingHandlerMapping.class); if (requestMappingHandlerMapping != null) { Object controller = SpringUtil.getBean(controllerBeanName); if (controller == null) { return; } //注册Controller Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass(). getDeclaredMethod("detectHandlerMethods", Object.class); //将private改为可使用 method.setAccessible(true); method.invoke(requestMappingHandlerMapping, controllerBeanName); } } /** * 动态去掉Controller的Mapping * * @param controllerBeanName controller的beanName */ public static void unregisterController(String controllerBeanName) { final RequestMappingHandlerMapping requestMappingHandlerMapping = SpringUtil.getBean("requestMappingHandlerMapping"); if (requestMappingHandlerMapping != null) { Object controller = SpringUtil.getBean(controllerBeanName); if (controller == null) { return; } final Class<?> targetClass = controller.getClass(); ReflectionUtils.doWithMethods(targetClass, method -> { Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); try { Method createMappingMethod = RequestMappingHandlerMapping.class. getDeclaredMethod("getMappingForMethod", Method.class, Class.class); createMappingMethod.setAccessible(true); RequestMappingInfo requestMappingInfo = (RequestMappingInfo) createMappingMethod.invoke(requestMappingHandlerMapping, specificMethod, targetClass); if (requestMappingInfo != null) { requestMappingHandlerMapping.unregisterMapping(requestMappingInfo); } } catch (Exception e) { e.printStackTrace(); } }, ReflectionUtils.USER_DECLARED_METHODS); } } }
编写测试用例
创建一个maven项目(dynamic-regist-bean),里面主要引入spring-boot-starter-web即可
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
创建一个service测试类
package org.example.service; public interface DynamicRegistService { void serviceDo(); }
创建接口对应的实现类,上面无需@Service 注解
package org.example.service.impl; import lombok.extern.slf4j.Slf4j; import org.example.service.DynamicRegistService; @Slf4j public class DynamicRegistServiceImpl implements DynamicRegistService { @Override public void serviceDo() { log.info("Spring动态注册的Bean dynamicRegistServiceImpl中的 serviceDo 无参方法执行完成..."); } }
创建一个controller测试类,类上面无需 @Controller注解
package org.example.controller; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; @ResponseBody @RequestMapping("dynamicRegistController") public class DynamicRegistController { @PostMapping("postTest") public Map<String, Object> postTest(@RequestBody Map<String, Object> params) { Map<String, Object> map = new HashMap<>(4); map.put("code", "0"); map.put("msg", "POST请求测试成功, 传递参数params:" + params.toString()); map.put("data", ""); return map; } @GetMapping("getTest/{id}") public Map<String, Object> getTest(@PathVariable("id") String id) { Map<String, Object> map = new HashMap<>(4); map.put("code", "0"); map.put("msg", "GET请求测试成功, 传输的参数id:" + id); map.put("data", ""); return map; } }
编译打包
> maven clean package
mavne打包命令生成 dynamic-regist-bean.jar
另外一个SpringBoot中创建测试接口
假设访问BaseUrl为: http://localhost:8081
import cn.guzt.utils.DynamicRegistUtil; import cn.hutool.core.util.ClassLoaderUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.extra.spring.SpringUtil; import com.middol.starter.common.pojo.vo.NoBody; import com.middol.starter.common.pojo.vo.ResponseVO; import io.swagger.annotations.Api; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.File; @Api(tags = "动态注册Bean、Controller测试") @RestController @RequestMapping("dynamicRegistTestController") public class DynamicRegistTestController { private static final Logger logger = LoggerFactory.getLogger(DynamicRegistTestController.class); /** * 模拟从远程下载准备要注册Bean的jar文件 * * @return jar文件 */ private File getRmoteJarFile() { return new File("E:/IDEA_HOME/dynamic-regist-bean/dynamic-regist-bean/target/dynamic-regist-bean.jar"); } @GetMapping("registBean") public ResponseVO<NoBody> registBean() { File jarFile = getRmoteJarFile(); // 准备要注册的Bean类名 String className = "org.example.service.impl.DynamicRegistServiceImpl"; // 准备要注册的Bean名称 String beanName = "dynamicRegistServiceImpl"; // 注册完成后调用Bean的测试方法 String invokeMethod = "serviceDo"; Class<?> targetClass = ClassLoaderUtil.loadClass(jarFile, className); logger.info("本次要注册的bean className = {}", targetClass.getName()); DynamicRegistUtil.registerBeanDefinition(beanName, targetClass); Object object = SpringUtil.getBean(beanName); ReflectUtil.invoke(object, invokeMethod); return ResponseVO.success(); } @GetMapping("unRegistBean") public ResponseVO<NoBody> unRegistBean() { String beanName = "dynamicRegistServiceImpl"; logger.info("本次要卸载的bean beanName = {}", beanName); DynamicRegistUtil.unRegisterBeanDefinition(beanName); logger.info("卸载结果:{}", SpringUtil.getApplicationContext().containsBean(beanName) ? "失败" : "成功"); return ResponseVO.success(); } @GetMapping("registController") public ResponseVO<NoBody> registController() throws Exception { File jarFile = getRmoteJarFile(); // 准备要注册的Bean类名 String className = "org.example.controller.DynamicRegistController"; // 准备要注册的Bean名称 String beanName = "dynamicRegistController"; Class<?> targetClass = ClassLoaderUtil.loadClass(jarFile, className); logger.info("本次要注册的controller className = {}", targetClass.getName()); DynamicRegistUtil.registerBeanDefinition(beanName, targetClass); DynamicRegistUtil.registerController(beanName); return ResponseVO.success(); } @GetMapping("unRegistController") public ResponseVO<NoBody> unRegistController() { String beanName = "dynamicRegistController"; logger.info("本次要卸载的Controller beanName = {}", beanName); DynamicRegistUtil.unregisterController(beanName); DynamicRegistUtil.unRegisterBeanDefinition(beanName); logger.info("卸载结果:{}", SpringUtil.getApplicationContext().containsBean(beanName) ? "失败" : "成功"); return ResponseVO.success(); } }
测试结果
注册Service
访问: http://localhost:8081/dynamicRegistTestController/registBean
返回:
{"code":"0","message":"SUCCESS","data":null}
日志:
[http-nio-8081-exec-1] c.g.c.DynamicRegistTestController : 本次要注册的bean className = org.example.service.impl.DynamicRegistServiceImpl
[http-nio-8081-exec-1] o.e.s.impl.DynamicRegistServiceImpl : Spring动态注册的Bean dynamicRegistServiceImpl中的 serviceDo 无参方法执行完成...
注册controller
访问:http://localhost:8081/dynamicRegistTestController/registController
返回:
{"code":"0","message":"SUCCESS","data":null}
日志:
[http-nio-8081-exec-5] c.g.c.DynamicRegistTestController : 本次要注册的controller className = org.example.controller.DynamicRegistController
测试Controller 是否真的注册成功:
访问: http://localhost:8081/dynamicRegistController/getTest/aaaa 返回:
{"msg":"GET请求测试成功, 传输的参数id:aaaa","data":"","code":"0"}
注销Controller
访问:http://localhost:8081/dynamicRegistTestController/unRegistController
然后重新访问: http://localhost:8081/dynamicRegistController/getTest/aaaa
返回:404错误 ,说明注销成功!
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.Tue Feb 07 14:14:52 CST 2023
There was an unexpected error (type=Not Found, status=404).
到此这篇关于SpringBean和Controller实现动态注册与注销过程详细讲解的文章就介绍到这了,更多相关SpringBean动态注册与注销内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
金三银四复工高频面试题java算法LeetCode396旋转函数
这篇文章主要为大家介绍了金三银四复工高频面试题之java算法题解LeetCode396旋转函数,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-02-02Spring中@RabbitHandler和@RabbitListener的区别详析
@RabbitHandler是用于处理消息的方法注解,它与@RabbitListener注解一起使用,这篇文章主要给大家介绍了关于Spring中@RabbitHandler和@RabbitListener区别的相关资料,需要的朋友可以参考下2024-02-02IntelliJ IDEA中使用mybatis-generator的示例
这篇文章主要介绍了IntelliJ IDEA中使用mybatis-generator,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧2018-04-04
最新评论