SpringBoot中动态注册Bean的方式
创始人
2025-01-11 16:04:52
0

测试环境,本文源码

  • Java:8
  • SpringBoot:2.5.14
  • 示例场景:动态注册ProxyServlet,间接实现类似于Nginx的反向代理功能

先理解如何实现动态注册 Bean

由于在 SpringBoot 中,先进行 Bean 的定义,再根据定义进行 Bean 的实例化,所以实现动态 Bean ,我们只需要动态注册 Bean 定义即可。

这就用到了 BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 这个方法。

源码注释

Modify the application context’s internal bean definition registry after its standard initialization. All regular bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for adding further bean definitions before the next post-processing phase kicks in.

所有常规的Bean都已经定义但尚未实例化时,这时候,你可以再新增 Bean 定义

一、通用方式

先说一个小插曲,建议搭配源码食用。假如三个类,他们分别实现且只实现了以下三个接口

  • BeanDefinitionRegistryPostProcessor
  • ApplicationContextAware
  • EnvironmentAware

这时候,这三个 Bean 的默认加载顺序如下。

并且,这三个 Bean 的方法执行顺序也是跟加载顺序相同。

但是,如果 Bean 实现了一个比 ApplicationContextAware 或者 EnvironmentAware 更先加载的 Bean ,那么就会出现先执行 setApplicationContext 或者 setEnvironment 的方法,为啥嘞?

是因为在 Bean 初始化前,先判定有没有实现 Aware 接口,如果实现过了,那么就直接优先调用 Aware 中的方法。如图。

既然明白了这个流程,那我们就可以实现功能了。

该通用方式是适用于 SpringBoot 框架中通用动态注册 Bean 的做法。主要是通过 BeanDefinitionRegistryPostProcessor 动态注册 Bean 定义。

import org.mitre.dsmiley.httpproxy.ProxyServlet; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment;  import java.util.HashMap; import java.util.Map;  /**  * 动态配置bean  *  * @author chenchuancheng  * @since 2024/07/11 22:50  */ @Configuration public class DynamicBeanConfig implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {      private Environment environment;      @Override     public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {         BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ServletRegistrationBean.class);         builder.addConstructorArgValue(new ProxyServlet());         builder.addConstructorArgValue(environment.getProperty("proxy.servletUrl"));         Map initParams = new HashMap<>();         initParams.put("targetUri", environment.getProperty("proxy.targetUrl"));         initParams.put("log", "true");         builder.addPropertyValue("initParameters", initParams);         builder.addPropertyValue("name", environment.getProperty("proxy.name"));         registry.registerBeanDefinition("proxyServlet", builder.getBeanDefinition());     }      @Override     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {     }      @Override     public void setEnvironment(Environment environment) {         this.environment = environment;     } } 

除了实现 Aware 接口,也可以通过构造函数注入,也可以保证调用方法时有值了。

二、特定方式

这个是使用 ServletContext 动态注册 Servlet 的方式。

import org.mitre.dsmiley.httpproxy.ProxyServlet; import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment;  import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import java.util.HashMap; import java.util.Map;  @Configuration public class ProxyServletInitializer implements ServletContextInitializer {      private final Environment environment;      public ProxyServletInitializer(Environment environment) {         this.environment = environment;     }      @Override     public void onStartup(ServletContext servletContext) throws ServletException {         registerProxyServlet(servletContext,environment.getProperty("proxy.name"),environment.getProperty("proxy.targetUrl"));     }      private void registerProxyServlet(ServletContext servletContext, String name, String targetUri) {         ProxyServlet proxyServlet = new ProxyServlet();         ServletRegistration.Dynamic registration = servletContext.addServlet(name + "ProxyServlet", proxyServlet);         registration.setLoadOnStartup(1);         registration.addMapping("/" + name + "/*");                  Map initParameters = new HashMap<>();         initParameters.put("targetUri", targetUri);         initParameters.put("log", "true");         registration.setInitParameters(initParameters);     } } 

相关内容

热门资讯

曝光教程开挂!非凡贪玩挂,芒果... 芒果辅助器安卓版开挂教程视频分享装挂详细步骤在当今的网络游戏中,芒果辅助器安卓版作为一种经典的娱乐方...
曝光辅助!鄱阳翻精辅助软件,越... 您好:鄱阳翻精辅助软件这款游戏可以开挂的,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌...
微扑克教程开挂!微信小程序财神... 您好:这款微信小程序财神十三张辅助游戏是可以开挂的,确实是有挂的,很多玩家在这款微信小程序财神十三张...
曝光辅助!天天卡五星辅助,盛世... 曝光辅助!天天卡五星辅助,盛世辅助器,透视总结开挂(有挂透明挂)《详细加薇136704302咨询》游...
必备教程辅助!广西微乐小程序脚... 必备教程辅助!广西微乐小程序脚本,广东雀伸辅助器(必备辅助推荐开挂辅助工具);亲真的是有正版授权,小...
发现辅助!小闲辅助软件下载,八... 发现辅助!小闲辅助软件下载,八仙游戏辅助,一分钟揭秘开挂(有挂方略)>>您好:软件加13670430...
教你攻略开挂!四川家园辅助器,... 【亲,四川家园辅助器 这款游戏可以开挂的,确实是有挂的,很多玩家在这款四川家园辅助器中打牌都会发现很...
发现辅助!创思维激k辅助器免费... 大家好,今天小编来为大家解答创思维激k辅助器免费这个问题咨询软件客服可以免费测试直接加微信(1367...
解密教程辅助!微信小程序微乐辅... 微信小程序微乐辅助免费开挂教程视频分享装挂详细步骤在当今的网络游戏中,微信小程序微乐辅助免费作为一种...
发现辅助!决战平安京辅助软件,... 您好:这款决战平安京辅助软件游戏是可以开挂的,确实是有挂的,很多玩家在这款决战平安京辅助软件游戏中打...