Pre-processing Spring Beans of Prototype Scope

Sometimes you have a type of bean in the Spring framework which you want to create by every asking for it - in the scope prototype.

Pre-processing of those prototypes only one and in the beginning  could be tricky.

Let's consider an annotation which tell you someting about the setting of the beans. For example which alternative names is your GUI box connected to. You want to create a proper bean based on its alternative name of course without featching all the beans every time and looking for the right one (of course you can do it by lazy caching, but that's still not the best solution). Or what if you want to get a list of all the alternative names of course withou the need to create the beans first?

This short article will tell you how to do it. 


Consider the following annotations:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Scope(value = SCOPE_PROTOTYPE)
public @interface Box {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Scope(value = SCOPE_PROTOTYPE)
public @interface AlternativeNames {
    AlternativeName[] value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(AlternativeNames.class)
@Documented
public @interface AlternativeName {
    String value();
}

Then you have defined the base annotation @Box which tell the Spring framework that this class is a bean, @AlternativeNames which allows you to put the @AlternativeName annotation more times (Repeating Annotations).

Based on the definitions you can create a class like this:

@Box
@AlternativeName("my-box")
@AlternativeName("box-of-mine")
public class MyBox {
    // ... some code goes here
}

Now it's time for the pre-processor:

@Component
public class BoxContextListener implements ApplicationListener<ContextRefreshedEvent> {

    private final ConfigurableListableBeanFactory factory;

    @Autowired
    public BoxContextListener(ConfigurableListableBeanFactory factory) {
        this.factory = factory;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        String[] beanDefinitionNames = event.getApplicationContext().getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            try {
                BeanDefinition beanDefinition = factory.getBeanDefinition(beanDefinitionName);

                String originalClassName = beanDefinition.getBeanClassName();
                if (originalClassName != null && !originalClassName.isEmpty()) {
                    try {
                        final Class<?> originalClass = Class.forName(originalClassName);
                        if (originalClass.isAnnotationPresent(Box.class)) {

                            if (originalClass.isAnnotationPresent(AlternativeNames.class) 
                                  || originalClass.isAnnotationPresent(AlternativeName.class)) { 
                                AlternativeName[] mappings = originalClass.getAnnotationsByType(AlternativeName.class);
                                for (AlternativeName alternativeMapping : mappings) {
                                    String alternativeName = alternativeMapping.value();
                                    // do someting with the alternative name ...
                                }
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        // ...
                    }
                }
            } catch (BeansException e) {
                // ...
            }
        }
    }
}

That's the way how to get values from the annotation of the Spring prototype beans in the pre-processing.

I hope it helped!