Я столкнулся со странной проблемой, когда не могу автоматически подключить @Repository
к @Service
... Вот код:
@SpringBootApplication
@EntityScan("com.example.api")
@EnableJpaRepositories("com.example.api")
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
Это базовый контроллер
package com.example.api.user.controller;
...
@RestController
public class UserController {
@PostMapping(
value = "/signup",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> userSignUp(
@RequestBody SignUpRequest request,
SignUpHandler handler
) {
return new ResponseEntity<>(handler.handle(request), HttpStatus.OK);
}
И сам сервис:
package com.example.api.user.handler;
...
@Service
public class SignUpHandler {
private final UserCrudRepository repository;
@Autowired
public SignUpHandler(UserCrudRepository repository) {
this.repository = repository;
}
public SignUpResponse handle(SignUpRequest request) {
User user = new User(...);
this.repository.save(user);
// then it does something and returns data,
// not important for the question
}
}
И репозиторий:
package com.example.api.user.repository;
...
@Repository
public interface UserCrudRepository extends CrudRepository<User, Long> {
Optional<User> findByEmail(String email);
}
И хотя все соответствует документам и другим ответам на SO, я все еще не могу вызвать this.repository.save(user)
:
java.lang.NullPointerException: Cannot invoke \"com.example.api.user.repository.UserCrudRepository.save(Object)\" because \"this.repository\" is null
@EnableJpaRepositories("com.example.api.user.repository")
, не помогло. Однако я считаю, что Spring должен справиться с этим сам.SignUpHandler
не создается вручную, он внедряется в метод, так что все должно быть в порядке, верно?User
есть аннотация @Entity
.Я думаю, вам следует объявить SignUpHandler
в классе UserController
вместо того, чтобы получать его из параметров метода.
@Autowired
private SignUpHandler signUpHandler;
затем
return new ResponseEntity<>(signUpHandler.handle(request), HttpStatus.OK);
Хм, это работает. Но почему это работает, а внедрение метода — нет? Actuator фактически показывает экземпляр UserCrudRepository
, если он введен через метод.
Параметры метода контроллера берутся из параметров http-запроса, а не из контекста Spring.
К сожалению, другие ответившие не удосужились объяснить, почему это не работает, поэтому мне пришлось углубиться в документацию. В любом случае, именно такого ответа следует ожидать от сообщества SO:
Spring явно определяет разрешенные аргументы метода контроллера в своей документации. В конце таблицы написано:
Любой другой аргумент. Если аргумент метода не соответствует ни одному из предыдущих значений в этой таблице и является простым типом (как определено
BeanUtils#isSimpleProperty
), он разрешается как@RequestParam
. В противном случае это разрешается как@ModelAttribute
.
Следовательно, когда я пытаюсь ввести SignUpHandler
, он разрешается как @ModelAttribute
, и Spring пытается привязать к нему параметры запроса, как если бы это был объект модели. Поэтому @Repository
не вводится.
Чтобы это работало, SignUpHandler
следует ввести через конструктор контроллера.
Я не работаю активно со Spring, поэтому не знал этого, прошу прощения, я не провел исследование должным образом.
Есть ли у
SignUpHandler
еще один конструктор? SpringModelAttributeMethodProcessor
не смог бы создать экземпляр (модели) типаSignUpHandler
с помощью этого конструктора.