注入 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 请求时,notificationServicenull

我认为这与 spring 将东西放入其容器有关,但由于某种原因无法为该功能执行此操作。我在控制器中有更多的功能,并且在所有这些功能中 notificationService 都被正确注入。我没有看到 createNotificationmarkNotificationAsRead 函数之间有任何真正的区别,而且我在 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.