背景

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

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

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

比如这样是不行的

  1. Person person = new MyClassLoaderPerson.class).newInstance()
  1. public class MyClassLoader extends ClassLoader {
  2. public MyClassLoader() {
  3. //指定父加载器为null
  4. super(null);
  5. }
  6. @Override
  7. protected Class<?> findClass(String name) throws ClassNotFoundException {
  8. System.out.println("findClass: "+name);
  9. try {
  10. ClassReader reader = new ClassReader(name);
  11. ClassWriter writer = new ClassWriter(reader, 0);
  12. RemoveFinalFlagClassVisitor classVisitor = new RemoveFinalFlagClassVisitor(writer);
  13. reader.accept(classVisitor, ClassReader.SKIP_CODE);
  14. byte[] bytes = writer.toByteArray();
  15. return defineClass(name, bytes, 0, bytes.length);
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. return null;
  20. }
  21. }
  22. // 利用ClassVisitor去掉final的修饰
  23. class RemoveFinalFlagClassVisitor extends ClassVisitor {
  24. public RemoveFinalFlagClassVisitor(ClassVisitor cv) {
  25. super(Opcodes.ASM5, cv);
  26. }
  27. @Override
  28. public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
  29. // we have the final flag
  30. if((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL) {
  31. //remove the final flag
  32. access = access ^ Opcodes.ACC_FINAL;
  33. }
  34. // 在调用super.visit的时候,我们就已经把final关键字去掉了
  35. super.visit(version, access, name, signature, superName, interfaces);
  36. }
  37. @Override
  38. public MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings) {
  39. /*可以在这里修改final修饰非方法
  40. 这里可以利用debug查看修饰的特征值
  41. */
  42. if (i==17){
  43. return super.visitMethod(1, s, s1, s2, strings);
  44. }
  45. return super.visitMethod(i, s, s1, s2, strings);
  46. }
  47. }

测试

测试类

  1. final public class Manager{
  2. private static int AAA = 1;
  3. private static int BBB = 2;
  4. private static int CCC = 3;
  5. final public void testFinal(){
  6. System.out.println("test");
  7. }
  8. public void testPublic(){
  9. System.out.println("test");
  10. }
  11. }

测试方法

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

测试结果

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

2 用Aspectj 切点?

aspecj是在编译时修改,app在打包的时候,并不会打包sdk里面的class,也就是说没法把修改过的class打包到手机里面,也就没法拦截。

9f4b4abf768b8df9a1ad.jpg

3.自己修改框架代码,push到手机