Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
spring-projects-issues opened this issue Feb 11, 2007 · 9 comments
Assignees
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Feb 11, 2007

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

@spring-projects-issues
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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
Copy link
Collaborator Author

spring-projects-issues commented Dec 13, 2013

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

@spring-projects-issues spring-projects-issues added type: enhancement A general enhancement in: core Issues in core modules (aop, beans, core, context, expression) has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 4.0 RC1 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants