Flying Wolf created WICKET-6911:
-----------------------------------

             Summary: wicket-spring throws an error when a spring bean uses 
ctor injection
                 Key: WICKET-6911
                 URL: https://issues.apache.org/jira/browse/WICKET-6911
             Project: Wicket
          Issue Type: Bug
          Components: wicket-spring
    Affects Versions: 9.4.0
         Environment: wicket-core: 9.4.0
wicket-spring: 9.4.0
spring-context: 5.3.9
            Reporter: Flying Wolf
         Attachments: HomePage.html, HomePage.java, MyComponent.java, 
MyService.java, WicketApplication.java, pom.xml

Since Spring 4, the use of constructor injection for dependencies is possible 
and recommended, instead of setter injection. So most Spring users probably use 
constructor dependency injection in their Spring components.

But this causes an exception when used with wicket-spring. When tried, cglib 
used by wicket-spring, will throw an error with message: "Superclass has no 
null constructors but no arguments were given". So basically every Spring user 
will by default get this error, without a clear solution.

However, there exists a simple but undocumented solution or feature, which is 
mentioned in [https://stackoverflow.com/a/36324808/1039774]
{quote}_Objenesis_ is a little and less known byte code manipulation library 
which (in opposite to _CGLIB_ provided together with _Wicket_) is able to 
create a proxy object for a class which has no default (no args) constructor. 
If you add it as a compile dependency to your project then _Wicket_ will switch 
it's internal lazily initializable proxy creation logic to take advantage of 
_Objenesis_ (instead _CGLIB_ which requires no args constructor) while 
instantiating a proxy. Unfortunately this feature is not documented but I can 
confirm it works in my case.
{quote}
Wicket-spring internally checks for the availability of Objenesis. If present 
in the class path, it will use Objenesis instead of cglib, when a no 
no-argument-constructor is present. See 
[https://github.com/apache/wicket/blob/509bd2fec696ff44d231874477ebcd2549437fef/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java#L170]

This feature or solution is not mentioned in the documentation 
([https://ci.apache.org/projects/wicket/guide/9.x/single.html#_integrating_wicket_with_spring]),
 neither in the exception, causing the error to appear like a bug.

To solve this problem, one or many of the following points should be taken for 
the Wicket repository code base:
 * Objenesis provided as a dependency in wicket-spring pom.xml
 * Objenesis should be mentioned in the documentation of 
[https://ci.apache.org/projects/wicket/guide/9.x/single.html#_integrating_wicket_with_spring]
 * LazyInitProxyFactory should mention about Objenesis in the exception, when 
Objenesis is not present and only argument-constructors are present.

h1. Example code causing the exception:
{code:java}
@Component
public class MyComponent {}

// This Spring bean isn't accepted by wicket-spring, because of ctor injection
@Service
public class MyService {
    private MyComponent myComponent;

    public MyService(MyComponent myComponent) {
        this.myComponent = myComponent;
    }
}

public class HomePage extends WebPage {
    @SpringBean
    private MyService myService;

    public HomePage(final PageParameters parameters) {
        super(parameters);
    }
}

public class WicketApplication extends WebApplication {
        //...

    @Override
    public void init() {
        super.init();

        AnnotationConfigApplicationContext context = new 
AnnotationConfigApplicationContext();
        context.scan("myapp.spring");
        context.refresh();

        getComponentInstantiationListeners().add(new 
SpringComponentInjector(this, context));
    }
}
{code}
Exception:
{quote}Exception:
 Last cause: Superclass has no null constructors but no arguments were given
 WicketMessage: Can't instantiate page using constructor 'public 
myapp.wicket.HomePage(org.apache.wicket.request.mapper.parameter.PageParameters)'
 and argument ''. An exception has been thrown during construction!

Root cause:
 java.lang.IllegalArgumentException: Superclass has no null constructors but no 
arguments were given
 at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:931)
 at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:631)
 at 
net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
 at 
net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
 at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
 at 
net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
 at 
net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
 at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
 at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
 at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
 at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
 at 
net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
 at 
net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
 at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
 at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
 at 
org.apache.wicket.proxy.LazyInitProxyFactory.createProxy(LazyInitProxyFactory.java:191)
 at 
org.apache.wicket.spring.injection.annot.AnnotProxyFieldValueFactory.getFieldValue(AnnotProxyFieldValueFactory.java:166)
 at org.apache.wicket.injection.Injector.inject(Injector.java:111)
 at 
org.apache.wicket.spring.injection.annot.SpringComponentInjector.inject(SpringComponentInjector.java:124)
 ...
 at java.base/java.lang.Thread.run(Thread.java:834)

Complete stack:

org.apache.wicket.WicketRuntimeException: Can't instantiate page using 
constructor 'public 
myapp.wicket.HomePage(org.apache.wicket.request.mapper.parameter.PageParameters)'
 and argument ''. An exception has been thrown during construction!
 at 
org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:194)
 ...
 at 
org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:277)

java.lang.reflect.InvocationTargetException
 at 
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
 Method)
 at 
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
 at 
java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
 at 
org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:171)
 ...
 at 
org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:277)

java.lang.RuntimeException: error while injecting object [[Page class = 
myapp.wicket.HomePage, id = 6, render count = 0]] of type 
[myapp.wicket.HomePage]
 at org.apache.wicket.injection.Injector.inject(Injector.java:122)
 at 
org.apache.wicket.spring.injection.annot.SpringComponentInjector.inject(SpringComponentInjector.java:124)
 at 
org.apache.wicket.spring.injection.annot.SpringComponentInjector.onInstantiation(SpringComponentInjector.java:130)
 at 
org.apache.wicket.application.ComponentInstantiationListenerCollection$1.notify(ComponentInstantiationListenerCollection.java:38)
 at 
org.apache.wicket.application.ComponentInstantiationListenerCollection$1.notify(ComponentInstantiationListenerCollection.java:34)
 at 
org.apache.wicket.util.listener.ListenerCollection.notify(ListenerCollection.java:80)
 at 
org.apache.wicket.application.ComponentInstantiationListenerCollection.onInstantiation(ComponentInstantiationListenerCollection.java:33)
 at org.apache.wicket.Component.<init>(Component.java:690)
 at org.apache.wicket.MarkupContainer.<init>(MarkupContainer.java:179)
 at org.apache.wicket.Page.<init>(Page.java:171)
 ...
 at 
org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:277)
{quote}
h1. Example code without the problem:

Add Objenesis dependency to pom.xml:
{code:java}
<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>3.2</version>
</dependency>
{code}
The rest of the code can stay the same. (Only the presence of Objenesis solves 
the problem.) 



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to