如何使控制器端点在 java spring 中获取两个不同的对象?

How to make controller endpoint to get two different objects in java spring?

我有一个使用 java 和 spring 构建的服务器。

我想要做的是我的具有相同端点的控制器将获得两个不同的对象。

这是我的意思的一个例子:

我知道我能做到:

  public class Option1{
   private String name;
   ...
     //getter and setter
    }

public class Option2{
 private Long id;
  ...
//getter and setter
}

@Controller
public class Controller{

 @RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
 @ResponseBody
 public String searchProv(@ResponseBody Option1 data1){
  return "option1"
   }

@RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
@ResponseBody
public String searchProv(@ResponseBody Option2 data2){
  return "option2"
  }
}

但我想知道是否可以将不同的 json 对象传递到同一端点并执行此操作:

 @Controller
public class Controller{

 @RequestMapping(value = "service/getData", method = RequestMethod.POST)
 @ResponseBody
 public ResponseEntity<Any> getData(@ResponseBody Option1And2 data){
if(data instanceof Option1){
  return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}        
if(data instanceof Option2){
   return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
 return ResponseEntity<Any>("ok",HttpStatus.OK)
  }

这样 'Option1And2' 是通用对象,可以是选项 1 或选项 2。

我尝试将 'Option1And2' 替换为 'Any' 但效果不佳,因为我得到了键和值列表

似乎你希望程序本身在你这样做之前确定选项的类型is.But,你确定这两个对象之间有什么区别吗?

首先,Option1And2 实际上是什么?如果 Option1And2 包含 Option1 和 Option2 的所有字段但它不是它们的子类,那么 Option1And2 可能如下所示:

@Data
public class Option1And2{
    private String name;

    private Long id;
}
  • 如果您有其他限制,例如“其中一个并且只有一个有 为null",则可以通过这个规则判断。
  • 如果你没有任何其他限制,那么也许你可以添加一个新的 字段作为标志。

其实那些代码风格不是recommend.If这两个函数有不同的职责,那么最好不要把它们混在一起。你会明白我的意思的当你必须重构这些代码时。

如果这两个功能确实有很多共同点,也许您最好重构服务逻辑,而不是通过创建新参数 Option1And2 粗略地组合两个服务。

顺便问一下,你到底想做什么?为什么要把这两个对象合并为一个?

这是使用继承和 Java 泛型的好时机。值得注意的是,如果您的控制器有任何依赖项,例如 @Service@Repository,那么这些也必须是通用的。

您可能有一个通用控制器:

abstract class GenericController<T> {

    public abstract GenericService<T> getService();

    @GetMapping
    public ResponseEntity<Iterable<T>> findAll() {

        return ResponseEntity.ok(getService().findAll());
    }

    @PostMapping
    public ResponseEntity<T> save(T entity) {

        return ResponseEntity.ok(getService().save(entity));
    }

    // @DeleteMapping, @PutMapping
    // These mappings will automatically be inherited by
    // the child class. So in the case of findAll(), the API
    // will have a GET mapping on /category as well as a GET
    // mapping on /product. So, by defining and annotating the
    // CRUD operations in the parent class, they will automatically
    // become available in all child classes.
}

@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {

    @Autowired CategoryServ serv;

    @Override
    public GenericService<Category> getService() {
        return serv;
    }
}

@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {

    @Autowired ProductServ serv;

    @Override
    public GenericService<Product> getService() {
        return serv;
    }
}

然后您必须具有依赖项的抽象版本。服务:

abstract class GenericService<T> {

    public abstract GenericRepository<T> getRepository();

    public Iterable<T> findAll() {

        return getRepository().findAll();
    }

    public T save(T entity) {

        return getRepository().save(entity);
    }

}

@Service
class CategoryServ extends GenericService<Category> {

    @Autowired CategoryRepo repo;

    @Override
    public GenericRepository<Category> getRepository() {
        return repo;
    }
}

@Service
class ProductServ extends GenericService<Product> {

    @Autowired ProductRepo repo;

    @Override
    public GenericRepository<Product> getRepository() {
        return repo;
    }
}

然后,服务也有它们的依赖关系——存储库:

@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}

@Repository
interface CategoryRepo extends GenericRepository<Category> {
}

@Repository
interface ProductRepo extends GenericRepository<Product> {
}

这是我的第一个方法。它工作得很好。但是,这确实在每个服务的业务逻辑和通用服务之间建立了强耦合。这同样适用于通用控制器及其子 classes。您当然可以始终覆盖特定的 CRUD 操作。但是,您必须小心执行此操作,因为您可能会产生意外行为。还值得注意的是,继承自 class 具有使用 @RequestMapping 注释的方法的 es 会自动公开所有注释的方法。这可能是不希望的。例如,我们可能不想要类别的删除选项,但我们想要产品的删除选项。为了解决这个问题,我们可以简单地在父 class 中定义它,而不是在父 class 中注释方法,并使用添加的 @RequestMapping 注释覆盖所需的 CRUD 操作,然后调用超级 class 方法。

另一种方法是using annotations.

你应该使用 JsonNode 对象。

对于您的示例,您应该这样做:

 @Controller
 public class Controller{

 @RequestMapping(value = "service/getData", method = RequestMethod.POST)
 @ResponseBody
 public ResponseEntity<Any> getData(@RequestBody JsonNode jsonNode){

   ObjectMapper obj = new ObjectMapper();

  if(jsonNode.has("name"){
   Option1 result= obj.convertValue(jsonNode,Option1.class)
  return ResponseEntity<Any>(result.name,HttpStatus.OK)
    }    

   else {

   Option2 result= obj.convertValue(jsonNode,Option2.class)
   return ResponseEntity<Any>(result.id,HttpStatus.OK)
    }

    return ResponseEntity<Any>("ok",HttpStatus.OK)
     }

您应该从此处导入的 JsonNode 和 ObjectMapper:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;

这篇 link 应该可以帮助您更好地了解 JsonNode 并为您提供更多详细信息。

而这个 link 应该可以帮助您将 convertValue 从 JsonNode 转换为 java object(POJO)。