Skip to content

Create CGLIB proxy instances honoring <constructor-arg> tags [SPR-3150] #7836

Closed
@spring-projects-issues

Description

@spring-projects-issues
Collaborator

Bruno Navert opened SPR-3150 and commented

This problem was encountered with Snapshot build 98 of version 2.0.3.

The following bean definition of a custom-scoped bean (the class is a backport of JDK 1.5's java.util.concurrent.ArrayBlockingQueue to work on JDK 1.4):

<bean class="edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue" scope="report">
    <constructor-arg value="3" />
    <aop:scoped-proxy />
</bean>

Failed with the error below. The class in question has no empty constructor, it needs an integer value, which is specified by <constructor-arg>.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue#55a338': Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Couldn't generate CGLIB subclass of class [class edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
Caused by: org.springframework.aop.framework.AopConfigException: Couldn't generate CGLIB subclass of class [class edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
Caused by: java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
	at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:718)
	at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:499)
	at net.sf.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)
	at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
	at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
	at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
	at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
	at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:193)
	at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:107)
	at org.springframework.aop.scope.ScopedProxyFactoryBean.setBeanFactory(ScopedProxyFactoryBean.java:109)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1074)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:430)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBeanDefinition(BeanDefinitionValueResolver.java:197)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1017)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:810)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:426)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:252)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:144)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:249)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:163)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:358)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:869)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:782)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:426)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:252)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:144)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:249)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:163)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:280)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:357)

I am using CGLib 2.1.3 (the one that ships with Hibernate 3.2.1)

This problem does not occur in version 2.0.0


Affects: 2.0.3

Issue Links:

15 votes, 19 watchers

Activity

spring-projects-issues

spring-projects-issues commented on Feb 22, 2007

@spring-projects-issues
CollaboratorAuthor

Juergen Hoeller commented

We do generally not support CGLIB proxies for classes that do not have default constructors, neither for standard AOP proxies nor for scoped proxies. (Well, to be exact, we do internally, but we're not exposing this functionality to applications.) Creating proxy instances with constructor arguments will usually lead to the proxy class internally initializing state, which is very undesirable. In general, only use CGLIB proxy classes that allow for 'stateless' construction.

In your case, I would strongly recommend to use

<aop:scoped-proxy proxy-target-class="false"/>

for such a bean, in particular since ArrayBlockingQueue implements a BlockingQueue interface anyway!

Juergen

spring-projects-issues

spring-projects-issues commented on Feb 22, 2007

@spring-projects-issues
CollaboratorAuthor

Bruno Navert commented

Point taken. I was forced to use CGLIB proxies due to another bug in 2.0.0, but that bug has since been fixed in 2.0.3 so it's not an issue anymore.
Thanks for the info.

spring-projects-issues

spring-projects-issues commented on Aug 30, 2009

@spring-projects-issues
CollaboratorAuthor

Kent Tong commented

Having a constructor that takes arguments does NOT imply that the object is stateful. For example, one singleton bean could refer to another singleton bean using constructor injection.

On the other hand, even if a default constructor is used, it is possible to be stateful (eg, establishing a DB connection in that constructor).

With the current behavior, it is impossible to create a CGLIB proxy for it if constructor injection is used. Therefore, I'd suggest change this behavior and give a warning in the documentation that the constructor will be executed twice.

spring-projects-issues

spring-projects-issues commented on Oct 14, 2011

@spring-projects-issues
CollaboratorAuthor

Tomasz Nurkiewicz commented

This limitation is even more annoying when working with Scala:

@Service
class Foo @Autowired() (bar: Bar) {

    def buzz() = bar.buzz()

}

Here I can inject dependencies via constructor and not only Scala compiler will automatically create private fields for each injected dependency, but also these fields will be final. However when CGLIB proxy is used I am forced to add dummy c-tor and keep it in sync with the primary one:

def this() {this(null)}
spring-projects-issues

spring-projects-issues commented on Apr 9, 2012

@spring-projects-issues
CollaboratorAuthor

Oleg Alexeyev commented

It basically means that constructor injection doesn't work if AOP is enabled and an object doesn't implement interfaces. Or do I miss something? This is now exactly the problem I faced with. And now have to get back to setter injection or create an interface, which don't really need.

spring-projects-issues

spring-projects-issues commented on Nov 15, 2012

@spring-projects-issues
CollaboratorAuthor

Ilya Kazakevich commented

Regular project has a lot of services. Most of them have final fields with pointers to other services. All of them are created using [constructor-arg] in xml.

Now I need to wrap them to implement logging and I do not want to use AspectJ weaving.
Why can't you support constructor parameters injection in Spring-AOP?

That works perfectly in CGLIB, so nothing prevents you from doing it. You only need to pass arguments to CGLIB.

BTW, you do have "Cglib2AopProxy#setConstructorArguments", why not to use it?

spring-projects-issues

spring-projects-issues commented on Jun 12, 2013

@spring-projects-issues
CollaboratorAuthor

Przemysław Pokrywka commented

Kent, Tomasz, Oleg and Ilya had a valid point. Even parameterless constructors can have side-effects, so clear warning in the docs would be useful. And with the warning in place there's no longer need to annoy users of constructor-based injection.

Spring was meant to be agnostic about setter/constructor injection, wasn't it?

spring-projects-issues

spring-projects-issues commented on Jul 24, 2013

@spring-projects-issues
CollaboratorAuthor

Andrzej Winnicki commented

Przemysław is right.

Constructor injection is becoming more and more popular. We need either clear warning in the docs: you can't use constructor-injection with CGLIB and AOP, or have this issue fixed.

spring-projects-issues

spring-projects-issues commented on Dec 13, 2013

@spring-projects-issues
CollaboratorAuthor

Juergen Hoeller commented

Let's consider this fixed as of 4.0 RC1, since #15223 introduced an ObjenesisCglibAopProxy that bypasses constructor invocation for a CGLIB proxy instance altogether, avoiding any kind of side effect from constructor invocation (even in case of a default constructor). This is used by default with an embedded version of Objenesis in Spring Framework 4.0 now.

Juergen

added
in: coreIssues in core modules (aop, beans, core, context, expression)
has: votes-jiraIssues migrated from JIRA with more than 10 votes at the time of import
on Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

has: votes-jiraIssues migrated from JIRA with more than 10 votes at the time of importin: coreIssues in core modules (aop, beans, core, context, expression)type: enhancementA general enhancement

Type

No type

Projects

No projects

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @jhoeller@spring-projects-issues

      Issue actions

        Create CGLIB proxy instances honoring <constructor-arg> tags [SPR-3150] · Issue #7836 · spring-projects/spring-framework