我用 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)}
更新: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
此代码也有效:
@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
之前起作用。
但我不知道,只是猜测。无论如何,好问题。
从对象本身获取 AOP 代理问题建议使用
AopContext.currentProxy()
的替代 hacky 方法,可能适合特殊情况。
顺便说一下,解决自调用问题的更优雅的解决方案是使用 AspectJ Load-Time Weaving 作为您的事务代理(或您正在使用的任何 AOP 引入的代理)。
例如,使用注解驱动的事务管理,可以使用“aspectj”模式,如下:
<tx:annotation-driven mode="aspectj" />
请注意,默认模式是“代理”(即 JDK 动态代理)。
问候,
山姆
鉴于上面的代码,我没有看到循环依赖。 您将一些 Service 实例注入 UserService 中。 注入的Service的实现不一定需要是另一个UserService,因此不存在循环依赖。
我不明白为什么你要将 UserService 注入到 UserService 中,但我希望这是一个理论上的尝试或类似的尝试。
另一种方法:
@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);
}
它进行了注入,但服务不在代理内部,并且我的方法没有在新线程上运行。通过这种方法,它可以按照我想要的方式工作。
这是我针对中小型项目的解决方案。没有 AspectJ 或应用程序上下文魔法,它可以与单例和构造函数注入一起使用,并且非常容易测试。
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {
private final PersonDao _personDao;
@Autowired
public PersonDao(PersonDao personDao) {
_personDao = personDao;
}
}
看起来 spring 创建并配置了一个对象,然后将其放置在 bean 查找上下文中。但是,就 Java 而言,我认为它会创建对象,并将其与名称和配置期间联系起来,当通过上下文中找到的名称查找对象时。
最短的实现:
import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Service
@Service
class MyService(
@Lazy val self: MyService
)