You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@struts.apache.org by "Chris Cranford (JIRA)" <ji...@apache.org> on 2015/10/21 15:14:27 UTC

[jira] [Comment Edited] (WW-4554) Spring BeanPostProcessor(s) are called twice for Struts constructed objects.

    [ https://issues.apache.org/jira/browse/WW-4554?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14966783#comment-14966783 ] 

Chris Cranford edited comment on WW-4554 at 10/21/15 1:13 PM:
--------------------------------------------------------------

It isn't the use of the {{autoWire}} method that is a concern but rather the use of {{initializeBean}}.  Per the spring documentation:
{noformat}
        /**
	 * Initialize the given raw bean, applying factory callbacks
	 * such as {@code setBeanName} and {@code setBeanFactory},
	 * also applying all bean post processors (including ones which
	 * might wrap the given raw bean).
	 * <p>Note that no bean definition of the given name has to exist
	 * in the bean factory. The passed-in bean name will simply be used
	 * for callbacks but not checked against the registered bean definitions.
	 * @param existingBean the existing bean instance
	 * @param beanName the name of the bean, to be passed to it if necessary
	 * (only passed to {@link BeanPostProcessor BeanPostProcessors})
	 * @return the bean instance to use, either the original or a wrapped one
	 * @throws BeansException if the initialization failed
	 */
{noformat}

Inside the SpringObjectFactory you'll notice:
{noformat}
@Override
    public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
        Object bean;

        try {
            // Decide to follow autowire strategy or use the legacy approach which mixes injection strategies
            if (alwaysRespectAutowireStrategy) {
                // Leave the creation up to Spring
                bean = autoWiringFactory.createBean(clazz, autowireStrategy, false);
                injectApplicationContext(bean);
                return injectInternalBeans(bean);
            } else if (enableAopSupport) {
                bean = autoWiringFactory.createBean(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
                bean = autoWireBean(bean, autoWiringFactory);
                bean = autoWiringFactory.initializeBean(bean, bean.getClass().getName());
                return bean;
            } else {
                bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
                bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
                bean = autoWiringFactory.initializeBean(bean, bean.getClass().getName());
                bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());
                return autoWireBean(bean, autoWiringFactory);
            }
        } catch (UnsatisfiedDependencyException e) {
            LOG.error("Error building bean", e);
            // Fall back
            return autoWireBean(super.buildBean(clazz, extraContext), autoWiringFactory);
        }
    }
{noformat}
The last else-block is the issue because the {{BeanPostProcessor}} s are being manually invoked on the autowire factory yet {{initializeBean}} specifically does that per the documentation and has since at least Spring 3.0.5.


was (Author: crancran):
It isn't the use of the {{autoWire}} method that is a concern but rather the use of {{initializeBean}}.  Per the spring documentation:
{noformat}
        /**
	 * Initialize the given raw bean, applying factory callbacks
	 * such as {@code setBeanName} and {@code setBeanFactory},
	 * also applying all bean post processors (including ones which
	 * might wrap the given raw bean).
	 * <p>Note that no bean definition of the given name has to exist
	 * in the bean factory. The passed-in bean name will simply be used
	 * for callbacks but not checked against the registered bean definitions.
	 * @param existingBean the existing bean instance
	 * @param beanName the name of the bean, to be passed to it if necessary
	 * (only passed to {@link BeanPostProcessor BeanPostProcessors})
	 * @return the bean instance to use, either the original or a wrapped one
	 * @throws BeansException if the initialization failed
	 */
{noformat}

Inside the SpringObjectFactory you'll notice:
{noformat}
@Override
    public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
        Object bean;

        try {
            // Decide to follow autowire strategy or use the legacy approach which mixes injection strategies
            if (alwaysRespectAutowireStrategy) {
                // Leave the creation up to Spring
                bean = autoWiringFactory.createBean(clazz, autowireStrategy, false);
                injectApplicationContext(bean);
                return injectInternalBeans(bean);
            } else if (enableAopSupport) {
                bean = autoWiringFactory.createBean(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
                bean = autoWireBean(bean, autoWiringFactory);
                bean = autoWiringFactory.initializeBean(bean, bean.getClass().getName());
                return bean;
            } else {
                bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
                bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
                bean = autoWiringFactory.initializeBean(bean, bean.getClass().getName());
                bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());
                return autoWireBean(bean, autoWiringFactory);
            }
        } catch (UnsatisfiedDependencyException e) {
            LOG.error("Error building bean", e);
            // Fall back
            return autoWireBean(super.buildBean(clazz, extraContext), autoWiringFactory);
        }
    }
{noformat}
The last else-block is the issue because the {{BeanPostProcessor}}s are being manually invoked on the autowire factory yet {{initializeBean}} specifically does that per the documentation and has since at least Spring 3.0.5.

> Spring BeanPostProcessor(s) are called twice for Struts constructed objects.
> ----------------------------------------------------------------------------
>
>                 Key: WW-4554
>                 URL: https://issues.apache.org/jira/browse/WW-4554
>             Project: Struts 2
>          Issue Type: Bug
>          Components: Plugin - Spring
>    Affects Versions: 2.3.24
>            Reporter: Chris Cranford
>             Fix For: 2.3.25
>
>
> It appears that the SpringObjectFactory in the xwork core at lines 194-197 manually yet when calling initializeBean on the autowire factory, the spring framework automatically invokes these processors too which lead to the following post processor's callbacks being invoked twice for both the before and after handlers.  
> I confirmed that both Sprnig 3.0.5 and 4.2.1 have called the bean post processors when the initializeBean function is called.  See a simple NullBeanPostProcessor implementation below that can be used as a simple test of post processor invocation.
> {code:title=NullBeanPostProcessor.java|borderStyle=solid}
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> import org.springframework.beans.BeansException;
> import org.springframework.beans.factory.config.BeanPostProcessor;
> /**
>  * @since	7.0.0
>  */
> public class NullBeanPostProcessor implements BeanPostProcessor {
> 	private static final Logger LOGGER = LoggerFactory.getLogger(NullBeanPostProcessor.class);
> 	
> 	/**
> 	 * {@inheritDoc}
> 	 */
> 	@Override
> 	public Object postProcessBeforeInitialization(Object bean, String beanName)
> 	throws BeansException {
> 		LOGGER.debug("Before Initialization for {} ({})", beanName, bean);
> 		return bean;
> 	}
> 	/**
> 	 * {@inheritDoc}
> 	 */
> 	@Override
> 	public Object postProcessAfterInitialization(Object bean, String beanName)
> 	throws BeansException {
> 		LOGGER.debug("After Initialization for {} ({})", beanName, bean);
> 		return bean;
> 	}
> }
> {code}



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)