Java Spring Boot 从服务调用 Rest Controller 方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51781348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 00:15:08  来源:igfitidea点击:

Spring Boot call a Rest Controller method from a service

javaspringspring-bootspring-rabbitmq

提问by Benjamin Mwendwa Munyoki

I am using Spring Boot to call a rest controller method from a service. When the method gets called, I get the error java.lang.NullPointerException The broad scenario is, my service receives a payload from RabbitMQ queue and extracts the contents of the payload which it should then pass to the controller to be saved into the database. The queue part works(I can receive messages from the queue and extract the contents). Also the database part works. The problem is calling the controller method from the service.

我正在使用 Spring Boot 从服务调用休息控制器方法。当该方法被调用时,我得到错误 java.lang.NullPointerException 广泛的场景是,我的服务从 RabbitMQ 队列接收一个有效负载并提取有效负载的内容,然后它应该传递给控制器​​以保存到数据库中。队列部分有效(我可以从队列接收消息并提取内容)。数据库部分也有效。问题是从服务调用控制器方法。

Here us my Rest Controller

这里是我的休息控制器

@RestController
@RequestMapping("/auth")
public class AuthController implements AuthService {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    AuthRepository authRepository;


    public AuthModel addAuthenticatable(AuthModel auth){
        auth.setCreatedAt(DateTimeUtility.getDateTime());
        return authRepository.save(auth);
    }
}

My service code:

我的服务代码:

public class QueueListener extends AuthController implements MessageListener{
    private String identifier;
    private JSONArray globalObject;
    private int userId;
    private String pin;

    @Autowired
    AuthController authController;

    @Override
    public void onMessage(Message message) {
        String msg = new String(message.getBody());
        String output = msg.replaceAll("\\", "");
        String jsonified = output.substring(1, output.length()-1);

        JSONArray obj = new JSONArray(jsonified);
        this.globalObject = obj;
        this.identifier = obj.getJSONObject(0).getString("identifier");
        resolveMessage();
    }
    public void resolveMessage() {
        if(identifier.equalsIgnoreCase("ADD_TO_AUTH")) {
            for(int i = 0; i < globalObject.length(); i++){ 
                JSONObject o = globalObject.getJSONObject(i);
                this.userId = Integer.parseInt(o.getString("userId"));
                this.pin = o.getString("pin");
            }

            AuthModel authModel = new AuthModel();
            authModel.setUserId(userId);
            authModel.setPin(pin);

            authController.addAuthenticatable(authModel);
        }
    }
}

The error occurs when I call the method addAuthenticatable() which is in AuthController. Any help will be appreciated.

当我调用 AuthController 中的 addAuthenticatable() 方法时发生错误。任何帮助将不胜感激。

采纳答案by Eirini Graonidou

I hope this does not go out of topic, but generally what we want to achieve is a sort of an onion architecture. The dependencies should have one direction.

我希望这不会偏离主题,但通常我们想要实现的是一种洋葱架构。依赖项应该有一个方向。

Your controller is an integration point for your application. You want per REST to trigger the executionof some piece of logic. Your controller should not extend classes or implement interfaces, that have to do with the business logic. This part belongs to another layer.

您的控制器是您的应用程序的集成点。您希望每个 REST触发某些逻辑的执行。您的控制器不应扩展与业务逻辑有关的类或实现接口。这部分属于另一层。

Everything that is about logic belongs to services:

与逻辑有关的一切都属于服务:

@Service
public class AuthService {
    @Autowired
    private AuthRepository authRepository;

    private String attribute;

    public boolean isAuthenticated(String username) {
        authRepository.doSomething();
        //implementation of the logic to check if a user is authenticated.
    }

   public boolean authenticate(String username, char[] password) {
       // implementation of logic to authenticate.
       authRepository.authenticate();
   }

   public AuthModel save(AuthModel model) {
       //implementation of saving the model
   }

}

Extracting your logic in the service layer, made things reusable. Now you could inject the service in a controller

