Spring自注入

问题描述 投票:0回答:9

我用 Spring 3.x 尝试了以下代码,但失败了,并显示

BeanNotFoundException
,它应该根据我之前提出的问题的答案 - 我可以使用 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:预计至少有 1 个有资格作为此依赖项的自动装配候选者的 bean。依赖注解:{@org.springframework.beans.factory.annotation.Autowired(required=true)}

java spring dependency-injection ioc-container
9个回答
67
投票

更新: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 实际上是一个自动装配候选者,但上面的 if 条件返回 false(因为
candidateName
实际上等于
beanName
)。因此,您根本无法自动装配 bean 本身(至少从 Spring 3.1 M1 开始)。

现在至于这是否是语义上的预期行为,这是另一个问题。 ;)

我会问于尔根,看看他会说什么。

问候,

Sam(核心 Spring 提交者)

附注我已经打开了一个 Spring JIRA 问题,考虑使用 @Autowired 按类型支持自我自动装配。欢迎在这里观看或投票:https://jira.springsource.org/browse/SPR-8450


44
投票

此代码也有效:

@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,如果是created,但不是initialized
@Autowired
在初始化之前工作,并且找不到相同的bean。因此,
@Resource
可能在
@Autowired
之后和
@PostConstruct
之前起作用。

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


3
投票

从对象本身获取 AOP 代理问题建议使用

AopContext.currentProxy()
的替代 hacky 方法,可能适合特殊情况。


1
投票

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

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

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

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

问候,

山姆


1
投票

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

我不明白为什么你要将 UserService 注入到 UserService 中,但我希望这是一个理论上的尝试或类似的尝试。


1
投票

另一种方法:

@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);
}

它进行了注入,但服务不在代理内部,并且我的方法没有在新线程上运行。通过这种方法,它可以按照我想要的方式工作。


1
投票

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

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

    private final PersonDao _personDao;

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

0
投票

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


0
投票

最短的实现:

import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Service

@Service
class MyService(
  @Lazy val self: MyService
)
© www.soinside.com 2019 - 2024. All rights reserved.