zoukankan      html  css  js  c++  java
  • 8.5(java学习笔记)8.5 字节码操作(javassist)

    一、javassist

      javassist让我们操作字节码更加简单,它是一个类库,允许我们修改字节码。它允许java程序动态的创建、修改类。

      javassist提供了两个层次的API,基于源码级别的和字节码级别的。

       

    二、javassist创建类

      1.获取类池

      2.在类池中添加要创建的类

      3.添加变量,方法,构造器

      4.将创建好的类写出 

    import java.io.IOException;
    import java.lang.String;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtConstructor;
    import javassist.CtField;
    import javassist.CtMethod;
    import javassist.NotFoundException;
    
    public class TestSsist {
        public static void main(String[] args) throws CannotCompileException, NotFoundException {
            //获取默认类池
            ClassPool pool = ClassPool.getDefault();
            //在类池中创建一个类
            CtClass cc = pool.makeClass("com.TestSsist.TestUser");//包名(com.TestSsist)+类名(TestUser)
            //创建属性                                                           
            CtField name = CtField.make("private String name;", cc);//(代码,类)
            CtField age = CtField.make("private int age;", cc);
            //添加属性
            cc.addField(name);
            cc.addField(age);
            
            //创建方法
            CtMethod setAll = CtMethod.make("public void setName(String name,int age){"
                    + "this.name = name;"
                    + "this.age = age;"
                    + "}", cc);
            CtMethod getName = CtMethod.make("public String getName(){"
                    + "return this.name;"
                    + "}", cc);
            //添加方法
            cc.addMethod(setAll);
            cc.addMethod(getName);
            
            //创建构造器
            CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String"),CtClass.intType},cc);
            //设置构造器体内容,即{}部分内容
            constructor.setBody("{this.name = name;"
                    + "this.age = age;}");
            //添加构造器
            cc.addConstructor(constructor);
            try {
                cc.writeFile("F:\TestJava");//将创建的类写出到指定路径
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("创建成功!");
        }
    }
    运行结果:
    创建成功

    我们可以看指定目录下有创建好的class文件。

    字节码文件我们是无法直接查看的,我们可以通过工具(XJad)将其反编译进行查看我们创建好的类。

    三、获取类基本信息(该类已存在)

    测试类(TestUser)内容如下:(该类已存在)

    import java.io.Serializable;
    
    public class TestUser implements Serializable{
        private String naem;
        private int age;
        
        public TestUser() {}
        
        public TestUser(String naem, int age) {
            super();
            this.naem = naem;
            this.age = age;
        }
        
        public String getNaem() {
            return naem;
        }
        public void setNaem(String naem) {
            this.naem = naem;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        
        public void testSsist(int a){
            System.out.println("ssist:"+a);
        }
    }

      public String getName();//获取类名

      public CtClass getSuperclass();//获取父类

      public CtClass[] getInterfaces();//获取接口

      

      1.获取类池

      2.获取指定类

      3.获取类相关信息

      

    import java.io.IOException;
    import java.lang.String;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtConstructor;
    import javassist.CtField;
    import javassist.CtMethod;
    import javassist.NotFoundException;
    
    public class TestSsist {
        public static void main(String[] args) throws CannotCompileException, NotFoundException {
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.get("TestSsist.TestUser");
            System.out.println("类名:" + cc.getName());
            System.out.println("父类:"+cc.getSuperclass().getName());
            for(CtClass temp:cc.getInterfaces())
                System.out.println("接口:" + temp.getName());
        }
    }
    运行结果:
    类名:TestSsist.TestUser
    父类:java.lang.Object
    接口:java.io.Serializable

    四、动态添加方法

      1.获取类池

      2.获取对应类(TestUser)

      3.动态添加方法

      4.通过反射调用动态添加的方法  

    import java.io.IOException;
    import java.lang.String;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtConstructor;
    import javassist.CtField;
    import javassist.CtMethod;
    import javassist.NotFoundException;
    
    public class TestSsist {
        public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException,
                                   SecurityException, InstantiationException, IllegalAccessException { ClassPool pool
    = ClassPool.getDefault();//获取类池 CtClass cc = pool.get("TestSsist.TestUser");//获取指定类 //public CtMethod(CtClass returnType, String mname,CtClass[] parameters, CtClass declaring) //动态添加方法 CtMethod mAdd = new CtMethod(CtClass.intType,"add", new CtClass[]{CtClass.intType,CtClass.intType},cc); mAdd.setModifiers(Modifier.PUBLIC);//设置方法访问权限 //因为我们在前面添加方法时只指定了形参类型,并没有指定形参名,所以要用如下方法设置。 //设置方法体,$1+$2,第一个参数+第二个参数:例如int add(int a,int b){...} retutn $1+$2就等于return a+b; mAdd.setBody("{System.out.println($1 +"+"+ $2);return $1+$2;}"); cc.addMethod(mAdd); //通过反射调用动态生成的方法 Class cl = cc.toClass(); Object u1 = cl.newInstance(); //获取指定方法 Method addm = cl.getDeclaredMethod("add",int.class,int.class); Object result = null; try { result = addm.invoke(u1,50,60);//调用方法 } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(result); } }
    运行结果:
    50+60
    110

     添加的方法只是运行时存在,运行结束就会消失,并不会将其真正写入TestUser类中,如果想保存的话,就通过writeFile写出。

    五、动态修改方法

       1.获取类池

       2.获取指定类(TestUser)

       3.获取指定类中指定方法

       4.调用insertBefore,insertAfter、insertAt插入新语句,即修改方法。

       5.通过反射调用动态修改后的方法

       

    import java.io.IOException;
    import java.lang.String;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtConstructor;
    import javassist.CtField;
    import javassist.CtMethod;
    import javassist.NotFoundException;
    
    public class TestSsist {
        public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException {
            ClassPool pool = ClassPool.getDefault();//获取类池
            CtClass cc = pool.get("TestSsist.TestUser");//获取指定类
            //获取指定方法,(方法名,方法类型(CtClass[]类型))
            CtMethod m1 = cc.getDeclaredMethod("testSsist",new CtClass[]{CtClass.intType});//获取类中指定方法
            //在32行插入语句
            m1.insertAt(32, "System.out.println("32");");
            //在方法体开头插入语句
            m1.insertBefore("System.out.println("start");");
            //在方法体末尾插入语句
            m1.insertAfter("System.out.println("end");");
            //通过反射调用动态修改后的方法
            Class cl = cc.toClass();
            Object u1 = cl.newInstance();
            //获取指定方法
            Method addm = cl.getDeclaredMethod("testSsist",int.class);
            Object result = null;
            try {
                
                result = addm.invoke(u1,50);//调用方法
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 
        }
    }
    运行结果:
    start ssist:
    50 32 end
    先执行方法体开头插入的语句
    System.out.println("start");
    然后执行方法中自带的语句System.out.println("ssist:"+a);
    然后执行插入到32行的语句Syste.out.println(32);
    最后执行方法体末尾插入的语句System.out.println("end");

     六、动态创建修改属性

       1.创建属性

       2.为对应属性创建set方法

       3.通过反射调用set方法为其赋值

       4.通过反射获取属性值

    import java.io.IOException;
    import java.lang.String;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtConstructor;
    import javassist.CtField;
    import javassist.CtMethod;
    import javassist.CtNewMethod;
    import javassist.NotFoundException;
    
    public class TestSsist {
        public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, ClassNotFoundException {
            ClassPool pool = ClassPool.getDefault();//获取类池
            CtClass cc = pool.get("TestSsist.TestUser");//获取指定类
            //动态创建password属性
            CtField nameF = CtField.make("private String password;", cc);
            cc.addField(nameF);
            //动态创建set方法
            CtMethod set = CtMethod.make("public void setPassword(String password){"
                    + "this.password = password;"
                    + "}", cc);
            cc.addMethod(set);
            Class clz = cc.toClass();
            Object test = clz.newInstance();
            try {
                //通过反射调用set方法,为password设置值
                Method setV = clz.getDeclaredMethod("setPassword",new Class[]{String.class});
                setV.invoke(test, "123456");
                //获取其属性值
                Field pw = clz.getDeclaredField("password");
                pw.setAccessible(true);//password是私有属性,设置不做访问检查
                System.out.println(pw.get(test));
            } catch (NoSuchFieldException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }
    }
    运行结果:
    123456

    本例所用javassist下载地址及版本:https://github.com/jboss-javassist/javassist/releasesJavassist 3.21.0-GA

  • 相关阅读:
    TCP传输粘包问题
    提取KIndle中每本书的笔记并单独保存
    # 可视化工具资源汇总
    抓取代理IP
    Linux 小工具学习之(1)——Wget十例[翻译]
    [转]关于矩阵的本质
    用2263份证件照图片样本测试how-old.net的人脸识别
    Python生成二维码脚本
    基于git的源代码管理模型——git flow
    使用Graphviz绘图(一)
  • 原文地址:https://www.cnblogs.com/huang-changfan/p/10130924.html
Copyright © 2011-2022 走看看