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);     } } 

相关内容

热门资讯

辅助举措!桃乐甘肃麻将辅助器,... 辅助举措!桃乐甘肃麻将辅助器,川南欢乐碰辅助,详细教程(有挂方法)1、在桃乐甘肃麻将辅助器插件功能辅...
了解大纲!火神微信辅助,新二号... 了解大纲!火神微信辅助,新二号辅助,详细教程(有挂助手)1、这是跨平台的火神微信辅助轻量版有透视,在...
推荐机巧!川娱竞技有挂透视脚本... 推荐机巧!川娱竞技有挂透视脚本,有没有人会衢州都莱的辅助,详细教程(有挂讲解)川娱竞技有挂透视脚本破...
详情教程书!潮友辅助插件,八仙... 详情教程书!潮友辅助插件,八仙游戏辅助,详细教程(发现有挂)1、不需要AI权限,帮助你快速的进行潮友...
辅助教材!乐胡脚本,麻辣竞技辅... 辅助教材!乐胡脚本,麻辣竞技辅助,详细教程(详细教程)1、让任何用户在无需麻辣竞技辅助安装教程第三方...
总结机巧!潮友会插件,wepo... 总结机巧!潮友会插件,wepoker开挂事件,详细教程(有挂总结)1、潮友会插件免费辅助多个强度级别...
专业妙计!边锋干瞪眼房租胡,顺... 专业妙计!边锋干瞪眼房租胡,顺欣茶楼有没有辅助,详细教程(证实有挂)暗藏猫腻,小编详细说明边锋干瞪眼...
关于教程书!微友助手app下载... 您好,微友助手app下载辅助器这款游戏可以开挂的,确实是有挂的,需要了解加去威信【136704302...
教你模板!丽水都莱辅助软件,呗... 教你模板!丽水都莱辅助软件,呗兔辅助怎么样,详细教程(竟然有挂)丽水都莱辅助软件破解侠是真的助透视。...
解迷法门!天胡辅助脚本,欢乐达... 解迷法门!天胡辅助脚本,欢乐达人破解器,详细教程(有挂猫腻)1、超多福利:超高返利,海量正版游戏,欢...