Spring - 在Java配置类

时间:2016-03-23 02:19:59

标签: java spring dependency-injection

我目前正在开发一个具有相互依赖服务的应用程序,通过Spring Java Configuration类注入,有点像这样:

@Configuration
public class ExampleConfiguration {

    @Bean
    public IFirstService firstService() {
        return new FirstServiceImpl();
    }

    @Bean
    public ISecondService secondService() {
        return new SecondServiceImpl();
    }
}

@Service
public class FirstServiceImpl implements IFirstService{
    ...
}

@Service
public class SecondServiceImpl implements ISecondService{

    @Inject
    private IFirstService firstService;

    ...
}

这是按预期工作的,每个服务的单个实例都在整个应用程序中创建和注入。但是,我有兴趣转换为构造函数注入 - 似乎它可以为单元/模拟测试模式提供更好的支持。据我了解,它会将SecondServiceImpl代码更改为:

@Service
public class SecondServiceImpl implements ISecondService {

    private IFirstService firstService;

    @Inject
    public SecondServiceImpl(IFirstService firstService){
        this.firstService = firstService;
    }

    ...
}

我遇到的问题是确定它如何与上面的Configuration类交互/工作。我见过的所有例子都是这样的:

@Configuration
public class ExampleConfiguration {

    @Bean
    public IFirstService firstService() {
        return new FirstServiceImpl();
    }

    @Bean
    public ISecondService secondService() {
        return new SecondServiceImpl(firstService());
    }
}

但是这似乎会破坏整个应用程序中应该注入一个IFirstService实例的想法,因为每次调用firstService()都会实例化一个新的IFirstService实例。

我不知道我是否遗漏了一个关于Spring如何处理这样的事情,或者错误依赖注入错误的细节。任何建议将不胜感激!

编辑:

虽然接受的答案是正确的,但我最近发现有一种更强大的方法可以做到这一点 - 您可以将所需的项目指定为使用@Bean注释的方法的参数,并且它将从相同或其他可用配置。所以上面会变成:

@Configuration
public class ExampleConfiguration {

    @Bean
    public IFirstService firstService() {
        return new FirstServiceImpl();
    }

    @Bean
    public ISecondService secondService(IFirstService firstService) {
        return new SecondServiceImpl(firstService);
    }
}

请注意,如果需要特定的bean id,@Qualifier注释可以与成员参数一起使用

3 个答案:

答案 0 :(得分:1)

您的配置类不会按原样使用。 Spring会将您的配置包装到所谓的代理类中。此代理类将拦截原始配置类的所有方法调用,这些方法标记为@Bean注释。让我们考虑一下您的配置代码:

@Configuration
public class ExampleConfiguration {

    @Bean
    public IFirstService firstService() {
        return new FirstServiceImpl();
    }

    @Bean
    public ISecondService secondService() {
        return new SecondServiceImpl(
              firstService() //actually here is will be invoked method of proxy class
        );
    }
}

以下是firstService()注释@Bean。因此,因为您的配置类包装到代理中,所以当您调用firstService()时,它将调用代理方法,但不调用原始配置类的方法。

代理类的简化逻辑如下所示。当代理类拦截方法的调用时,它会检查(在单例的情况下):是否已经创建了bean的实例。如果bean存在,则返回此bean。如果bean不存在,则通过调用原始配置类的方法来创建新实例。

这意味着每次调用firstService() 都不会实例化新的IFirstService实例。它将在第一次调用时创建,并且所有后续调用都将返回相同的实例。

答案 1 :(得分:1)

@Qualifier注释是你的朋友:

@Configuration
public class ExampleConfiguration {

    @Bean(name="first-service")                       // #1 - put a name on it
    public IFirstService firstService() {
        return new FirstServiceImpl();
    }

    @Bean
    public ISecondService secondService(
                          @Qualifier("first-service") // #2 - inject it here
                          IFirstService service {
        return new SecondServiceImpl(service);
    }
}

请注意,此示例使用默认的单例原型,它可能适用于所有情况,也可能不适用。例如,您可能需要在每次咨询Bean工厂时创建一个新的ISecondService实例,但希望使用IFirstService单例。或者您可能希望两者都充当原型bean。

然后,您必须明智地使用配置对象的范围作为整体或特定的bean声明(但现在这是另一个主题。)

编辑(使用@Named取代)

实际上,让我扩大我的回应。您可以使用@Qualifier,但我建议使用@Named,如下所示。

@Configuration
public class ExampleConfiguration {

    @Bean(name="first-service")                       // #1 - put a name on it
    public IFirstService firstService() {
        return new FirstServiceImpl();
    }

    @Bean
    public ISecondService secondService(
                          @Named("first-service") // #2 - inject it here
                          IFirstService service {
        return new SecondServiceImpl(service);
    }
}

为什么呢?好吧,我们有两个版本的@Qualifier注释:

  1. 一个是JSR-330附带的那个。
  2. 另一个是one with Spring
  3. JSR-330 @Qualifier不能像我的例子那样在参数中使用。它仅限于方法和属性。

    然而,我的示例中的一个是Spring中的一个,可以应用于参数。

    一般来说,我们希望坚持使用JSR-330注释(就像Spring注释一样。)为此,我们改为使用来自JSR-330的@Named,2)它等同于Spring的@Qualifier

答案 2 :(得分:0)

Spring使用CGLIB个代理来解决配置阶段的依赖注入问题。 firstService()的结果最初将成为一个代理,一旦所有依赖关系得到解决并适当注入,它将被合并为一个单例。