Reputation: 14999
I have the simplest of Spring configuration cases so I don't know why I don't find how this is done.
I have an interface
public interface UserService {
List<User> findAll();
}
with two implementations
@Component
public class SimpleUserService implements UserService {
private List<User> users = Arrays.asList(new User());
@Override
public List<User> findAll() {
return Collections.unmodifiableList(users);
}
}
and
@Repository
@Primary
public interface UserRepository extends JpaRepository<User, Integer>, UserService {
}
Note the @Repository
is marked @Primary
, so when I do this:
@RestController
public class SimpleUserController implements UserController {
@Autowired
private UserService userService;
@Override
@GetMapping("/users")
public List<User> getAllUsers() {
return userService.userResources;
}
}
it should be using the UserRepository
, no? Instead, I get
Field userService in com.studying.user.SimpleUserController required a single bean, but 2 were found:
UserDaoService: defined in file [...\SimpleUserService.class]
userRepository: defined in null
I get that Spring creates an actual class to implement the repository, so I guess that could be the issue, but is there a way to still do this?
As an additional question, how would I create two configurations, with one using the simple service and the other the repository? It's not like I can instantiate the repository in a @Bean
-annotated method in the Configuration. I'd add an @Autowired
to a private member of the configuration and return it, but that won't work for the same reasons as above.
Upvotes: 1
Views: 919
Reputation: 44535
For a Spring Data interface you need neither @Component
nor @Repository
. The repositories are detected by a different Spring Data mechanism, not the regular component scanning from Spring itself. That's also why your @Primary
annotation doesn't have any effect.
A solution could look like this:
public interface UserService {}
@Component
public class SimpleUserService implements UserService {}
@Component
public class JpaUserService implements UserService {
@Autowired
UserRepository userRepository
}
public interface UserRepository extends JpaRepository<User, Integer> {}
Then you have a clear separation between your repository layer with the Spring Data repository and your service layer, which uses the repository layer. You also don't mix regular Spring bean annotations and Spring Data repository interfaces, which will also simplify your work.
You can then e.g. move the SimpleUserService
into the test source folder, if it should only be available for unit tests, and mark it as @Primary
. Or you can use the @Conditional
annotation (https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html) to provide an even more sophisticated way to decide, which implementation you want to use.
Upvotes: 3