简体   繁体   English

Wicket @SpringBean 和 Spring @Autowired 通过构造函数注入

[英]Wicket @SpringBean and Spring @Autowired with injection via constructor

I have a Wicket panel in which I want to inject bean using @SpringBean我有一个 Wicket 面板,我想在其中使用 @SpringBean 注入 bean

public class SomePanel extends Panel {

  @SpringBean
  private BlogSummaryMailGenerator blogSummaryMailGenerator;

}

But this BlogSummaryMailGenerator has injection via constructor defined like this:但是这个 BlogSummaryMailGenerator 通过如下定义的构造函数进行注入:

@Component
public class BlogSummaryMailGenerator {

  private BlogRepository blogRepository;
  private BlogPostRepository blogPostRepository;

  @Autowired
  public BlogSummaryMailGenerator(BlogRepository blogRepository,
                                BlogPostRepository blogPostRepository) {
    this.blogRepository = blogRepository;
    this.blogPostRepository = blogPostRepository;
  }
}

And when SomePanel is instantiated I am getting an exception当 SomePanel 被实例化时,我得到一个异常

Caused by: java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:721) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:499) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) ~[cglib-3.1.jar:na]
at org.apache.wicket.proxy.LazyInitProxyFactory.createProxy(LazyInitProxyFactory.java:191) ~[wicket-ioc-7.2.0.jar:7.2.0]

Adding empty no-args constructor to the BlogSummaryMailGenerator solves this issue but adding such code only to make injection work is wrong and I would like to avoid it.向 BlogSummaryMailGenerator 添加空的无参数构造函数解决了这个问题,但是添加这样的代码只是为了使注入工作是错误的,我想避免它。

Any suggestions how to make @SpringBean work with beans using injection via constructor?关于如何通过构造函数使用注入使 @SpringBean 与 bean 一起工作有什么建议吗?

The real problem is in CGLIB. 真正的问题在于CGLIB。 It requires a default constructor to be able to create the proxy instance. 它需要一个默认构造函数才能创建代理实例。 The real Spring bean is created separately by Spring and has no such restrictions. 真正的Spring bean是由Spring单独创建的,没有这样的限制。 The default constructor needed by CGLIB could be even private as far as I remember. 就我记忆而言,CGLIB所需的默认构造函数甚至可以是private

Another solution is to use an interface for this bean. 另一种解决方案是为此bean使用接口。 Then Wicket will use JDK Proxy instead of CGLIB and in this case there is no need of default constructor in the implementation. 然后Wicket将使用JDK代理而不是CGLIB,在这种情况下,实现中不需要默认构造函数。

Solution

To be able to take advantage of constructor injection for @SpringBean in Wicket components you just have to add Objenesis to your compile time dependencies. 为了能够在Wicket组件中利用@SpringBean的构造函数注入,您只需将Objenesis添加到编译时依赖项中。

Explanation 说明

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. Objenesis是一个鲜为人知的字节代码操作库,(与与Wicket一起提供的CGLIB相反)能够为没有默认(无args)构造函数的类创建代理对象。 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. 如果将它作为编译依赖项添加到项目中,那么Wicket将切换它的内部延迟初始化代理创建逻辑,以便在实例化代理时利用Objenesis (而不是CGLIB ,它不需要args构造函数)。 Unfortunately this feature is not documented but I can confirm it works in my case. 不幸的是,这个功能没有记录,但我可以确认它适用于我的情况。

The error message is pretty clear. 错误消息非常清楚。 Wicked trying to create instance of proxy class for BlogSummaryMailGenerator with CGLIB library. Wicked尝试使用CGLIB库为BlogSummaryMailGenerator创建代理类实例。 Since you didn't (or you can't) provide arguments to constructor, it looking for contstructor with no arguments. 既然你没有(或你不能)为构造函数提供参数,它就会查找没有参数的contstructor。 But it can't, and you get error. 但它不能,你得到错误。

Just replace constructor injection with property injection, and create no argument constructor: 只需用属性注入替换构造函数注入,并创建无参数构造函数:

@Component
public class BlogSummaryMailGenerator {

  @Autowired
  private BlogRepository blogRepository;

  @Autowired
  private BlogPostRepository blogPostRepository;

  public BlogSummaryMailGenerator() {}

}

Actually, you do not need to declare an empty constructor. 实际上,您不需要声明一个空构造函数。 I did it just for clarity. 我这样做只是为了清楚。 Note, that BlogRepository and BlogPostRepository should be declared as beans (marked with @Component annotation, or created as @Bean in Spring configuration). 请注意, BlogRepositoryBlogPostRepository应声明为bean(用@Component注释标记,或在Spring配置中创建为@Bean )。

UPDATE: 更新:

When you add SpringComponentInjector in your WebApplication.init() , you can specify false for third paramter, which means 'wrapInProxies'. WebApplication.init()添加SpringComponentInjector时,可以为第三个参数指定false ,这意味着'wrapInProxies'。 Wicket will never wrap Spring beans in porxy, and you can use @Autowired for constructors. Wicket永远不会将Spring bean包装在porxy中,你可以使用@Autowired作为构造函数。

@Override
public void init()
{

    super.init();

    AnnotationConfigApplicationContext springContext = 
         new AnnotationConfigApplicationContext();
    springContext.register(SpringConfig.class);
    springContext.refresh();
    getComponentInstantiationListeners().add(new SpringComponentInjector(this, 
         springContext, false));
}

The correct way to solve this is not to add Objenesis to your project, but to inject interfaces instead of concrete implementations, as @martin-g already explained (of course, we do not always have the privilege to be able to do the right thing, but when we do, we should do it).解决这个问题的正确方法不是将Objenesis添加到您的项目中,而是注入接口而不是具体实现,正如@martin-g 已经解释过的那样(当然,我们并不总是有特权能够做正确的事情,但当我们这样做时,我们应该这样做)。

I have a project that started to give the exact same error and stack after a library update I still don't exactly understand (complete Maven dependency hell, I inherited it, go easy on me).我有一个项目在库更新后开始给出完全相同的错误和堆栈我仍然不完全理解(完整的 Maven 依赖地狱,我继承了它,go 对我来说很容易)。 The reason was that I was creating a Spring request-scoped bean from a concrete subclass of ListModel<MyClass> and Wicket was hell bent on wrapping that class into a lazy loaded proxy, which it couldn't do because there was no zero-args-constructor.原因是我正在从ListModel<MyClass>的具体子类创建一个 Spring 请求范围的 bean,而 Wicket 一心想将 class 包装到一个延迟加载的代理中,但它不能这样做,因为没有零参数-构造函数。

I fixed it by changing the configuration class to create a named instance of IModel<List<MyClass>> and by defining the injected dependency using the name.我通过更改配置 class 来创建IModel<List<MyClass>>的命名实例并使用该名称定义注入的依赖项来修复它。

In the configuration class:在配置 class 中:

@Bean(name = "namedModel")
@RequestScope
public IModel<List<MyClass>> myObjectList() {
    return new MyClass(parameters);
}

In the component:在组件中:

@Inject
@Named("namedModel")
private IModel<List<MyClass>> myModel;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM