背景
cglib是基于继承的代理的,但是final修饰的类的方法不能被代理,毕竟虚拟机规范限定了final关键字,想要实现代理的功能,自己不要加上final就是了。但是现在别人写好了库里的类,不方便修改想要尝试。
1.自定义Classloader,利用ASM去掉final关键字
jvm利用双亲委派机制加载类,优先尝试让父类加载,父类加载成功,自己就没有机会加载,这是安全和防止重复加载的机制。默认情况下自己写的class是程序加载器加载的,jvm认为Classloder+类路径名 完全一样才是相同的一个class。所以用自己的加载器加载的类,没法转换成为之前的的引用。想要调用和访问,也只能利用反射。
比如这样是不行的
Person person = new MyClassLoader(Person.class).newInstance()
public class MyClassLoader extends ClassLoader {public MyClassLoader() {//指定父加载器为nullsuper(null);}@Overrideprotected 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);}@Overridepublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {// we have the final flagif((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL) {//remove the final flagaccess = access ^ Opcodes.ACC_FINAL;}// 在调用super.visit的时候,我们就已经把final关键字去掉了super.visit(version, access, name, signature, superName, interfaces);}@Overridepublic 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);}}
测试
测试类
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");}}
测试方法
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()));}
测试结果
findClass: test.Managerpublicpublicpublic
2 用Aspectj 切点?
aspecj是在编译时修改,app在打包的时候,并不会打包sdk里面的class,也就是说没法把修改过的class打包到手机里面,也就没法拦截。

