简体   繁体   English

@Autowired 或构造函数将非托管类注入 Spring 上下文 @Service 以进行更好的测试

[英]@Autowired or constructor inject non-managed classes into Spring context @Service for better testing

In a Spring Boot project, we access our non managed DAO classes through a static method:在 Spring 引导项目中,我们通过 static 方法访问我们的非托管 DAO 类:

MyDao myDao = DaoProvider.getMyDao();

We use this in Spring managed @Service classes:我们在 Spring 托管@Service类中使用它:

@Service
public class MyService {
    
    public void foo() {
        MyDao myDao = DaoProvider.getMyDao();
        myDao.bar();
        ....
    }
}

This makes it hard to write unit tests.这使得编写单元测试变得困难。 Static mocks are no option, because they are slowing down our build pipeline. Static 模拟是没有选择的,因为它们会减慢我们的构建管道。

We would rather have a solution with @Autowired or constructor dependency and some kind of configuration, that decides which class to inject:我们宁愿有一个具有@Autowired或构造函数依赖和某种配置的解决方案,它决定注入哪个 class:

@Service
public class MyService {
    
    @Autowired
    MyDao myDao;
    
    public void foo() {
        myDao.bar();
        ....
    }
}

Obviously, someone has to tell Spring what class to inject, is there a way to do so?:显然,必须有人告诉 Spring 什么 class 注入,有没有办法这样做?:

public class NonManagedSpringInjectionConfiguration {
    
    @Bean
    MyDao getMyDao() {
        return DaoProvider.getMyDao();
    }
}

You just have to specify the profil for example:您只需指定配置文件,例如:

@Configuration
public class NonManagedSpringInjectionConfiguration {
    
    @Bean
    @Profile("dev")
    MyDao getMyDao() {
        return DaoProvider.getMyDao();
    }
}

Option 1选项1

If you don't want to turn your MyDao into a Spring-managed Bean, your simpler and probably best option is to instead create the MyService Spring-managed Bean programmatically instead of relying on @Service annotation.如果您不想将MyDao变成 Spring 管理的 Bean,则更简单且可能是最好的选择是以编程方式创建MyService Spring 管理的 Bean,而不是依赖@Service注释。 First, just remove @Service from MyService and adjust its constructor to accept MyDao :首先,只需从MyService中删除@Service并调整其构造函数以接受MyDao

public class MyService {

    MyDao myDao;

    public MyService(MyDao myDao) {
        this.myDao = myDao;
    }
    
    public void foo() {
        myDao.bar();
        ....
    }
}

And now you just need to register a MyService bean using @Bean in a Configuration class as follows:现在您只需要在配置 class 中使用@Bean注册一个MyService bean,如下所示:

@Configuration
public class WhateverConfiguration {
    
    @Bean
    MyService myService() {
        return new MyService(DaoProvider.getMyDao());
    }
}

Option 2选项 2

If instead there is a possibility to make MyDao a Spring-managed Bean, then just keep your NonManagedSpringInjectionConfiguration as is:相反,如果有可能使MyDao成为 Spring 管理的 Bean,那么只需保持NonManagedSpringInjectionConfiguration

@Configuration
public class NonManagedSpringInjectionConfiguration {
    
    @Bean
    MyDao getMyDao() {
        return DaoProvider.getMyDao();
    }
}

But then rely on constructor injection instead of on @Autowired .但是然后依靠构造函数注入而不是@Autowired It makes MyService easier to unit test and also explicitly defines that MyDao is mandatory for MyService to work properly.它使MyService更容易进行单元测试,并明确定义MyDaoMyService正常工作所必需的。 In this case you would keep @Service and rely on Spring to instantiate it:在这种情况下,您将保留@Service并依靠 Spring 来实例化它:

@Service
public class MyService {

    MyDao myDao;

    public MyService(MyDao myDao) {
        this.myDao = myDao;
    }

    public void foo() {
        myDao.bar();
        ....
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM