With Spring, how can I inject a child bean in a parent bean?

时间:2016-04-04 17:54:44

标签: java spring

I have a Bean that wants to inject its child classes like so:

Parent:

package test;

@Component
public class Parent {
  @Autowired
  Child child;

  public Child getChild() {
    return child;
  }
}

Child:

package test;

@Component
public class Child extends Parent {
}

This results in the following error:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [test.Child] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency

Removing the extends Parent bit causes everything to work as expected, so it looks like Spring is unable to find the Child, as though the Parent is shadowing it somehow. How do I configure Spring to correctly wire these child instances? I'm using Java Class Configuration, like the following:

@Configuration
@ComponentScan(basePackages = "test")
public class AppConfig {
}

I have tried playing around with the @Qualifier annotation and assigning distinct names within AppConfig as in the following, which did not help:

@Bean(name = "parent")
public Parent parent() {
  return new Parent();
}

@Bean(name = "child")
public Child child() {
  return new Child();
}

I'm not sure what the missing ingredient is in order to make Spring see the Child class as its own distinct entity. Is this not possible?

2 个答案:

答案 0 :(得分:2)

我通过修改Parent的定义解决了这个问题,如下所示:

package test;

@Primary
@Component
public class Parent {
  @Resource
  Child child;

  public Child getChild() {
    return child;
  }
}

并修改孩子如下:

package test;

@Component("child")
public class Child extends Parent {
}

我已将@Autowired注释替换为@Resource,它在类型之前尝试按名称查找,并为子类添加了显式名称。为了在尝试自动装配父级时消除孩子的父母歧义,我还将@Primary注释添加到父级。

我仍然不完全理解为什么按类型查找失败,迫使我使用@Resource来代替查找bean。

答案 1 :(得分:0)

Since Child extends Parent, a Parent must be constructed to construct a Child but a Parent requires a Child for construction. This is a circular dependency; avoid creating circular dependencies in your code.

You will need to refactor your code. This may not be possible in your code-base, but if you can invert the relationship so Parent extends Child (instead of the reverse as you have it now), you'll be able to inject:

package hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @Autowired
    private Parent parent;
    @Bean
    Child makeChild() { return new Child(); }
}
@Component
class Parent extends Child {
    @Autowired
    private Child child;

    public Child getChild() { return this.child; }
}
class Child {
}

Alternatively, refactor the code that both Child and Parent need into another class and inject that code into the Parent and Child:

package hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @Autowired
    private Parent parent;
    @Bean
    Child makeChild() { return new Child(); }
    @Bean
    Shared makeShared() { return new Shared(); }
}
@Component
class Parent {
    @Autowired
    private Shared shared;
    @Autowired
    private Child child;
    public Child getChild() { return this.child; }
}
class Child {
    @Autowired
    private Shared shared;
}
class Shared {
}
相关问题