【问题标题】:Spring Boot - How to call method from a service class just by the provided service class name?Spring Boot - 如何仅通过提供的服务类名称从服务类调用方法?
【发布时间】:2020-02-09 23:56:08
【问题描述】:

我需要从 API 请求控制器的调用中指定的服务类执行一些方法。我有一个 POST 请求,它接收一个服务类名称、一个包含一些变量的请求正文和一个用于该方法中某些操作的大小参数。无论从哪个服务类被调用,该方法总是被调用getFilteredResults。除了 url 路径中提供的服务类的名称之外,我不知道哪个类服务将执行该方法:

@PostMapping(value = "method-call/{serviceClassName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Object executeMethod(@PathVariable("serviceClassName") String serviceClassName,
  @RequestBody Filter filter, @RequestParam(required = false) Integer size) {
  // ...
  // need to execute getFilteredResults(filter, size) from serviceClassName
  // ...
}

请求的服务类,不管名字总是这样:

@Service
public class SomeService {

  @Autowired
  SomeDao someDao;

  public List<SomeEntity> getFilteredResults(Filter filter, Integer size) {
    return this.someDao.findAll(filter, PageRequest.of(0, size)).getContent();
  }
}

我试过这个:

@PostMapping(value = "method-call/{serviceClassName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Object executeMethod(@PathVariable("serviceClassName") String serviceClassName,
  @RequestBody Filter filter, @RequestParam(required = false) Integer size) {
  try {
    Class<?> cls = Class.forName(serviceClassName));
    Method getFilteredResults = cls.getDeclaredMethod("getFilteredResults", Filter.class, Integer.class);
    getFilteredResults.setAccessible(true);
    return getFilteredResults.invoke(cls.newInstance(), filter, size);
  } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
    e.getCause().printStackTrace();
    return e;
  }
}

我用Class.forName 获得服务类,然后我用Method getFilteredResults = cls.getDeclaredMethod("getFilteredResults", Filter.class, Integer.class) 获得方法,然后我从带有参数的服务类中调用方法:getFilteredResults.invoke(cls.newInstance(), filter, size)。不管我做什么,总是抛出java.lang.NullPointerException

如果我打印类和方法,它们看起来还不错:

class co.com.company.fpv.service.SomeService
public java.util.List co.com.company.fpv.service.SomeService.getFilteredResults(co.com.company.fpv.service.filter.Filter,java.lang.Integer)

我做错了什么?或者我还能做些什么来做到这一点?提前致谢。

【问题讨论】:

    标签: java spring-boot jpa


    【解决方案1】:

    你会有一个

    重大安全问题

    使用您所遵循的设计。基本上,您允许使用反射调用任何类上的任何方法。使用它们各自的控制器调用服务或编写 if /else 块。 此外,您正在搞乱 spring 框架的 DI。

    【讨论】:

      【解决方案2】:

      最好使用 Spring 注入 bean 并按名称选择适当的 bean,而不是依赖类名和反射。

      例如,如果所有服务 bean 都实现了这样的通用接口:

      @Service
      public class SomeService implements FilteredResultsService {
      
        @Autowired
        SomeDao someDao;
      
        @Override
        public List<SomeEntity> getFilteredResults(Filter filter, Integer size) {
          return this.someDao.findAll(filter, PageRequest.of(0, size)).getContent();
        }
      }
      

      然后您可以将实现该接口的所有服务注入控制器:

      // a Map of Spring beans of type FilteredResultsService, where
      // the key is the name of the bean and the value is the bean object
      @Autowired
      private Map<String, FilteredResultsService> serviceBeans;
      
      @PostMapping(value = "method-call/{serviceName}", produces = MediaType.APPLICATION_JSON_VALUE)
      public Object executeMethod(@PathVariable("serviceName") String serviceName,
        @RequestBody Filter filter, @RequestParam(required = false) Integer size) {
      
        FilteredResultsService service = serviceBeans.get(serviceName);
        if (service == null) {
          // invalid serviceName, throw an error
        } 
      
        return service.getFilteredResults(filter, size);
      }
      

      这将减少@syed-afzal 指出的安全风险,因为只能调用由您的 Spring 配置创建的 bean,而不是类路径上的任何类。

      【讨论】:

        猜你喜欢
        • 2021-05-24
        • 2020-01-20
        • 2019-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-28
        • 2019-02-24
        • 1970-01-01
        相关资源
        最近更新 更多