nigel magnay opened SPR-7582 and commented
We have an application that uses a fairly standard pattern to declare a proxy for a bean:
<bean id="AService" name="aService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="AServiceTXN"/></property>
<property name="interceptorNames">
<list>
<value>Interceptor1</value>
<value>Interceptor2</value>
...
<value>InterceptorN</value>
</list>
</property>
</bean>
The instantiation order causes this bean to be being created in response to an unrelated autowiring request, e.g:
@Autowired(required=false)
List<SomeOther> someOthers;
Which is internally executing getBeanNamesForType()
.
This causes a chain of objects to be created, and the ProxyFactoryBean
for AService
to be called with getObject()
.
However, this getObject()
call is in advance of Spring having set the interceptorNames
. Thus the proxy is created, and the advisorChainInitialized
variable set to true
with no interceptor names.
There's several things here
- I don't see why spring is calling
getObject
on an uninitialized factory; it ought to have set the properties first. I don't believe there are circular dependencies here (since it is initiated by a request to find all instances of an unrelated class). I can remove the problem (for one bean, but this applies to many) by creating a derived type ofProxyFactoryBean
that sets the target andinterceptorNames
on construction... but this seems to be a nasty workaround as I'd have to set it for many, many beans. If it believes there to be a circular dependency I'd expect an exception. - If
setInterceptorNames
finds thatadvisorChainInitialized == true
, it ought to throw an exception, since it is clearly too late to influence the interception chain (so for example security interceptors mysteriously fail to work).
Affects: 2.5.6
Attachments: - spring-SPR-7582-workaround.patch (1.71 kB)
Referenced from: pull request https://github.com/spring-projects/spring-framework/pull/1477
2 votes, 3 watchers
Comment From: spring-projects-issues
nigel magnay commented
I have tried this with Spring 3, and it's a problem there too.
In fact, there are also scenarios where ProxyFactoryBean is re-entered because of a loop (because of executing getBeanNamesForType(), not a real dependency loop), where you then end up with many multiples of the interceptors created.
Comment From: spring-projects-issues
Alar Kvell commented
I can confirm this with Spring 2.0.8
I used a workaround: in afterPropertiesSet method, set advisorChainInitialized to false.
Comment From: spring-projects-issues
Bulk closing outdated, unresolved issues. Please, reopen if still relevant.
Comment From: marijnm
This is still an issue
Comment From: sbrannen
@marijnm, would you be willing to provide a simple example project that demonstrates the issue -- for example, if the form of a GitHub repository or ZIP file that we can download and use to reproduce the issue?
Comment From: mp5er
I'm facing the same problem. Detailed description: https://stackoverflow.com/questions/66133760/spring-proxyfactorybean-interceptor-advice-sometimes-not-called-missing
The ProxyFactoryBean also tries multiple times to create the Proxy while it's not initialized:
org.springframework.beans.factory.FactoryBeanNotInitializedException: Cannot determine target class for proxy
at org.springframework.aop.framework.ProxyFactoryBean.getSingletonInstance(ProxyFactoryBean.java:322)
at org.springframework.aop.framework.ProxyFactoryBean.getObject(ProxyFactoryBean.java:252)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:135)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1674)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getObjectForBeanInstance(AbstractAutowireCapableBeanFactory.java:1248)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:257)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:303)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:676)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:188)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1340)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1186)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:303)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1674)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1426)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
.....
After the first call of getObject
the advisorChainInitialized
is set to true and the adivces are not added.
Maybe advisorChainInitialized should be set to false here before throwing the exception.
Spring version: 5.1.15.RELEASE OS: Linux JDK: OpenJDK 11.0.9
Comment From: kennymacleod
I'm seeing behaviour that looks very like this (i.e. getObject
being called from within Spring before setInterceptorNames
, and advisorChainInitialized
being set to true and short-circuiting the logic), but I can't come up with anything simpler than what @mp5er provided.
I'm using Spring Framework 5.1.18.