[英]Why Spring Boot is not finding a @Service bean trying to perform autowiring? the bean exist but it can't find it
我在使用JUnit的Spring Boot 2.2.5.RELEASE項目上遇到了一個奇怪的問題。
我嘗試詳細解釋我的問題:
1)我定義了一個服務。 首先,我定義了一個名為OrderService的接口,如下所示:
package com.dgs.soc.service;
import java.util.List;
import com.dgs.soc.excelapi.dto.Order;
public interface OrderService {
public List<Order> getOrdersList();
}
然后我定義了它的實現,目前名為OrderServiceImpl 的東西非常簡單:
package com.dgs.soc.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.dgs.soc.excelapi.dto.Order;
import com.dgs.soc.repository.OrderRepository;
@Service
public class OrderServiceImpl {
public List<Order> getOrdersList() {
List<Order> result = new ArrayList<Order>();
return result;
}
}
如您所見,此類由@Service注釋進行注釋。
問題是使用JUnit ,我有這個測試類:
package com.dgs.soc.excelapi.integration;
// IMPORTS LIST
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { Application.class })
@WebAppConfiguration
@ActiveProfiles(profiles = { "no-liquibase" })
public class ExcelResourceIntegrationTest {
@Autowired
OrderServiceImpl orderService;
@Test
public void getOrdersListRepositoryTest() {
List<Order> ordersList = orderService.getOrdersList();
assertThat(ordersList).isNotEmpty();
}
}
在這里我遇到了一個奇怪的行為:執行getOrdersListRepositoryTest()測試方法我獲得了這個異常
2020-03-23 04:42:24.783 ERROR 5281 --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@7fbdb894] to prepare test instance [com.dgs.soc.excelapi.integration.ExcelResourceIntegrationTest@2ad6895a]
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.dgs.soc.excelapi.integration.ExcelResourceIntegrationTest': Unsatisfied dependency expressed through field 'orderService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.dgs.soc.service.OrderServiceImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:393) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:119) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:43) ~[spring-boot-test-autoconfigure-2.2.5.RELEASE.jar:2.2.5.RELEASE]
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) ~[junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) ~[junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) ~[junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) ~[junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) ~[junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) ~[junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) ~[junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) ~[spring-test-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.junit.runner.JUnitCore.run(JUnitCore.java:137) ~[junit-4.12.jar:4.12]
at org.junit.runner.JUnitCore.run(JUnitCore.java:115) ~[junit-4.12.jar:4.12]
at org.junit.vintage.engine.execution.RunnerExecutor.execute(RunnerExecutor.java:40) ~[junit-vintage-engine-5.5.2.jar:5.5.2]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[na:na]
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133) ~[na:na]
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497) ~[na:na]
at org.junit.vintage.engine.VintageTestEngine.executeAllChildren(VintageTestEngine.java:80) ~[junit-vintage-engine-5.5.2.jar:5.5.2]
at org.junit.vintage.engine.VintageTestEngine.execute(VintageTestEngine.java:71) ~[junit-vintage-engine-5.5.2.jar:5.5.2]
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229) ~[junit-platform-launcher-1.5.2.jar:1.5.2]
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197) ~[junit-platform-launcher-1.5.2.jar:1.5.2]
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211) ~[junit-platform-launcher-1.5.2.jar:1.5.2]
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191) ~[junit-platform-launcher-1.5.2.jar:1.5.2]
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137) ~[junit-platform-launcher-1.5.2.jar:1.5.2]
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:89) ~[.cp/:na]
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41) ~[.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542) ~[.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770) ~[.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464) ~[.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210) ~[.cp/:na]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.dgs.soc.service.OrderServiceImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
... 49 common frames omitted
2020-03-23 04:42:24.816 INFO 5281 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2020-03-23 04:42:24.819 INFO 5281 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-03-23 04:42:24.846 INFO 5281 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-03-23 04:42:24.875 INFO 5281 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
它說沒有 'com.dgs.soc.service.OrderServiceImpl' 類型的合格 bean 可用,但是,正如您在之前的代碼中看到的那樣,此 bean 存在並且它由@Service批注注釋以允許自動裝配!!!
在這個項目中,定義了一個由@RestController注釋的類,用於公開 API。 如果進入這個類,我嘗試自動裝配同一個服務類,如下所示:
@Description(value = "Resource layer for handling REST requests.")
@RestController
@RequestMapping("api")
public class ExcelResource {
@Autowired
OrderServiceImpl orderService;
.......................................................
.......................................................
.......................................................
}
現在我嘗試將應用程序作為Spring Boot 應用程序執行我遇到了同樣的問題:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field orderService in com.dgs.soc.excelapi.resources.ExcelResource required a bean of type 'com.dgs.soc.service.OrderServiceImpl' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.dgs.soc.service.OrderServiceImpl' in your configuration.
但是com.dgs.soc.service.OrderServiceImpl bean 再次存在,它由@Service注釋。
為什么我會遇到這個問題? 我錯過了什么? 我該如何嘗試解決這個問題? 我在想我有一些參考問題或類似的問題,但我不知道
EDIT-1:將@ComponentScan("com.dgs.soc.service")放在我的Application類的頂部它似乎可以工作,但我有一些疑問!!!
所以這是我現在的應用程序類:
package com.dgs.soc.excelapi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan("com.dgs.soc.service")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
}
它可以工作,但是在沒有 ** @ComponentScan("com.dgs.soc.service")** 注釋的情況下它也可以工作,因為這個類是由@SpringBootApplication 注釋的,你可以在這里閱讀:
自動啟用@ComponentScan:對應用程序所在的包進行@Component掃描(見最佳實踐)
因此,該應用程序位於com.dgs.soc.excelapi中,理論上我希望組件掃描也必須在沒有@ComponentScan的顯式定義的情況下正常工作
為什么? 有什么想法?
嘗試將@ComponentScan
注釋放在主應用程序類的頂部
@ComponentScan("base.package.name")
您的問題的答案是您沒有用於 spring-boot 自動掃描以獲取您的服務類的正確包結構。
SpringBootApplication
注解只允許掃描它所在的包及其子包。
如果你有SpringBootApplication
類com.acme.app
包,那么所有與春天開機注解類將得到掃描的包com.acme.app
及其任何子包的即com.acme.app.services
, com.acme.app.controllers
等。但是,如果您有一個包com.acme.services
那么任何 spring 注釋都不會從此包中自動掃描。
你有兩個選擇;
您可以修改包結構以允許 spring-boot 自動掃描所有注釋。 在您的情況下,將您的Application
類從com.dgs.soc.excelapi
移動到com.dgs.soc
。 或者你可以移動所有其他包,即com.dgs.soc.excelapi
下的com.dgs.soc.excelapi
。
您可以使用SpringBootApplication
注釋上的 @ComponentScan 或scanBasePackages
屬性明確列出要掃描的包。
問題如下:
您的應用程序類位於“com.dgs.soc.excelapi”包中
@SpringBootApplication 為@Services/@Components/@Repositories 掃描該包及其所有子包。
您將所有服務放在完全不同的包中(“com.dgs.soc.service”),因此 Spring Boot 不會找到它們。 因此@ComponentScan 注釋有效。
修復:重新排序您的包結構。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.