关于修改和代理Final类和方法?

背景

cglib是基于继承的代理的,但是final修饰的类的方法不能被代理,毕竟虚拟机规范限定了final关键字,想要实现代理的功能,自己不要加上final就是了。但是现在别人写好了库里的类,不方便修改想要尝试。

1.自定义Classloader,利用ASM去掉final关键字


jvm利用双亲委派机制加载类,优先尝试让父类加载,父类加载成功,自己就没有机会加载,这是安全和防止重复加载的机制。默认情况下自己写的class是程序加载器加载的,jvm认为Classloder+类路径名 完全一样才是相同的一个class。所以用自己的加载器加载的类,没法转换成为之前的的引用。想要调用和访问,也只能利用反射。

比如这样是不行的

1
Person person = new MyClassLoader(Person.class).newInstance()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class MyClassLoader extends ClassLoader {

public MyClassLoader() {
//指定父加载器为null
super(null);
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findClass: "+name);
try {
ClassReader reader = new ClassReader(name);
ClassWriter writer = new ClassWriter(reader, 0);
RemoveFinalFlagClassVisitor classVisitor = new RemoveFinalFlagClassVisitor(writer);
reader.accept(classVisitor, ClassReader.SKIP_CODE);
byte[] bytes = writer.toByteArray();
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}


}

// 利用ClassVisitor去掉final的修饰
class RemoveFinalFlagClassVisitor extends ClassVisitor {

public RemoveFinalFlagClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// we have the final flag
if((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL) {
//remove the final flag
access = access ^ Opcodes.ACC_FINAL;
}
// 在调用super.visit的时候,我们就已经把final关键字去掉了
super.visit(version, access, name, signature, superName, interfaces);
}


@Override
public MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings) {
/*可以在这里修改final修饰非方法
这里可以利用debug查看修饰的特征值
*/
if (i==17){
return super.visitMethod(1, s, s1, s2, strings);
}
return super.visitMethod(i, s, s1, s2, strings);
}
}

测试

测试类

1
2
3
4
5
6
7
8
9
10
11
12
final public class Manager{
private static int AAA = 1;
private static int BBB = 2;
private static int CCC = 3;

final public void testFinal(){
System.out.println("test");
}
public void testPublic(){
System.out.println("test");
}
}

测试方法

1
2
3
4
5
6
7
MyClassLoader myClassLoader = new MyClassLoader();
Class<?> aClass = myClassLoader.loadClass(Manager.class.getName());
System.out.println(Modifier.toString(aClass.getModifiers()));
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(Modifier.toString(method.getModifiers()));
}

测试结果

1
2
3
4
findClass: test.Manager
public
public
public