Modifying Prototype Spring Beans

Version 2

    In this installment of my "no more overlays" series, I had a need to use a custom MailProcessor.

     

    The MailProcessors are configured on the EmailSender object in spring-managerContext.xml:

     

        <bean id="emailSender" class="com.jivesoftware.community.mail.outgoing.EmailSender" scope="prototype">
            <constructor-arg index="0">
                <util:list>
                    <ref bean="defaultOutgoingMailProcessor"/>
                    <ref bean="replyToMessageProcessor"/>
                    <ref bean="debugOutgoingMailProcessor"/>
                </util:list>
            </constructor-arg>
            <constructor-arg index="1">
                <util:list>
                </util:list>
            </constructor-arg>
            <property name="authenticationProvider" ref="authenticationProvider"/>
        </bean>
    
    
    
    
    

     

    Since this bean is scoped prototype, we can't modify all instances of it.

     

    Resolving not to overlay the spring bean definition in my spring config, I decided to programmatically modify the spring bean definition at run time instead.  Using a BeanFactoryPostProcessor, I grabbed the spring BeanDefinition and directly added my MessageProcessor instance to the constructor arg:

     

    public class AddMailProcessorToEmailSender implements BeanFactoryPostProcessor {
    
        private MailProcessor mailProcessor;
    
        public void setMailProcessor(MailProcessor mailProcessor) {
            this.mailProcessor = mailProcessor;
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition("emailSender");
         
            ConstructorArgumentValues constructorValues = beanDefinition.getConstructorArgumentValues();
            ConstructorArgumentValues.ValueHolder preProcessorListValueHolder = constructorValues.getArgumentValue(0, Set.class);
         
            BeanDefinitionHolder preProcessorListBeanDefinitionHolder = (BeanDefinitionHolder) preProcessorListValueHolder.getValue();
            BeanDefinition preProcessorListBeanDefinition =  preProcessorListBeanDefinitionHolder.getBeanDefinition();
         
            @SuppressWarnings("unchecked")
            List<Object> list = (List<Object>) preProcessorListBeanDefinition.getPropertyValues().getPropertyValue("sourceList").getValue();
         
            list.add(0, mailProcessor);    
        }
    }
    
    
    

     

     

    This is obviously specific to injecting a MailProcessor in to the first argument of the emailSender bean but you can see how this pattern can be used to modify other spring beans without resorting to overlaying the actual configuration.

     

    Thanks to Kevin.Conaway for the great write-up!