使用 Javassist 修改超类中声明的方法
Modify a method declared in the superclass with Javassist
我正在尝试修改在超类中声明的具有 CtMethod#insertBefore
的方法。但是,Javassist 似乎是不可能的。
private class AbstractTestDataSource {
public Connection getConnection() throws SQLException {
return connection;
}
}
private class TestDataSource extends AbstractTestDataSource implements DataSource {
public Connection getConnection(String username, String password) throws SQLException {
return connection;
}
// other methods omitted
}
这是我的ClassFileTransformer
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws Exception {
if (!className.equals("org/example/TestDataSource")) {
return classfileBuffer;
}
final CtClass ctClass = createCtClass(loader, classfileBuffer);
for (CtMethod method : ctClass.getMethods()) {
if (method.getName().equals("getConnection")) {
System.out.print(method.getName());
System.out.println(method.getSignature());
method.insertBefore("System.out.println(\"foo\");");
}
}
return ctClass.toBytecode();
}
当我调用 getConnection(String, String)
方法时,foo
会打印到控制台,但如果我调用 AbstractTestDataSource
中声明的 getConnection()
方法,则什么也没有发生。
我做错了什么?
编辑
我可以确认这两种方法都已检测到,因为这是打印到控制台的内容:
getConnection(Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection;
getConnection()Ljava/sql/Connection;
您没有覆盖超级 class 中的方法 getConnection(),该方法具有此方法签名:
public Connection getConnection() throws SQLException
在您的 child class 中,您必须使用相同的方法签名覆盖此方法,否则您只是在 child [=17= 中创建一个新方法]
我的解决办法是检查getConnection方法是否在class中声明,而不是当前
if (!ctClass.equals(method.getDeclaringClass())) {
method = overrideMethod(ctClass, method);
}
如果是这样,我创建(并因此重写)getConnection 方法并委托给 superclass。
private CtMethod overrideMethod(CtClass ctClass, CtMethod getConnectionMethodOfSuperclass)
throws NotFoundException, CannotCompileException {
final CtMethod m = CtNewMethod.delegator(getConnectionMethodOfSuperclass, ctClass);
ctClass.addMethod(m);
return m;
}
感觉这不是理想的解决方案,但工作正常。
我正在尝试修改在超类中声明的具有 CtMethod#insertBefore
的方法。但是,Javassist 似乎是不可能的。
private class AbstractTestDataSource {
public Connection getConnection() throws SQLException {
return connection;
}
}
private class TestDataSource extends AbstractTestDataSource implements DataSource {
public Connection getConnection(String username, String password) throws SQLException {
return connection;
}
// other methods omitted
}
这是我的ClassFileTransformer
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws Exception {
if (!className.equals("org/example/TestDataSource")) {
return classfileBuffer;
}
final CtClass ctClass = createCtClass(loader, classfileBuffer);
for (CtMethod method : ctClass.getMethods()) {
if (method.getName().equals("getConnection")) {
System.out.print(method.getName());
System.out.println(method.getSignature());
method.insertBefore("System.out.println(\"foo\");");
}
}
return ctClass.toBytecode();
}
当我调用 getConnection(String, String)
方法时,foo
会打印到控制台,但如果我调用 AbstractTestDataSource
中声明的 getConnection()
方法,则什么也没有发生。
我做错了什么?
编辑
我可以确认这两种方法都已检测到,因为这是打印到控制台的内容:
getConnection(Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection;
getConnection()Ljava/sql/Connection;
您没有覆盖超级 class 中的方法 getConnection(),该方法具有此方法签名:
public Connection getConnection() throws SQLException
在您的 child class 中,您必须使用相同的方法签名覆盖此方法,否则您只是在 child [=17= 中创建一个新方法]
我的解决办法是检查getConnection方法是否在class中声明,而不是当前
if (!ctClass.equals(method.getDeclaringClass())) {
method = overrideMethod(ctClass, method);
}
如果是这样,我创建(并因此重写)getConnection 方法并委托给 superclass。
private CtMethod overrideMethod(CtClass ctClass, CtMethod getConnectionMethodOfSuperclass)
throws NotFoundException, CannotCompileException {
final CtMethod m = CtNewMethod.delegator(getConnectionMethodOfSuperclass, ctClass);
ctClass.addMethod(m);
return m;
}
感觉这不是理想的解决方案,但工作正常。