SpringBean和Controller实现动态注册与注销过程详细讲解

目录
  • 说明
  • 注册和注销工具类
  • 编写测试用例
  • 测试结果
    • 注册Service
    • 注册controller
    • 注销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动态注册与注销内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot使用Spring Security实现登录注销功能

    1.首先看下我的项目结构 我们逐个讲解 /** * 用户登录配置类 * @author Administrator * */ public class AdminUserDateils implements UserDetails { private static final long serialVersionUID = -1546619839676530441L; private transient YCAdmin yCAdmin; public AdminUserDateils() { }

  • SpringBoot注册FilterRegistrationBean相关情况讲解

    目录 思路 手动注册代码 思路 首先编写程序时,或多或少会存在几个固定的Filter,那么第一步就是为确定的那几个Filter指定好顺序.(通常情况下的使用场景是:你要将你现在编写的项目打包成jar给别的项目使用) 其次程序确定的几个Filter已经编写好了,那么需要将它们和不确定的那几个放在一起进行重新排序. 将排好序的Filter进行注册. 补充:FilterRegistrationBean的使用 @Bean FilterRegistrationBean<AFilter> filterRe

  • Spring Bean注册与注入实现方法详解

    目录 1. 逻辑上的 Bean 注册 2. XML 注册 Bean 到自建的库中 2.1 工厂方法 2.2 使用工厂方法和实例化工厂注册 Bean 3. XML 配合注解进行 Bean 注册 4. 使用注解注册 Bean 4.1 注解方式注册的必要条件 4.2 用到的注解 4.3 @Component注解注入 4.4 使用 @Bean 注解注册 5. 通过注解注入 Bean 6. 注入时的一个坑点 7. 获取 库中的对象 上接[Spring]spring核心思想——IOC和DI 上篇文章结尾简单

  • SpringCloud注册中心部署Eureka流程详解

    目录 1.Eureka服务 2.服务提供者 3.服务消费者 4.服务调用测试 今天我们开始正式编码,如何创建spring boot项目这篇文章就不再讲述,如果想要了解可以阅读我之前的创建springboot项目. 首先我们先进行Spring cloud五大组件之一的注册中心,之前文章已经讲过注册中心的介绍,今天我们来部署Netflix的Eureka,进行单机部署以及高可用部署,并开发生产者以及消费者来进行测试eureka的注册消费.(ps:系列文章使用的Spring cloud版本为2021.0

  • SpringBean和Controller实现动态注册与注销过程详细讲解

    目录 说明 注册和注销工具类 编写测试用例 测试结果 注册Service 注册controller 注销Controller 部分场景下可能需要下载远程jar包,然后注册jar包中的Bean和Controller 说明 这里的Bean 一般特指 Service层的服务类,Controller本质上也是Bean 注册和注销工具类 这里用了一些 hutool的工具类,hutools是一个不错的基础工具集. package cn.guzt.utils; import cn.hutool.extra.s

  • Oracle静态注册与动态注册详解

    一.概述: Oracle的注册就是将数据库作为一个服务注册到监听程序.客户端不需要知道数据库名和实例名,只需要知道该数据库对外提供的服务名就可以申请连接到这个数据库.这个服务名可能与实例名一样,也有可能不一样. 在数据库服务器启动过程中,数据库服务器会向监听程序注册相应的服务(无论何时启动一个数据库,默认地都有两条信息注册到监听器中:数据库服务器对应的实例和服务.) 相当于是这样:在数据库服务器和客户端之间有一监听程序(Listener),在监听程序中,会记录相应数据库对应的服务名(一个数据库可

  • Oracle Listener 动态注册 与 静态注册

    一.什么是注册 注册就是将数据库作为一个服务注册到监听程序.客户端不需要知道数据库名和实例名,只需要知道该数据库对外提供的服务名就可以申请连接到数据库.这个服务名可能与实例名一样,也有可能不一样. 在数据库服务器启动过程中,数据库服务器会向监听程序注册相应的服务(无论何时启动一个数据库,默认地都有两条信息注册到监听器中:数据库服务器对应的实例和服务.) 相当于是这样:在数据库服务器和客户端之间有一监听程序(Listener),在监听程序中,会记录相应数据库对应的服务名(一个数据库可能对应有多个服

  • Spring动态注册多数据源的实现方法

    最近在做SaaS应用,数据库采用了单实例多schema的架构(详见参考资料1),每个租户有一个独立的schema,同时整个数据源有一个共享的schema,因此需要解决动态增删.切换数据源的问题. 在网上搜了很多文章后,很多都是讲主从数据源配置,或都是在应用启动前已经确定好数据源配置的,甚少讲在不停机的情况如何动态加载数据源,所以写下这篇文章,以供参考. 使用到的技术 Java8 Spring + SpringMVC + MyBatis Druid连接池 Lombok (以上技术并不影响思路实现,

  • Spring运行时动态注册bean的方法

    在spring运行时,动态的添加bean,dapeng框架在解析xml的字段时,使用到了动态注册,注册了一个实现了FactoryBean类! 定义一个没有被Spring管理的Controller public class UserController implements InitializingBean{ private UserService userService; public UserService getUserService() { return userService; } pu

  • 向Spring IOC 容器动态注册bean实现方式

    目录 本文的大纲 从一个需求谈起 Spring Bean的生命周期再完善 BeanDefinition Bean 加入IOC容器的几种方式 从spring容器中动态添加或移除bean 本文的大纲 从一个需求谈起 这周遇到了这样一个需求,从第三方的数据库中获取值,只是一个简单的分页查询,处理这种问题,我一般都是在配置文件中配置数据库的地址等相关信息,然后在Spring Configuration 注册数据量连接池的bean,然后再将数据库连接池给JdbcTemplate, 但是这种的缺陷是,假设填

  • Mvc动态注册HttpModule详解

    序言 注册Httpmodule可以让我们使用HttpApplication对象中的处理管道事件.目前大家所熟知的应该有2种方式来使用HttpApplication对象中的处理管道事件.第一种是通过Global.asax全局文件,另外一种是通过配置文件来注册httpmodule.那么有这2种方式啦,为什么还要有今天这篇博客呢? 这里我也提1个简单的问题,用实例来证明下动态注册httpmodule的可取之处. 如果你要写一个.net框架,供公司所有mvc项目使用,那么你的框架集成的众多功能中,至少应

  • 一个事半功倍的c#方法 动态注册按钮事件

    言归正传,下面就来讲讲怎样动态注册按钮事件. 首先,我们需要设置变量来获取点击一个数字按钮的值,以便在"="按钮单击事件中计算最终的结果.下面就是我开始的时候写的一段代码: 复制代码 代码如下: public double? value1 = null ;//获取运算符前的数值 public double? value2 = null ;//获取运算符前的数值 public type caltype=type .none ;//获取运算符 private void btnvalue1_

  • Android 动态注册监听网络变化实例详解

    Android 动态注册监听网络变化实例详解 新建一个BroadcastTest项目,然后修改MainActivity中的代码,如下: public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle

  • Android的广播Receiver动态注册和静态注册示例

    广播接收器注册一共有两种形式 : 静态注册和动态注册. 两者及其接收广播的区别: 1.动态注册的广播 永远要快于 静态注册的广播,不管静态注册的优先级设置的多高,不管动态注册的优先级有多低. 2.动态注册广播不是 常驻型广播 ,也就是说广播跟随activity的生命周期.注意: 在activity结束前,移除广播接收器. 静态注册是常驻型 ,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行. 3.在同一个优先级下,谁先启动的快,谁将先接收到广播. 下面这个Demo,界面如下

随机推荐