You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by "Sven Meier (Jira)" <ji...@apache.org> on 2021/07/31 21:24:00 UTC

[jira] [Updated] (WICKET-6911) wicket-spring throws an error when a spring bean uses ctor injection

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

Sven Meier updated WICKET-6911:
-------------------------------
    Issue Type: Improvement  (was: Bug)
      Priority: Minor  (was: Major)

> 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
>            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)