Вам следует ознакомиться с шаблоном Visitor .
Оператор
cr.accept(cm, ClassReader.EXPAND_FRAMES);
уже выполняет всю работу.ClassReader
вызовет весь метод visit…
для указанного ClassTransformer
, который будет выполнять все необходимое делегирование ClassWriter
при следовании стандартной модели программирования этого API.
Другими словами, этоВесь блок
ClassNode cn = new ClassNode();
cr.accept(cn, 0);
System.out.println("Analyzing bytecode in class " + cn.name);
List<MethodNode> methods = cn.methods;
for (MethodNode method : methods) {
System.out.println("Analyzing bytecode in method " + method.name);
String[] exceptions = (String[]) method.exceptions.toArray(new String[method.exceptions.size()]);
cm.visitMethod(method.access, method.name, method.desc, method.signature, exceptions);
}
устарел, и поскольку он циклически обрабатывает все методы и вызывает visitMethod
для каждого в дополнение к оператору, который уже выполняет всю работу, неудивительно, что вы получаете каждый метод дважды.
Так что просто удалите этот блок кода.
Ну, конечно, вы можете сохранить оператор печати ...
Кстати, вы можете еще больше упростить свой код, просто передав JarInputStream
конструктору ClassReader(InputStream)
, не копируя сначала ByteArrayOutputStream
.
Очищенная версия вашего кода будет выглядеть так:
try(JarInputStream jis = new JarInputStream(new FileInputStream(input));
JarOutputStream jos = new JarOutputStream(new FileOutputStream(output)) ) {
JarEntry entry;
while((entry = jis.getNextJarEntry()) != null) {
jos.putNextEntry(new JarEntry(entry.getName()));
if (entry.getName().endsWith(".class")) {
ClassReader cr = new ClassReader(jis);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
ClassTransformer cm = new ClassTransformer(cw);
System.out.println("Analyzing bytecode in class " + cr.getClassName());
cr.accept(cm, ClassReader.EXPAND_FRAMES);
jos.write(cw.toByteArray());
} else {
jis.transferTo(jos); // Java 9
/* before Java 9:
byte[] buffer = new byte[8192];
for(int read; (read = jis.read(buffer, 0, buffer.length)) >= 0; )
jos.write(buffer, 0, read);
*/
}
}
}
Обратите внимание, что я также передал ClassReader
конструктору ClassWriter
.Для таких случаев использования, когда вносятся только небольшие изменения, это позволяет оптимизировать генерацию кода.