SpringBean和Controller實(shí)現(xiàn)動(dòng)態(tài)注冊(cè)與注銷過程詳細(xì)講解
部分場(chǎng)景下可能需要下載遠(yuǎn)程jar包,然后注冊(cè)jar包中的Bean和Controller
說明
這里的Bean 一般特指 Service層的服務(wù)類,Controller本質(zhì)上也是Bean
注冊(cè)和注銷工具類
這里用了一些 hutool的工具類,hutools是一個(gè)不錯(cuò)的基礎(chǔ)工具集。
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;
/**
* 動(dòng)態(tài)注冊(cè)注銷Spring Bean
*
* @author guzt
*/
@SuppressWarnings("unused")
public class DynamicRegistUtil {
/**
* 動(dòng)態(tài)注冊(cè)Bean
*
* @param beanName bean名稱
* @param targetClass bean對(duì)應(yīng)的類
*/
public static void registerBeanDefinition(String beanName, Class<?> targetClass) {
ApplicationContext applicationContext = SpringUtil.getApplicationContext();
//獲取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory =
(DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
//創(chuàng)建bean信息.
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(targetClass);
//動(dòng)態(tài)注冊(cè)bean.
defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
}
/**
* 動(dòng)態(tài)卸載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);
}
/**
* 動(dòng)態(tài)注冊(cè)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;
}
//注冊(cè)Controller
Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().
getDeclaredMethod("detectHandlerMethods", Object.class);
//將private改為可使用
method.setAccessible(true);
method.invoke(requestMappingHandlerMapping, controllerBeanName);
}
}
/**
* 動(dòng)態(tài)去掉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);
}
}
}編寫測(cè)試用例
創(chuàng)建一個(gè)maven項(xiàng)目(dynamic-regist-bean),里面主要引入spring-boot-starter-web即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
創(chuàng)建一個(gè)service測(cè)試類
package org.example.service;
public interface DynamicRegistService {
void serviceDo();
}
創(chuàng)建接口對(duì)應(yīng)的實(shí)現(xiàn)類,上面無需@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動(dòng)態(tài)注冊(cè)的Bean dynamicRegistServiceImpl中的 serviceDo 無參方法執(zhí)行完成...");
}
}
創(chuàng)建一個(gè)controller測(cè)試類,類上面無需 @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請(qǐng)求測(cè)試成功, 傳遞參數(shù)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請(qǐng)求測(cè)試成功, 傳輸?shù)膮?shù)id:" + id);
map.put("data", "");
return map;
}
}
編譯打包
> maven clean package
mavne打包命令生成 dynamic-regist-bean.jar
另外一個(gè)SpringBoot中創(chuàng)建測(cè)試接口
假設(shè)訪問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 = "動(dòng)態(tài)注冊(cè)Bean、Controller測(cè)試")
@RestController
@RequestMapping("dynamicRegistTestController")
public class DynamicRegistTestController {
private static final Logger logger = LoggerFactory.getLogger(DynamicRegistTestController.class);
/**
* 模擬從遠(yuǎn)程下載準(zhǔn)備要注冊(cè)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();
// 準(zhǔn)備要注冊(cè)的Bean類名
String className = "org.example.service.impl.DynamicRegistServiceImpl";
// 準(zhǔn)備要注冊(cè)的Bean名稱
String beanName = "dynamicRegistServiceImpl";
// 注冊(cè)完成后調(diào)用Bean的測(cè)試方法
String invokeMethod = "serviceDo";
Class<?> targetClass = ClassLoaderUtil.loadClass(jarFile, className);
logger.info("本次要注冊(cè)的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("卸載結(jié)果:{}", SpringUtil.getApplicationContext().containsBean(beanName) ? "失敗" : "成功");
return ResponseVO.success();
}
@GetMapping("registController")
public ResponseVO<NoBody> registController() throws Exception {
File jarFile = getRmoteJarFile();
// 準(zhǔn)備要注冊(cè)的Bean類名
String className = "org.example.controller.DynamicRegistController";
// 準(zhǔn)備要注冊(cè)的Bean名稱
String beanName = "dynamicRegistController";
Class<?> targetClass = ClassLoaderUtil.loadClass(jarFile, className);
logger.info("本次要注冊(cè)的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("卸載結(jié)果:{}", SpringUtil.getApplicationContext().containsBean(beanName) ? "失敗" : "成功");
return ResponseVO.success();
}
}
測(cè)試結(jié)果
注冊(cè)Service
訪問: http://localhost:8081/dynamicRegistTestController/registBean
返回:
{"code":"0","message":"SUCCESS","data":null}
日志:
[http-nio-8081-exec-1] c.g.c.DynamicRegistTestController : 本次要注冊(cè)的bean className = org.example.service.impl.DynamicRegistServiceImpl
[http-nio-8081-exec-1] o.e.s.impl.DynamicRegistServiceImpl : Spring動(dòng)態(tài)注冊(cè)的Bean dynamicRegistServiceImpl中的 serviceDo 無參方法執(zhí)行完成...
注冊(cè)controller
訪問:http://localhost:8081/dynamicRegistTestController/registController
返回:
{"code":"0","message":"SUCCESS","data":null}
日志:
[http-nio-8081-exec-5] c.g.c.DynamicRegistTestController : 本次要注冊(cè)的controller className = org.example.controller.DynamicRegistController
測(cè)試Controller 是否真的注冊(cè)成功:
訪問: http://localhost:8081/dynamicRegistController/getTest/aaaa 返回:
{"msg":"GET請(qǐng)求測(cè)試成功, 傳輸?shù)膮?shù)id:aaaa","data":"","code":"0"}
注銷Controller
訪問:http://localhost:8081/dynamicRegistTestController/unRegistController
然后重新訪問: http://localhost:8081/dynamicRegistController/getTest/aaaa
返回:404錯(cuò)誤 ,說明注銷成功!
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).
到此這篇關(guān)于SpringBean和Controller實(shí)現(xiàn)動(dòng)態(tài)注冊(cè)與注銷過程詳細(xì)講解的文章就介紹到這了,更多相關(guān)SpringBean動(dòng)態(tài)注冊(cè)與注銷內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring?@Conditional通過條件控制bean注冊(cè)過程
- 向Spring IOC 容器動(dòng)態(tài)注冊(cè)bean實(shí)現(xiàn)方式
- 解決Springboot全局異常處理與AOP日志處理中@AfterThrowing失效問題
- SpringBoot項(xiàng)目使用aop案例詳解
- BeanDefinitionRegistryPostProcessor如何動(dòng)態(tài)注冊(cè)Bean到Spring
- Spring運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)bean的方法
- spring動(dòng)態(tài)注冊(cè)bean?AOP失效原理解析
相關(guān)文章
java實(shí)現(xiàn)Xml與json之間的相互轉(zhuǎn)換操作示例
這篇文章主要介紹了java實(shí)現(xiàn)Xml與json之間的相互轉(zhuǎn)換操作,結(jié)合實(shí)例形式分析了Java xml與json相互轉(zhuǎn)換工具類的定義與使用相關(guān)操作技巧,需要的朋友可以參考下2019-06-06
SpringBoot整合screw實(shí)現(xiàn)自動(dòng)生成數(shù)據(jù)庫設(shè)計(jì)文檔
使用navicat工作的話,導(dǎo)出的格式是excel不符合格式,還得自己整理。所以本文將用screw工具包,整合到springboot的項(xiàng)目中便可以自動(dòng)生成數(shù)據(jù)庫設(shè)計(jì)文檔,非常方便,下面就分享一下教程2022-11-11
SpringBoot過濾器實(shí)現(xiàn)項(xiàng)目?jī)?nèi)接口過濾詳解
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何利用過濾器實(shí)現(xiàn)項(xiàng)目?jī)?nèi)接口過濾,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-04-04
解決Eclipse中java文件的圖標(biāo)變成空心J的問題
這篇文章主要介紹了解決Eclipse中java文件的圖標(biāo)變成空心J的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01
SpringBoot?使用定時(shí)任務(wù)(SpringTask)的詳細(xì)步驟
Cron?表達(dá)式非常靈活,可以滿足各種定時(shí)任務(wù)的需求,但需要注意的是,Cron?表達(dá)式只能表示固定的時(shí)間點(diǎn),無法處理復(fù)雜的時(shí)間邏輯,本文給大家介紹SpringBoot?使用定時(shí)任務(wù)(SpringTask)的詳細(xì)步驟,感興趣的朋友一起看看吧2024-02-02
springboot實(shí)現(xiàn)郵箱發(fā)送(激活碼)功能的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用springboot實(shí)現(xiàn)郵箱發(fā)送(激活碼)功能,文中的示例代碼簡(jiǎn)潔易懂,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-10-10
java中利用反射調(diào)用另一類的private方法的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄猨ava中利用反射調(diào)用另一類的private方法的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06
SpringBoot如何基于POI-tl和word模板導(dǎo)出龐大的Word文件
這篇文章主要介紹了SpringBoot如何基于POI-tl和word模板導(dǎo)出龐大的Word文件,poi-tl是一個(gè)基于Apache?POI的Word模板引擎,也是一個(gè)免費(fèi)開源的Java類庫2022-08-08

