Хорошо, я пытаюсь сделать java-агент, который будет контролировать приложение. Итак, я пытаюсь ввести код в PreparedStatements для измерения времени выполнения запросов SQL. Для этого я разработал класс, реализующий ClassFileTransformer. Похоже, что:ClassFileTransformer + Javassist: no такое поле
public class JDBCClassTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
ClassPool pool = ClassPool.getDefault();
CtClass currentClass = null;
CtClass statement = null;
try {
currentClass = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));
statement = pool.get("java.sql.PreparedStatement");
if (currentClass.subtypeOf(statement) && !currentClass.isInterface()) {
probeStatement(currentClass);
}
classfileBuffer = currentClass.toBytecode();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (currentClass != null) {
currentClass.detach();
}
}
return classfileBuffer;
}
private void probeStatement(CtClass currentClass) throws NotFoundException, CannotCompileException {
CtField field = CtField.make("private fr.mael.package.SQLProbe $$probe;", currentClass);
currentClass.addField(field);
CtMethod setter = CtNewMethod.make("public void set$$probe(fr.mael.package.SQLProbe probe){ this.$$probe = probe;}", currentClass);
currentClass.addMethod(setter);
CtMethod executeQuery = currentClass.getMethod("executeQuery", "()Ljava/sql/ResultSet;");
executeQuery.insertBefore("this.$$probe.start();");
executeQuery.insertAfter("this.$$probe.stop();");
}
}
Итак, что я хочу сделать, это инъекционные код в каждом классе (в «ExecuteQuery()» метод), который реализует java.sql.PreparedStatement. Чтобы проверить его, я просто запускаю базовый «select * from a_table» в базе данных MySQL с агентом, подключенным к JVM. Но я получаю следующее исключение:
javassist.CannotCompileException: [source error] no such field: $$probe
at javassist.CtBehavior.insertBefore(CtBehavior.java:725)
at javassist.CtBehavior.insertBefore(CtBehavior.java:685)
Это происходит на линии executeQuery.insertBefore("this.$$probe.start();");
. Что странно - это не происходит каждый раз, когда выполняется метод «probeStatement»: сначала метод вызывается для класса com.mysql.jdbc.PreparedStatement
, и никакое исключение не генерируется. Затем метод вызывается для класса com.mysql.jdbc.ServerPreparedStatement
. Исключение выбрано. Метод также вызывается для класса com.mysql.jdbc.JDBC4PreparedStatement
, и исключение также выбрасывается. Оба ServerPreparedStatement
и JDBC4PreparedStatement
продлить com.mysql.jdbc.PreparedStatement
так может быть, это как-то связаны ...
Я новичок в Javassist и Java агент вещи, так что, может быть, ответ очевиден, но я не могу получить, почему это исключение.
Спасибо, ты положил меня на правильный путь! Я на самом деле исправил его, используя [getDeclaredMethod] (http://www.csg.ci.iu-tokyo.ac.jp/~chiba/javassist/html/javassist/CtClass.html#getDeclaredMethod%28java.lang.String,% 20javassist.CtClass []% 29). getDeclaredMethod выдает исключение NotFoundException, когда метод не объявлен в текущем классе. Неправильно использовать блок try/catch, но я не смог найти способ проверить, объявлен ли метод в классе. – mael
Как только проблема найдена, решение находится под углом. Надеюсь, вы скоро найдете хорошее решение! –