两个实现类实现同一个Service接口
public interface CustomUrlService {
List<ShopMetrics> getShopMetrics();
}
@Service
public class CustomUrlServiceImpl implements CustomUrlService {
@Override
public List<ShopMetrics> getShopMetrics() {
return null;
}
}
@Service
public class CustomUrlServiceImpl2 implements CustomUrlService {
@Override
public List<ShopMetrics> getShopMetrics() {
return Arrays.asList(new ShopMetrics(34L, 34L));
}
}
使用的时候如果使用接口作为类型,直接使用@Autowired
是无法找到具体是哪个bean的,因为@Autowired
默认是按照类型注入的,需要加上@Qualifier
注解指定实现类的bean id,如果@Service
中没有指定bean id(bean name),默认是类名的首字母小写作为bean name。当然也可以使用@Resource
注解,它默认是by name 注入的,所以变量名需要和待注入的实现类的bean name保持一致,如下所示:
@Autowired
@Qualifier("customUrlServiceImpl")
private CustomUrlService customUrlServiceImpl;
@Resource
private CustomUrlService customUrlServiceImpl2;
@TestConfiguration
我之前在springboot中写单元测试喜欢全部按照下面这种来写测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class CustomUrlServiceImplTest {}
但是@SpringBootTest
是把整个spring容器启动起来,然后把测试环境给你准备好,这样也是可以的,但是有时候只是显得有点有点笨重,看日志就知道整个应用不管和这个单元测试有关没关的都被启动了,而我们只是为了跑一个单元单元测试。
看了参考资料1之后,我才知道了单元测试不一定需要启动整个应用,层与层之间也可以并且应该隔离测试。如果上层测试需要用到下层的依赖,就使用mock的方式构造一个依赖。比如测试DAO层可以使用@DataJpaTest
注解;测试controller层可以使用@WebMvcTest
;测试Service层可以使用@TestConfiguration
把需要用到Bean依赖进来。下面是测试Service层的一个例子:
@RunWith(SpringRunner.class)
public class CustomUrlServiceImplTest {
@TestConfiguration
static class prepareCustomServices{
@Bean
public CustomUrlService getCustomUrlServiceImpl() {
return new CustomUrlServiceImpl();
}
@Bean
public CustomUrlService getCustomUrlServiceImpl2() {
return new CustomUrlServiceImpl2();
}
}
@Autowired
@Qualifier("getCustomUrlServiceImpl")
private CustomUrlService customUrlServiceImpl;
@Resource
private CustomUrlService getCustomUrlServiceImpl2;
@Test
public void getShopMetrics() {
assertNull(customUrlServiceImpl.getShopMetrics());
assertNotNull(getCustomUrlServiceImpl2.getShopMetrics());
}
}
现在再运行@Test
标注的单元测试就不会启动整个springboot容器中了。这里面需要注意的是使用 @Bean
放入到spring容器的的bean的默认id是方法的方法名而不再是类名。