BAEL-634 javassist (#1349)
* BEEL-634 javassist dependency * BEEL-634 code for javassist article * BEEL-634 test refinement * BEEL-634 increment lib to newest version * add test that uses reflection to verify * add field * add bytecode to different class
This commit is contained in:
parent
61660f5d37
commit
5cd552a16b
|
@ -23,7 +23,7 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
|
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cglib</groupId>
|
<groupId>cglib</groupId>
|
||||||
|
@ -51,6 +51,11 @@
|
||||||
<artifactId>javatuples</artifactId>
|
<artifactId>javatuples</artifactId>
|
||||||
<version>${javatuples.version}</version>
|
<version>${javatuples.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.javassist</groupId>
|
||||||
|
<artifactId>javassist</artifactId>
|
||||||
|
<version>${javaassist.version}</version>
|
||||||
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
|
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.assertj</groupId>
|
<groupId>org.assertj</groupId>
|
||||||
|
@ -65,6 +70,7 @@
|
||||||
<junit.version>4.12</junit.version>
|
<junit.version>4.12</junit.version>
|
||||||
<jasypt.version>1.9.2</jasypt.version>
|
<jasypt.version>1.9.2</jasypt.version>
|
||||||
<javatuples.version>1.2</javatuples.version>
|
<javatuples.version>1.2</javatuples.version>
|
||||||
|
<javaassist.version>3.21.0-GA</javaassist.version>
|
||||||
<assertj.version>3.6.2</assertj.version>
|
<assertj.version>3.6.2</assertj.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.baeldung.javasisst;
|
||||||
|
|
||||||
|
|
||||||
|
public class Point {
|
||||||
|
public int x = 0;
|
||||||
|
public int y = 0;
|
||||||
|
|
||||||
|
public Point(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void move(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.baeldung.javasisst;
|
||||||
|
|
||||||
|
|
||||||
|
public class ThreeDimensionalPoint {
|
||||||
|
public int x = 0;
|
||||||
|
public int y = 0;
|
||||||
|
public int z = 0;
|
||||||
|
|
||||||
|
public ThreeDimensionalPoint(int x, int y, int z) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void move(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package com.baeldung.javassist;
|
||||||
|
|
||||||
|
|
||||||
|
import javassist.CannotCompileException;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.NotFoundException;
|
||||||
|
import javassist.bytecode.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class JavasisstTest {
|
||||||
|
@Test
|
||||||
|
public void givenJavasisstAPI_whenConstructClass_thenGenerateAClassFile() throws CannotCompileException, IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||||
|
//given
|
||||||
|
String classNameWithPackage = "com.baeldung.JavassistGeneratedClass";
|
||||||
|
ClassFile cf = new ClassFile(false, classNameWithPackage, null);
|
||||||
|
cf.setInterfaces(new String[]{"java.lang.Cloneable"});
|
||||||
|
|
||||||
|
FieldInfo f = new FieldInfo(cf.getConstPool(), "id", "I");
|
||||||
|
f.setAccessFlags(AccessFlag.PUBLIC);
|
||||||
|
cf.addField(f);
|
||||||
|
|
||||||
|
//when
|
||||||
|
String className = "JavassistGeneratedClass.class";
|
||||||
|
cf.write(new DataOutputStream(new FileOutputStream(className)));
|
||||||
|
|
||||||
|
//then
|
||||||
|
ClassPool classPool = ClassPool.getDefault();
|
||||||
|
Field[] fields = classPool.makeClass(cf).toClass().getFields();
|
||||||
|
assertEquals(fields[0].getName(), "id");
|
||||||
|
|
||||||
|
String classContent = new String(Files.readAllBytes(Paths.get(className)));
|
||||||
|
assertTrue(classContent.contains("java/lang/Cloneable"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenJavaClass_whenLoadAtByJavassist_thenTraversWholeClass() throws NotFoundException, CannotCompileException, BadBytecode {
|
||||||
|
//given
|
||||||
|
ClassPool cp = ClassPool.getDefault();
|
||||||
|
ClassFile cf = cp.get("com.baeldung.javasisst.Point").getClassFile();
|
||||||
|
MethodInfo minfo = cf.getMethod("move");
|
||||||
|
CodeAttribute ca = minfo.getCodeAttribute();
|
||||||
|
CodeIterator ci = ca.iterator();
|
||||||
|
|
||||||
|
//when
|
||||||
|
List<String> operations = new LinkedList<>();
|
||||||
|
while (ci.hasNext()) {
|
||||||
|
int index = ci.next();
|
||||||
|
int op = ci.byteAt(index);
|
||||||
|
operations.add(Mnemonic.OPCODE[op]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertEquals(operations,
|
||||||
|
Arrays.asList("aload_0", "iload_1", "putfield", "aload_0", "iload_2", "putfield", "return"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTableOfInstructions_whenAddNewInstruction_thenShouldConstructProperSequence() throws NotFoundException, BadBytecode, CannotCompileException, IllegalAccessException, InstantiationException {
|
||||||
|
//given
|
||||||
|
ClassFile cf = ClassPool.getDefault().get("com.baeldung.javasisst.ThreeDimensionalPoint").getClassFile();
|
||||||
|
|
||||||
|
//when
|
||||||
|
FieldInfo f = new FieldInfo(cf.getConstPool(), "id", "I");
|
||||||
|
f.setAccessFlags(AccessFlag.PUBLIC);
|
||||||
|
cf.addField(f);
|
||||||
|
|
||||||
|
|
||||||
|
ClassPool classPool = ClassPool.getDefault();
|
||||||
|
Field[] fields = classPool.makeClass(cf).toClass().getFields();
|
||||||
|
List<String> fieldsList = Stream.of(fields).map(Field::getName).collect(Collectors.toList());
|
||||||
|
assertTrue(fieldsList.contains("id"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLoadedClass_whenAddConstructorToClass_shouldCreateClassWithConstructor() throws NotFoundException, CannotCompileException, BadBytecode {
|
||||||
|
//given
|
||||||
|
ClassFile cf = ClassPool.getDefault().get("com.baeldung.javasisst.Point").getClassFile();
|
||||||
|
Bytecode code = new Bytecode(cf.getConstPool());
|
||||||
|
code.addAload(0);
|
||||||
|
code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
|
||||||
|
code.addReturn(null);
|
||||||
|
|
||||||
|
//when
|
||||||
|
MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V");
|
||||||
|
minfo.setCodeAttribute(code.toCodeAttribute());
|
||||||
|
cf.addMethod(minfo);
|
||||||
|
|
||||||
|
//then
|
||||||
|
CodeIterator ci = code.toCodeAttribute().iterator();
|
||||||
|
List<String> operations = new LinkedList<>();
|
||||||
|
while (ci.hasNext()) {
|
||||||
|
int index = ci.next();
|
||||||
|
int op = ci.byteAt(index);
|
||||||
|
operations.add(Mnemonic.OPCODE[op]);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(operations,
|
||||||
|
Arrays.asList("aload_0", "invokespecial", "return"));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue