Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Approaches for Dynamically Registering Beans in Spring Boot

Tech 1

Test Environment & Use Case

  • Runtime: Java 8
  • Framework version: Spring Boot 2.5.14
  • Demo scenario: Dynamically register ProxyServlet to implement Nginx-style reverse proxy functionality

In Spring Boot's bean lifecycle, bean definition loading always precedes bean instantiation. To register beans dynamically, you only need to inject custom bean definitions during the window after standard bean definitions are loaded and before instantiation starts. This is achieved via the postProcessBeanDefinitionRegistry method of the BeanDefinitionRegistryPostProcessor interface.

Official source code note: 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.

General Dynamic Registration Method

Note for bean loading order: If you define three separate beans that implement only BeanDefinitionRegistryPostProcessor, ApplicationContextAware, and EnvironmentAware respectively, their default loading and method execution order matches the order listed above. If a bean implements an Aware interface with higher loading priority than ApplicationContextAware or EnvironmentAware, its corresponding setter method will execute first, as Spring detects all implemented Aware interfaces and invokes their setters before the formal bean initialization process.

This general approach is applicable for all dynamic bean registration scenarios in Spring Boot, by registering custom bean definitions through BeanDefinitionRegistryPostProcessor:

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;

@Configuration
public class DynamicBeanRegistrar implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

    private Environment runtimeEnv;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinitionBuilder servletRegBuilder = BeanDefinitionBuilder.genericBeanDefinition(ServletRegistrationBean.class);
        servletRegBuilder.addConstructorArgValue(new ProxyServlet());
        servletRegBuilder.addConstructorArgValue(runtimeEnv.getProperty("proxy.servletUrl"));
        
        Map<String, String> servletInitParams = new HashMap<>();
        servletInitParams.put("targetUri", runtimeEnv.getProperty("proxy.targetUrl"));
        servletInitParams.put("log", "true");
        
        servletRegBuilder.addPropertyValue("initParameters", servletInitParams);
        servletRegBuilder.addPropertyValue("name", runtimeEnv.getProperty("proxy.name"));
        registry.registerBeanDefinition("customProxyServlet", servletRegBuilder.getBeanDefinition());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // No custom processing required for this scenario
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.runtimeEnv = environment;
    }
}

As an alternative to implementing the EnvironmentAware interface, you can also inject the Environment object via constructor to ensure its fully initialized when the registration method runs.

Servlet-Specific Dynamic Registration Method

For servlet registration scenarios, you can directly use the ServletContext to register servlets dynamically without going through the general bean definition process:

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.Map;

@Configuration
public class CustomProxyServletRegistrar implements ServletContextInitializer {

    private final Environment runtimeEnv;

    public CustomProxyServletRegistrar(Environment runtimeEnv) {
        this.runtimeEnv = runtimeEnv;
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        String proxyName = runtimeEnv.getProperty("proxy.name");
        String targetEndpoint = runtimeEnv.getProperty("proxy.targetUrl");
        createAndRegisterProxyServlet(servletContext, proxyName, targetEndpoint);
    }

    private void createAndRegisterProxyServlet(ServletContext servletContext, String proxyAlias, String targetUri) {
        ProxyServlet reverseProxyServlet = new ProxyServlet();
        ServletRegistration.Dynamic servletReg = servletContext.addServlet(proxyAlias + "ReverseProxyServlet", reverseProxyServlet);
        servletReg.setLoadOnStartup(1);
        servletReg.addMapping("/" + proxyAlias + "/*");
        servletReg.setInitParameters(Map.of(
                "targetUri", targetUri,
                "log", "true"
        ));
    }
}
Tags: Spring Boot

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.