Первый, идеально с реальной ссылкой для следования в случае, если пользователю отключили JavaScript. Просто удостоверьтесь, что возвратили false, чтобы препятствовать тому, чтобы событие щелчка стреляло, если JavaScript выполняется.
Link
, Если Вы используете Angular2, этот путь работы:
Посмотрите здесь https://stackoverflow.com/a/45465728/2803344
Я не обнаружил никаких проблем, когда преобразовал B до A в Sun 1.6.0_15 и 1.5.0_17 JRE (я использовал ASM ). Я бы дважды проверил код преобразования, запустив его извне и проверив результирующие классы (например, с помощью javap). Я бы также проверил вашу конфигурацию пути к классам, чтобы убедиться, что A не загружен перед вашим агентом по какой-то причине (возможно, проверьте ваш preain с помощью getAllLoadedClasses ).
EDIT:
Если вы загрузите class
в вашем агенте выглядит следующим образом:
Class.forName("A");
... тогда возникает исключение:
Exception in thread "main" java.lang.NoSuchMethodError: B.print()V
Это имеет смысл -
становится зависимостью агента, и это не имеет смысла чтобы агент инструментировал свой собственный код. Вы получите бесконечный цикл, который приведет к переполнению стека. Следовательно,
не обрабатывается ClassFileTransformer
.
Для полноты, вот мой тестовый код, который работает без проблем. Как уже упоминалось, это зависит от библиотеки ASM.
Агент:
public class ClassModifierAgent implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("transform: " + className);
if ("A".equals(className)) {
return new AModifier().modify(classfileBuffer);
}
if ("B".equals(className)) {
return new BModifier().modify(classfileBuffer);
}
return classfileBuffer;
}
/** Agent "main" equivalent */
public static void premain(String agentArguments,
Instrumentation instrumentation) {
instrumentation.addTransformer(new ClassModifierAgent());
}
}
Инжектор метода для A
:
public class AModifier extends Modifier {
@Override
protected ClassVisitor createVisitor(ClassVisitor cv) {
return new AVisitor(cv);
}
private static class AVisitor extends ClassAdapter {
public AVisitor(ClassVisitor cv) { super(cv); }
@Override
public void visitEnd() {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "print", "()V",
null, null);
mv.visitCode();
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn("X");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
"println", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
super.visitEnd();
}
}
}
Заменитель метода для B
:
public class BModifier extends Modifier {
@Override
protected ClassVisitor createVisitor(ClassVisitor cv) {
return new BVisitor(cv);
}
class BVisitor extends ClassAdapter {
public BVisitor(ClassVisitor cv) { super(cv); }
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if ("foo".equals(name)) {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "foo", "()V",
null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "B", "print", "()V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
return new EmptyVisitor();
} else {
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
}
}
Общий базовый код :
public abstract class Modifier {
protected abstract ClassVisitor createVisitor(ClassVisitor cv);
public byte[] modify(byte[] data) {
ClassReader reader = new ClassReader(data);
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
ClassVisitor visitor = writer;
visitor = new CheckClassAdapter(visitor);
visitor = createVisitor(visitor);
reader.accept(visitor, 0);
return writer.toByteArray();
}
}
Для некоторых видимых результатов я добавил System.out.println ('X');
в A.print ()
.
При запуске с этим кодом :
public class MainInstrumented {
public static void main(String[] args) {
new B().foo();
}
}
... выводит следующий результат:
transform: MainInstrumented
transform: B
transform: A
X