Замена метода со статической ссылкой в ​​Java

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

public class Name {
    public static String getName() {
        return "MyName";
    }
}

Есть какой-либо способ сделать это? Я попробовал BCEL, но это, похоже, не изменило возвращаемое значение.

Изменить: это для мода. Я пытаюсь сделать его полностью независимым от существующего кода, не изменяя его.

Спасибо.


person Kaikz    schedule 25.08.2012    source источник
comment
почему ты хочешь это сделать? Вы можете сделать это с помощью метода установки?   -  person Nandkumar Tekale    schedule 25.08.2012
comment
Name.getName() метод возвращает жестко запрограммированное значение (как указано выше) или поле статического класса?   -  person Nandkumar Tekale    schedule 25.08.2012
comment
Он возвращает жестко запрограммированное значение, как указано выше. Если бы значение было переменной класса, я мог бы использовать простое отражение, чтобы изменить его значение, но это не так.   -  person Kaikz    schedule 25.08.2012


Ответы (3)


Вы уверены, что пробовали BCEL? Я создал полностью рабочий пример здесь.

JavaClass target;
try {
  target = Repository.lookupClass("Target");
} catch (final ClassNotFoundException ex) {
  throw new RuntimeException("unable to resolve Target", ex);
}
final ClassGen targetGen = new ClassGen(target);
final ConstantPoolGen pool = targetGen.getConstantPool();
final ConstantMethodref ref = (ConstantMethodref) pool.getConstant(
    pool.lookupMethodref("Name", "getName", "()Ljava/lang/String;"));
ref.setClassIndex(pool.lookupClass("Target"));
ref.setNameAndTypeIndex(pool.addNameAndType("$Name$getName", "()Ljava/lang/String;"));
final InstructionList code = new InstructionList();
final InstructionFactory factory = new InstructionFactory(targetGen, pool);
code.append(factory.createConstant("overriden-name"));
code.append(factory.createReturn(Type.STRING));
code.setPositions();
final MethodGen methodGen = new MethodGen(
    Constants.ACC_PRIVATE | Constants.ACC_SYNTHETIC | Constants.ACC_STATIC,
    Type.STRING, new Type[0], new String[0], "$Name$getName", "Target",
    code, pool);
methodGen.setMaxLocals(0);
methodGen.setMaxStack(1);
targetGen.addMethod(methodGen.getMethod());
try {
  targetGen.getJavaClass().dump("Target.class");
} catch (final IOException ex) {
  throw new RuntimeException("unable to save Target", ex);
}
C:\dev\scrap>javac Target.java
C:\dev\scrap>java Target
original-name
C:\dev\scrap>javac -cp .;bcel-6.0.jar Instrumenter.java
C:\dev\scrap>java -cp .;bcel-6.0.jar Instrumenter
C:\dev\scrap>java Target
overriden-name
person obataku    schedule 25.08.2012
comment
Хм, мне нужно добавить BCEL в путь к классам, у меня просто есть его источник в моем проекте, и мой мод запускает этот метод, и я получаю ArrayOutOfBoundsException -1. Я поиграюсь с этим, спасибо. - person Kaikz; 26.08.2012
comment
В основном это была проверка концепции, но я могу убедиться, что она работает. - person obataku; 26.08.2012

Вы можете передать параметр в метод

 public class Main {
        getName("newName")
    }

public class Name {
    public static String getName(String name) {
        return name;
    }
}
person Mohammod Hossain    schedule 25.08.2012
comment
Да, однако я не могу изменить существующий класс, возвращающий String. Я пытаюсь сделать это, не внося изменений в существующий код. (Да это мод) - person Kaikz; 25.08.2012

Вы можете попробовать добавить -javaagent агент, который будет использовать что-то вроде asm или bcel, чтобы изменить байт-код класса Name, чтобы статический метод возвращал другую строку. Многие фреймворки для фиктивного тестирования - например, powermock или jmockit - может это сделать.

ИЗМЕНИТЬ: Вот пример кода для начала. Это javaagent, который может изменять данный общедоступный статический метод, который возвращает String для возврата другой константы String. Например:

public class TestMain
{
    public static void main(String[] args)
    {
        System.out.println(Name.getName());
    }
}

class Name
{
    public static String getName()
    {
        return "ORIGINAL";
    }
}

$ java -cp build/libs/bciex.jar mycompany.myapp.TestMain
ORIGINAL
$ java -cp build/libs/bciex.jar -javaagent:build/libs/bciex.jar="mycompany.myapp.TestMain|getName|SOME_STRING" mycompany.myapp.TestMain
Agent loaded; will modify [getName] method of classes in [mycompany.myapp.TestMain] to return [SOME_STRING]
SOME_STRING
$ 
person Binil Thomas    schedule 25.08.2012