在服务层提取您的逻辑,使事情可重用。现在您可以将服务注入controller

@RestController
@RequestMapping("/auth")
public class AuthController {

   @Autowired
   private AuthService authService;

   public AuthModel addAuthenticatable(AuthModel auth){
       //process input etc..
       return authService.save(auth);
   }
}

or in a amqListener

或在 amqListener

@Component
public class QueueListener implements MessageListener {
   @Autowired
   private AuthService authService;

   @Autowired
   private SomeOtherService otherService;

   @Override
   public void onMessage(Message message) {
      JSONArray array = processInput();

      JSONArray obj = new JSONArray(jsonified);
      String identifier = obj.getJSONObject(0).getString("identifier");
      // extract the business logic to the service layer. Don't mix layer responsibilities
      otherService.doYourThing(obj, identifier);

      resolveMessage();
  }

  private JSONArray processInput(Message message) {
     String msg = new String(message.getBody());
     String output = msg.replaceAll("\\", "");
     String jsonified = output.substring(1, output.length()-1);


}

and your configuration, so that you can let spring know where to look for the annotated classes.

和您的配置,以便您可以让 spring 知道在哪里查找带注释的类。

@Configuration
@ComponentScan({"your.service.packages"})
@EntityScan(basePackages = "your.model.package")
@EnableJpaRepositories("your.repository.packages")
@EnableRabbit // probaby
@EnableWebMvc // probably
public class Config {
   //you could also define other beans here
   @Bean
   public SomeBean someBean() {
       return new SomeBean();
   }
}

@pvpkiran gave the answer to your actual question. But I hope this helps you

@pvpkiran 回答了您的实际问题。但我希望这对你有帮助

回答by pvpkiran

The problem is your class QueueListener is not a spring bean. Hence all the variables that you have @Autowiredwill not be injected. Make it a spring bean using one of the Annotations, @Service, @Component.....

问题是您的类 QueueListener 不是 spring bean。因此,您拥有的所有变量都@Autowired不会被注入。使用其中一个注释使其成为 spring bean @Service@Component.....

For example.

例如。

@Component
public class QueueListener extends AuthController implements MessageListener{
   .....
}

Cannot say this will solve your problem entirely(because of your super classes), because I don't have enough information. But I am sure it will get rid of NPE.

不能说这会完全解决您的问题(因为您的超类),因为我没有足够的信息。但我相信它会摆脱 NPE。

回答by Urosh T.

There is no need to extend andautowire that controller. If you are going to extend, than there is no need to put dependencies in the extended class, this is just complicating the design. So either make the controller a dependency by autowiring it OR extend it (in which case there is no need for it to have dependencies). When using @Autowired- make sure all your classes are properly annotated as suggested and that they are being scanned by your main application class. There are some good practices that are recommended in the docs.

无需扩展自动装配该控制器。如果要扩展,则无需在扩展类中放置依赖项,这只会使设计复杂化。因此,要么通过自动装配使控制器成为依赖项,要么扩展它(在这种情况下,它不需要具有依赖项)。使用时@Autowired- 确保您的所有类都按照建议正确注释,并且您的主应用程序类正在扫描它们。文档中推荐了一些好的做法。

EDITControllers are annotated with @Controllerand service classes with @Servicewhich is basically a decorated @Componentannotation. All of this is explained pretty well in the docs, it is a great place to start exploring.

编辑控制器用@Controller和服务类注释,@Service基本上是一个装饰@Component注释。所有这些在文档中都有很好的解释,这是一个开始探索的好地方。

Back to the question:

回到问题:

If you are still getting a NPE, check your project structure, probably your class is not being picked up by spring's component scan or one of the classes is not anotated. Your main class (One that is annotated with @SpringBootApplication) should be in the root/default package in order to scan all other @Components.

如果您仍然获得 NPE,请检查您的项目结构,可能您的类没有被 Spring 的组件扫描选中,或者其中一个类没有被注释。您的主类(用 注释的类@SpringBootApplication)应该在 root/default 包中,以便扫描所有其他@Components。