When Spring cannot inject an object, the constructor is not always the problem. Sometimes the container never registered the object in the first place. Component scanning and Java configuration both register beans, but they suit different ownership and assembly decisions.
Component scanning finds application classes
The diagram shows two ways to register MemberService. Choose the intended path; do not accidentally register the same service through both.
Classes annotated with @Component, @Service, @Repository, or @Controller are candidates for automatic registration within the scan scope.
@Service
class MemberService {
private final MemberRepository repository;
MemberService(MemberRepository repository) {
this.repository = repository;
}
}Constructor injection exposes required dependencies and makes it straightforward to pass a fake implementation in a unit test. Field injection may be shorter, but it obscures when an object is fully usable and can make replacement in tests harder.
This example assumes MemberRepository is also registered. If MemberService is outside the scan scope or lacks @Service, its constructor can be correct while the container still has no service bean to inject. Check registration and the starting package for scanning first. The Spring component-scanning reference explains which annotated classes become candidates.
Use @Bean for explicit assembly or external types
You can assemble the same service explicitly instead of scanning it. If you choose this path, remove @Service from the earlier class and register it once:
@Configuration
class AppConfig {
@Bean
MemberService memberService(MemberRepository repository) {
return new MemberService(repository);
}
}Spring supplies the already registered MemberRepository parameter. This pattern is useful when you need to choose an implementation or control creation in one place. A library type such as Clock, whose source you do not annotate, can be registered through @Bean too. The important question is who should own the object's creation and selection, not which syntax looks newer.
Why not scan every package?
Placing the main application class in a top-level package brings its subpackages into the default scan scope. Widening the scope indiscriminately can also register test configuration or beans from another module. If you leave both @Service and this @Bean method active, there may be two beans of the same type. Before hiding an injection failure with @Primary, decide which registration path is intended.
When implementations vary by environment, make the name or condition explicit. Do not hard-code production secrets in a configuration class; readable configuration code is not a safe secret store.
Confirm registration with a context test
Bean registration can fail at startup, so a small context test can be useful:
@SpringBootTest
class ContextTest {
@Autowired MemberService memberService;
@Test
void service_is_registered() {
assertThat(memberService).isNotNull();
}
}A passing test proves the service is registered, not which route registered it. When testing @Bean, remove the scanned annotation; when testing scanning, remove the configuration method. Also check the bean count so duplicated registration is not overlooked. Service rules can remain in lighter unit tests rather than making every test load a full context.
Key takeaways
Component scanning works well for application classes with role annotations. Java @Bean configuration suits external types and explicit assembly. Use constructor injection, keep the scan boundary intentional, and check registration paths before adding more annotations to fix a duplicate-bean error.

