Изменить значение статического поля во время загрузки класса с помощью агента Java

У нас есть java-процесс, который вызывает некоторый метод класса X. Класс X имеет статическое поле тайм-аута, которое определяет, как долго поток должен ждать в случае какой-либо ошибки. Теперь я хочу изменить это значение, не меняя процесс Java (я не хочу развертывания, и это изменение является экспериментальным). Как я могу использовать java-агент, чтобы изменить это значение тайм-аута на 1 минуту (1 * 60 * 1000)

Class X {
    ....
    // timeout = 5 minutes
    private static long timeout = 5*60*1000;
    ....
}

Короче говоря, как написать java-агент для изменения значения статической переменной. Я прошел несколько руководств, но ни один из них не объясняет, как это сделать. У меня нет доступа к основному методу. Проект управляется контейнером IOC.

Спасибо,

Риши


person hrishikeshp19    schedule 20.11.2014    source источник
comment
почему бы просто не абстрагировать это из своего кода и не поместить в файл свойств? затем вы можете изменить файл свойств во время выполнения, если хотите, и всякий раз, когда ваш класс будет создан, он будет читать файл свойств в это время. Кроме того, статические поля не являются final, поэтому с помощью установщика/геттера вы также можете манипулировать его значением во время выполнения.   -  person SnakeDoc    schedule 20.11.2014
comment
вы можете использовать отражение и получить доступ к полю tutorials.jenkov.com/ java-отражение/   -  person searchengine27    schedule 20.11.2014


Ответы (1)


Используя отражение, вы можете реализовать это довольно легко:

class EasyFieldAlterationAgent {
  public static void premain(String args) throws Exception {
    Field field = X.class.getDeclaredField("timeout");
    field.setAccessible(true);
    field.setValue(null, 42L); // set your value here.
  }
}

Обратите внимание, что это изменит поле не до, а сразу после загрузки класса. Если вы не можете работать с этим, вы также можете просто переопределить сам класс, что я бы рекомендовал, только если:

  1. Ваша настройка безопасности не позволяет вам использовать отражение.
  2. Вам нужно изменить значение перед выполнением инициализатора типа класса.

Если вы действительно хотите изменить поле перед загрузкой класса, вам повезло, что вы хотите изменить значение поля, которое одновременно равно static и определяет примитивное значение. Такие поля хранят свои значения непосредственно на месте поля. Используя агент, вы можете определить ClassFileTransformer, который просто изменяет значение поля на лету. ASM — хороший инструмент для реализации такого простого преобразования. Используя этот инструмент, вы могли бы реализовать своего агента примерно следующим образом:

class FieldAlterationAgent {
  public static void premain(String args, Instrumentation inst) {
    instrumentation.addTransformer(new ClassFileTransformer() {
      @Override
      public void byte[] transform(ClassLoader loader,
                                   String className,
                                   Class<?> classBeingRedefined,
                                   ProtectionDomain protectionDomain,
                                   byte[] classfileBuffer)
          throws IllegalClassFormatException {
        if (!className.equals("X") {
          return classFileBuffer;
        }
        ClassWriter classWriter = new ClassWriter(new ClassVisitor() {
          @Override
          public FieldVisitor visitField(int access, 
                                         String name, 
                                         String desc, 
                                         String signature, 
                                         Object value) {
            if(name.equals("timeout") {
              value = 42L; // set value here, make sure its a long!
            }
            return super.visitField(access, name, desc, signature, value);
          }
        }, 0);
        new ClassReader(classFileBuffer).accept(classWriter);
        return classWriter.toByteArray();
      }
    });
  }
}

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

Чтобы применить агент, поместите любой класс в jar. файл и поместите его в путь агента.

person Rafael Winterhalter    schedule 21.11.2014
comment
Спасибо за это. В первом примере, как получить доступ к X. Как X передается в premain? - person hrishikeshp19; 21.11.2014
comment
Если ваш класс доступен в пути к классам, он ищет системный загрузчик классов. Premain передаются аргументы командной строки, где вы можете передать информацию. В противном случае жестко запрограммируйте его или установите системное свойство. - person Rafael Winterhalter; 22.11.2014
comment
java-агент — это другой jar-файл, в котором есть только класс-преобразователь и класс, содержащий метод premain. Все остальные мои классы находятся в оригинальной банке, которая работает без агента. - person hrishikeshp19; 22.11.2014
comment
Добавьте классы приложений в качестве зависимости время компиляции. Класс агента загружается системным загрузчиком классов. Затем ваше приложение становится видимым для этого загрузчика классов. Если вы используете сервер приложений, где классы не видны загрузчику системных классов, вам нужно использовать вторую стратегию, когда вы называете класс только строкой. - person Rafael Winterhalter; 22.11.2014
comment
Спасибо, посмотрю asm. Я думаю, это то, что мне нужно. - person hrishikeshp19; 22.11.2014