Вызвать метод путем отражения с динамическими параметрами - PullRequest
0 голосов
/ 07 мая 2018

Я вызываю некоторый метод экземпляра класса, используя способ method.invoke(instance, args...), но для каждого метода внутри instance, как справедливо указывает invoke Javadoc, каждый аргумент должен быть указан вручную.

Думая о Spring ... как он мог бы оценить параметры в методе контроллера, скрытого во время HTTP-вызовов? (но, конечно, это происходит совсем по-другому, я думаю ...)

Интересно, есть ли в Java какой-либо способ динамически передавать параметры в отражении (или даже не отражении), не указывая каждый из них по отдельности.

EDIT

Объявление класса экземпляра выглядит примерно так:

public class Something {

    public void doSth(String par1, String par2, Integer par3) {
        //....
    }

    public void doSthElse(String par1, Boolean par2) {
        //....
    }

    public void doSthElseMore(Integer par1) {
        //....
    }

}

Как я вызываю каждый метод:

...
for (Method method : instance.getDeclaredMethods()) {
    Object[] array = //BL: build array of values to pass to the invoke method.
//1. doSth may be new Object[] {"abc", "def", 123}
//2. doSthElse iteration may be new Object[] {"abc", false}
//3. doSthElseMore iteration may be new Object[] {123}
    return method.invoke(instance, array);
}
...

Как показано выше, каждый метод внутри Something класса (instance) имеет разное количество параметров.

На каждой итерации array имеет разное количество значений для передачи в invoke.

Ответы [ 2 ]

0 голосов
/ 07 мая 2018

На самом деле, как говорит @Boris, все, что мне нужно было сделать, чтобы завершить свою работу, это преобразовать каждый параметр в правильный тип. Таким образом Java удалось вызвать правильный метод класса Something с правильными типами параметров.

Мой проект - приложение Vert.x, использующее Vavr и jodd, но последнее выражение возврата показывает, как мне удалось решить.

public Object invokeMethod(Object service, Method method, RoutingContext routingContext) throws Exception {
        MultiMap queryParams = routingContext.queryParams();
        Map<String, String> pathParams = routingContext.pathParams();
        Buffer body = routingContext.getBody();

        // 1. type, 2. name, 3. value
        List<Tuple3<Class<?>, String, Object>> list = List.empty();

        for (Parameter par : method.getParameters()) {
            ParamQuery paramQuery = par.getAnnotation(ParamQuery.class);
            if (paramQuery != null) {
                list = list.push(new Tuple3<Class<?>, String, Object>(par.getType(), paramQuery.value(),
                        queryParams.get(paramQuery.value())));
            }
        }

// TypeConverterManager used to "covnert" each object (String) from the HTTP call to the correct data type
        return method.invoke(service, list.reverse()
                .map(mapper -> TypeConverterManager.lookup(mapper._1()).convert(mapper._3())).toJavaArray());
    }

Однако этот проект можно найти на GitHub

0 голосов
/ 07 мая 2018

Поскольку я заметил, что вы используете Integer вместо int (поэтому в примерах нет параметров примитивов), вы можете без проблем отправить null всем вашим методам.

Таким образом, вы можете создать массив правильной длины, и это будет работать в вашем случае.

public static Object[] getParametersArray(Parameter[] param){
    Object[] array = new Object[param.length];
    // create default primitive values based on param[#].getType()
    return array;
}

Затем все, что вам нужно сделать, это повторить метод:

Labo l = new Labo();
for(Method m : Labo.class.getDeclaredMethods()){
    if((m.getModifiers() & Modifier.STATIC) > 0){
        System.out.println("SKIP " + m.getName());
        continue;
    }
    try {
        m.invoke(l, getParametersArray(m.getParameters()));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Обратите внимание на пропущенный статический метод, главным образом потому, что если вы запустите его в методе, содержащем метод main, у вас будет рекурсивный вызов.

Это было проверено с:

public void test(String s){
    System.out.println("test String " + s);
}

public void test2(String s1, String s2){
    System.out.println("test String " + s1 + " | String " + s2);
}

public void test(Integer s){
    System.out.println("test Integer " + s);
}

Пропустить основной
тестовая строка null
тест Integer null
SKIP getParametersArray
тестовая строка ноль | Строка ноль

Примечание. Если вам нужно управлять некоторыми примитивными значениями, вам нужно получить тип параметра, чтобы обеспечить значение по умолчанию вместо нуля

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