[ 
https://issues.apache.org/jira/browse/WICKET-6911?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Sven Meier reassigned WICKET-6911:
----------------------------------

    Assignee: Sven Meier

> 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: Improvement
>          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
>            Assignee: Sven Meier
>            Priority: Minor
>         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