Dynamically creating Spring Beans at runtime

The translation has been prepared specifically for future students of the course “Spring Framework Developer”


This article about dynamic bin creation in five years became the most popular on my blog (over 9300 views). It’s time to update it. I also added example on Github

Spring Bean dynamics on Github
Spring Bean dynamics on Github

Once in a training session, I was asked: “Is it possible to create a Spring Bean dynamically so that you can choose an implementation at runtime.” Since at compile time it is not yet known which bean should be created. The application should solve this based on the properties file.

1. Let’s create an annotation to mark the method that should create the object dynamically:

package your.package;

@Retention(RetentionPolicy.RUNTIME)
public @interface InjectDynamicObject {
}

2. Next, we use it in the method that should create the object:

@Service
public class CustomerServiceImpl {
    private Customer dynamicCustomerWithAspect;
    
    @InjectDynamicObject
    public Customer getDynamicCustomerWithAspect() {
        return this.dynamicCustomerWithAspect;
    }
}

3. Let’s write an aspect with Pointcut and Advise that modifies the object returned by the method in step 2:

@Component
@Aspect
public class DynamicObjectAspect {
    // This comes from the property file
    @Value("${dynamic.object.name}")
    private String object;
    @Autowired
    private ApplicationContext applicationContext;
    
    @Pointcut("execution(@com.lofi.springbean.dynamic.
        InjectDynamicObject * *(..))")
    public void beanAnnotatedWithInjectDynamicObject() {
    }
    @Around("beanAnnotatedWithInjectDynamicObject()")
    public Object adviceBeanAnnotatedWithInjectDynamicObject(
        ProceedingJoinPoint pjp) throws Throwable {   
        pjp.proceed();
        
        // Create the bean or object depends on the property file  
        Object createdObject = applicationContext.getBean(object);
        return createdObject;
    }
}

4. We write a class that should be returned from @InjectDynamicObject. The class name is configured in the properties file. In this example, I wrote two implementations Customer: CustomerOneImpl and CustomerTwoImpl:

@Component("customerOne")
public class CustomerOneImpl implements Customer {
    @Override
    public String getName() {
        return "Customer One";
    }
}

application.properties
dynamic.object.name=customerOne

5. We write the test:

@RunWith(SpringRunner.class)
@SpringBootTest
public class CustomerServiceImplTest {
    @Autowired
    private CustomerServiceImpl customerService;
    @Test
    public void testGetDynamicCustomerWithAspect() {
        // Dynamic object creation
        logger.info("Dynamic Customer with Aspect: " +
            customerService.getDynamicCustomerWithAspect()
            .getName());
}

But there is still an easier way to do it. No aspects and AspectJ, just pure Spring. You can simply store all implementations in a Map and get the required implementation from it. We did this in the eXTra Client application. As an example, you can look at the implementation PluginsLocatorManager… Spring will automatically inject a Map with the bean name (String) and the bean itself.

“… Even typed Maps can be injected automatically if the key type is String. The Map values ​​will contain all the beans of the expected type, and the keys contain the corresponding bean names.”

For more details see in Spring documentation

@Service
public class CustomerServiceImpl {
    
    // We inject the customer implementations into a Map
    @Autowired
    private Map<String, Customer> dynamicCustomerWithMap;
    
    // This comes from the property file as a key for the Map
    @Value("${dynamic.object.name}")
    private String object;
    public Customer getDynamicCustomerWithMap() {
        return this.dynamicCustomerWithMap.get(object);
    }
}

Learn more about the Spring Framework Developer course. here.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *