注入 spring 控制器的服务在其中一项功能中不可用
Service injected into spring controller is not available in one of the functions
我正在编写 spring 控制器,它会注入一个 bean。
bean 已添加到配置中(我们对所有内容都使用 java 配置):
@Bean
public NotificationService notificationService() {
return new NotificationService();
}
服务本身注入的依赖项和函数很少:
public class NotificationService {
@Inject
NotificationRepository notificationRepository;
@Inject
ProjectRepository projectRepository;
@Inject
ModelMapper modelMapper;
public NotificationDto create(NotificationDto notificationDto) {
//convert to domain object, save, return dto with updated ID
return notificationDto;
}
public void markAsRead(Long id, String recipientNip) {
//find notification, update status
}
}
模型映射器几乎没有配置,只设置为严格。同时,存储库是扩展 JpaRepository
的接口,没有自定义功能。它们是由 @EnableJpaRepositories
.
找到的
最后我有尝试使用上面代码的控制器:
@RestController
@RequestMapping("/notifications")
public class NotificationController extends ExceptionHandlerController {
@Autowired
private NotificationService notificationService;
@PreAuthorize("isFullyAuthenticated() and hasRole('create_notification')")
@RequestMapping(method = RequestMethod.POST, consumes = MediaTypeExtension.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> createNotification(@Valid @RequestBody(required = true) final NotificationDto notification) {
this.notificationService.create(notification);
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.CREATED);
}
@PreAuthorize("isFullyAuthenticated() and hasRole('update_notification')")
@RequestMapping(value = "/{id}/read", method = RequestMethod.PUT)
private ResponseEntity<?> markNotificationAsRead(@PathVariable("id") Long id, @AuthenticatedContractor ContractorDto contractor) {
this.notificationService.markAsRead(id, contractor.getNip());
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.OK);
}
}
所有控制器都是通过@ComponentScan 添加的,基于它们的包。
如您所见,这两个函数都使用 notificationService
。当我在 /notifications
上为 create
发送 POST
时,notificationService
被正确注入。在同一个控制器中,当我在 /{id}/read
上执行 PUT
请求时,notificationService
是 null
。
我认为这与 spring 将东西放入其容器有关,但由于某种原因无法为该功能执行此操作。我在控制器中有更多的功能,并且在所有这些功能中 notificationService
都被正确注入。我没有看到 createNotification
和 markNotificationAsRead
函数之间有任何真正的区别,而且我在 google/stack 上什至找不到任何相关的东西。在所有情况下,由于配置错误,服务根本不会注入。
编辑
我尝试改变函数中的内容,直到它开始工作。我的最终代码如下所示:
@PreAuthorize("isFullyAuthenticated() and hasRole('update_notification')")
@RequestMapping(value = "{id}/read", method = RequestMethod.PUT)
public ResponseEntity<?> read(@PathVariable("id") Long id, @AuthenticatedContractor ContractorDto contractor) {
this.notificationService.markAsRead(id, contractor.getNip());
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.OK);
}
并且有效。老实说,我看不出与我的原始代码有什么不同,而且我已经盯着它看了大约一个小时了。导入也一样。
我还注意到(在不工作的代码上)虽然调试堆栈上来自控制器的所有函数都被标记为
NotificationController.functionName(arguments) line: x
非工作函数是:
NotificationController$$EnhancerBySpringCGLIB$d88bfe(NotificationController).markNotificationAsRead(ContractorDto) line: 86
为什么 spring CGLIB 增强了这个单一功能我不知道。我曾尝试查找它,但现在我空手而归。即使代码开始工作,我仍将问题悬而未决,以便找到根本原因。
您的方法 markNotificationAsRead
是私有的,这可能会导致问题。我刚刚遇到了与最终方法相同的问题 - 此消息出现在日志中:
2016-11-28 17:19:14.186 INFO 97079 --- [ main] o.s.aop.framework.CglibAopProxy : Unable to proxy method [public final java.util.Map com.package.controller.MyController.someMethod(javax.servlet.http.HttpServletResponse)] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
看起来在一种情况下我们看到了 CGLib 代理,而在另一种情况下 - 实际 class。其中只有一个注入了所有字段,看起来代理的所有字段都为空。但这没关系 - 关键是你的方法应该是 public 而不是最终的,以便被 @PreAuthorize 方法正确代理。
我也遇到了同样的问题。这都是由于使用了私有访问修饰符和@PreAuthorize。如果您不使其安全,则将控制器方法设为私有不会产生问题。但是,为了安全起见,请将其设置为 public.
我正在编写 spring 控制器,它会注入一个 bean。
bean 已添加到配置中(我们对所有内容都使用 java 配置):
@Bean
public NotificationService notificationService() {
return new NotificationService();
}
服务本身注入的依赖项和函数很少:
public class NotificationService {
@Inject
NotificationRepository notificationRepository;
@Inject
ProjectRepository projectRepository;
@Inject
ModelMapper modelMapper;
public NotificationDto create(NotificationDto notificationDto) {
//convert to domain object, save, return dto with updated ID
return notificationDto;
}
public void markAsRead(Long id, String recipientNip) {
//find notification, update status
}
}
模型映射器几乎没有配置,只设置为严格。同时,存储库是扩展 JpaRepository
的接口,没有自定义功能。它们是由 @EnableJpaRepositories
.
最后我有尝试使用上面代码的控制器:
@RestController
@RequestMapping("/notifications")
public class NotificationController extends ExceptionHandlerController {
@Autowired
private NotificationService notificationService;
@PreAuthorize("isFullyAuthenticated() and hasRole('create_notification')")
@RequestMapping(method = RequestMethod.POST, consumes = MediaTypeExtension.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> createNotification(@Valid @RequestBody(required = true) final NotificationDto notification) {
this.notificationService.create(notification);
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.CREATED);
}
@PreAuthorize("isFullyAuthenticated() and hasRole('update_notification')")
@RequestMapping(value = "/{id}/read", method = RequestMethod.PUT)
private ResponseEntity<?> markNotificationAsRead(@PathVariable("id") Long id, @AuthenticatedContractor ContractorDto contractor) {
this.notificationService.markAsRead(id, contractor.getNip());
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.OK);
}
}
所有控制器都是通过@ComponentScan 添加的,基于它们的包。
如您所见,这两个函数都使用 notificationService
。当我在 /notifications
上为 create
发送 POST
时,notificationService
被正确注入。在同一个控制器中,当我在 /{id}/read
上执行 PUT
请求时,notificationService
是 null
。
我认为这与 spring 将东西放入其容器有关,但由于某种原因无法为该功能执行此操作。我在控制器中有更多的功能,并且在所有这些功能中 notificationService
都被正确注入。我没有看到 createNotification
和 markNotificationAsRead
函数之间有任何真正的区别,而且我在 google/stack 上什至找不到任何相关的东西。在所有情况下,由于配置错误,服务根本不会注入。
编辑 我尝试改变函数中的内容,直到它开始工作。我的最终代码如下所示:
@PreAuthorize("isFullyAuthenticated() and hasRole('update_notification')")
@RequestMapping(value = "{id}/read", method = RequestMethod.PUT)
public ResponseEntity<?> read(@PathVariable("id") Long id, @AuthenticatedContractor ContractorDto contractor) {
this.notificationService.markAsRead(id, contractor.getNip());
final HttpHeaders headers = new HttpHeaders();
return new ResponseEntity<>(headers, HttpStatus.OK);
}
并且有效。老实说,我看不出与我的原始代码有什么不同,而且我已经盯着它看了大约一个小时了。导入也一样。
我还注意到(在不工作的代码上)虽然调试堆栈上来自控制器的所有函数都被标记为
NotificationController.functionName(arguments) line: x
非工作函数是:
NotificationController$$EnhancerBySpringCGLIB$d88bfe(NotificationController).markNotificationAsRead(ContractorDto) line: 86
为什么 spring CGLIB 增强了这个单一功能我不知道。我曾尝试查找它,但现在我空手而归。即使代码开始工作,我仍将问题悬而未决,以便找到根本原因。
您的方法 markNotificationAsRead
是私有的,这可能会导致问题。我刚刚遇到了与最终方法相同的问题 - 此消息出现在日志中:
2016-11-28 17:19:14.186 INFO 97079 --- [ main] o.s.aop.framework.CglibAopProxy : Unable to proxy method [public final java.util.Map com.package.controller.MyController.someMethod(javax.servlet.http.HttpServletResponse)] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
看起来在一种情况下我们看到了 CGLib 代理,而在另一种情况下 - 实际 class。其中只有一个注入了所有字段,看起来代理的所有字段都为空。但这没关系 - 关键是你的方法应该是 public 而不是最终的,以便被 @PreAuthorize 方法正确代理。
我也遇到了同样的问题。这都是由于使用了私有访问修饰符和@PreAuthorize。如果您不使其安全,则将控制器方法设为私有不会产生问题。但是,为了安全起见,请将其设置为 public.