兩個實現類實現同一個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是方法的方法名而不再是類名。
