Spring Boot - Как вызвать метод из класса обслуживания только по предоставленному имени класса обслуживания? - PullRequest
0 голосов
/ 10 февраля 2020

Мне нужно выполнить какой-то метод из класса обслуживания, который указан в вызове от контроллера запросов 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)

Что я делаю не так? или что еще я могу сделать для достижения этой цели? Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 10 февраля 2020

У вас будет

большая проблема безопасности

с дизайном, которому вы следуете. По сути, вы разрешаете вызывать любой метод любого класса с отражением. Позвоните в сервисы с их отдельными контроллерами или напишите блоки if / else. Кроме того, вы запутались с DI весеннего каркаса.

1 голос
/ 10 февраля 2020

Было бы лучше использовать Spring для внедрения 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, а не какой-либо класс в пути к классам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...