春天自我注射

时间:2011-03-01 09:36:33

标签: java spring dependency-injection ioc-container

我使用Spring {x尝试了以下代码,但BeanNotFoundException失败了,它应该根据我之前提出的问题的答案 - Can I inject same class using Spring?

@Service
public class UserService implements Service{
    @Autowired
    private Service self;
}

因为我在Java 6中尝试这个,所以我发现以下代码工作正常:

@Service(value = "someService")
public class UserService implements Service{
    @Resource(name = "someService")
    private Service self;
}

但我不明白它如何解决循环依赖。

编辑:
这是错误消息。 OP在其中一个答案的评论中提及它:

  

引起:org.springframework.beans.factory.NoSuchBeanDefinitionException:没有为依赖项找到[com.spring.service.Service]类型的匹配bean:期望至少有一个bean可以作为此依赖项的autowire候选者。依赖注释:{@ org.springframework.beans.factory.annotation.Autowired(required = true)}

8 个答案:

答案 0 :(得分:42)

更新:2016年2月

Spring Framework 4.3将正式支持

自动装配。可以在此GitHub commit中看到实现。


你不能自己动手的最终原因是Spring的DefaultListableBeanFactory.findAutowireCandidates(String, Class, DependencyDescriptor)方法的实现明确排除了这种可能性。这在以下代码摘录中可见:

for (String candidateName : candidateNames) {
    if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, descriptor)) {
        result.put(candidateName, getBean(candidateName));
    }
}

仅供参考:bean的名称(即尝试自动装配的bean)为beanName。该bean实际上是一个autowire候选者,但上面的if-condition返回false(因为candidateName实际上等于beanName)。因此,您根本无法自动将bean自动装配(至少不是Spring 3.1 M1)。

现在至于这是否是从语义上讲的意图,这是另一个问题。 ;)

我会问Juergen,看看他有什么要说的。

此致

Sam(核心春天提交者)

P.S。我打开了一个Spring JIRA问题,考虑使用@Autowired支持自动装配类型。请在此处观看或投票支持此问题:https://jira.springsource.org/browse/SPR-8450

答案 1 :(得分:33)

此代码也适用:

@Service
public class UserService implements Service {

    @Autowired
    private ApplicationContext applicationContext;

    private Service self;

    @PostConstruct
    private void init() {
        self = applicationContext.getBean(UserService.class);
    }
}

我不知道为什么,但是如果创建了,那么Spring似乎可以从ApplicationContext获取bean,但不能初始化@Autowired在初始化之前工作,但找不到相同的bean。因此,@Resource可能在@Autowired之后和@PostConstruct之前有效。

但我不知道,只是推测。无论如何,好问题。

答案 2 :(得分:1)

鉴于上面的代码,我没有看到循环依赖。 您将一些Service实例注入UserService。 注入的Service的实现不一定需要是另一个UserService,因此没有循环依赖。

我不明白你为什么要把UserService注入UserService,但我希望这是一个理论上的尝试等等。

答案 3 :(得分:1)

顺便说一下,自我调用问题的更优雅的解决方案是为事务代理(或者您使用的任何AOP引入的代理)使用AspectJ Load-Time Weaving。

例如,使用注释驱动的事务管理,您可以使用“aspectj”模式,如下所示:

<tx:annotation-driven mode="aspectj" />

请注意,默认模式是“proxy”(即JDK动态代理)。

此致

萨姆

答案 4 :(得分:1)

Get AOP proxy from the object itself问题提出了AopContext.currentProxy()替代hacky方法,可能适用于特殊情况。

答案 5 :(得分:0)

看起来spring创建并配置一个对象,然后将它放在bean查找上下文中。但是,在Java的情况下,我认为它创建了对象并将其与名称和配置期间绑定,当通过上下文中找到的名称查找对象时。

答案 6 :(得分:0)

另一个方法:

@EnableAsync
@SpringBootApplication
public class Application {

    @Autowired
    private AccountStatusService accountStatusService;

    @PostConstruct
    private void init() {
        accountStatusService.setSelf(accountStatusService);
    }
}

@Service
public class AccountStatusService {
    private AccountStatusService self;

    public void setSelf(AccountStatusService self) {
        this.self = self;
    }
}

与此相关,您的服务将在代理中。我这样做是为了在其内部使用异步方法。

我尝试了@sinuhepop解决方案:

@PostConstruct
private void init() {
    self = applicationContext.getBean(UserService.class);
}

它进行了注入,但是该服务不在代理内部,并且我的方法未在新线程上运行。有了这个方法,它就可以像我想要的那样工作。

答案 7 :(得分:0)

这是我针对中小型项目的解决方案。没有AspectJ或应用程序上下文魔术,它可用于单例和构造函数注入,并且非常易于测试。

@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {

    private final PersonDao _personDao;

    @Autowired
    public PersonDao(PersonDao personDao) {
        _personDao = personDao;
    }
}