zoukankan      html  css  js  c++  java
  • java----字节码操作

    Java动态性的两种常见方式

      -反射

      -字节码操作:所谓字节码操作就是当xx.class文件被加载到虚拟机后,我们可以使用类库来操作这些字节码

    运行时操作字节码可以让我们实现如下功能 

      -动态生成新的类
      -动态改变某个类的结构(添加/删除/修改新的属性/方法

    优势:

      -比反射开销小,性能高。
      -JAVAasist性能高于反射,低于ASM

    常见的字节码操作类库

      BCEL

      ASM

      CGLIB(Code Generation Library)

      Javassist

    Javassist使用

    如果需要详细了解:可以下载框架

    maven依赖jar包

    <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.25.0-GA</version>
    </dependency>
    

    简单使用

    字节码不能操作对象,如果需要操作对象还需要借助反射

    创建新类

        public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ScriptException, CannotCompileException, NotFoundException {
            //获得类池
            ClassPool aDefault = ClassPool.getDefault();
    
            //创建类的名字 可以指定包名 例如:com.zy.Dog
            CtClass ctClass = aDefault.makeClass("Dog");
    
            //添加属性(可以new CtField,和添加方法类似(在test01中提及))
            CtField public_int_age = CtField.make("public int age;", ctClass);
            CtField public_string_like= CtField.make("public String like;", ctClass);
            ctClass.addField(public_int_age);
            ctClass.addField(public_string_like);
    
            //添加方法
            CtMethod test = CtMethod.make("public void test(){System.out.println("添加成功");}", ctClass);
            ctClass.addMethod(test);
    
            //添加构造器
            CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType,aDefault.get("java.lang.String")}, ctClass);
            ctConstructor.setBody("{this.age=$1;this.like=$2;}");
            ctClass.addConstructor(ctConstructor);
    
            //开始创建一个Dog.class文件,可以指定Dog.class文件路径
            ctClass.writeFile();
        }

    操作已经存在的类

    1、给存在的.class文件追加方法.让对象去执行这个方法

    public static void test01(){
            try {
                ClassPool aDefault = ClassPool.getDefault();
                CtClass ctClass = aDefault.get("com.zy.Dog");
                //byte[] bytes = ctClass.toBytecode();
                //获取类名
                System.out.println(ctClass.getName());
                //获取简单类名
                System.out.println(ctClass.getSimpleName());
    
                //添加方法
                //CtMethod test1 = CtMethod.make("public void test(int a,int b){System.out.println("添加成功");}", ctClass);
                //另一种添加方法的方式
                CtMethod ctMethod = new CtMethod(CtClass.intType, "t", new CtClass[]{CtClass.intType,CtClass.intType}, ctClass);
                ctMethod.setModifiers(Modifier.PUBLIC);
                ctMethod.setBody("return $1+$2;");
                ctClass.addMethod(ctMethod);
    
                //通过反射调用方法
                Class<?> aClass = ctClass.toClass();
                Method test = aClass.getDeclaredMethod("t",int.class,int.class);
                //对象0可以是外部传递进来,此时test方法就是一个代理方法
                Object o = aClass.getConstructor().newInstance();
                Object invoke = test.invoke(o, 1,1);
                System.out.println(invoke);
            } catch (NotFoundException e) {
                e.printStackTrace();
            } catch (CannotCompileException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    

    2、通过字节码实现AOP编程

    此时test02必须是一个代理类的方法。我们将自己的对象通过test02传递进去

    public static void test02(){
            try {
                ClassPool aDefault = ClassPool.getDefault();
                CtClass ctClass = aDefault.get("com.zy.Dog");
                CtMethod play = ctClass.getDeclaredMethod("play");
                //可以对已经存在的方法进行修改
                //play.setBody("System.out.println("Dog没有play");");
                //执行方法之前可以执行其他的方法;
                play.insertBefore("System.out.println("Dog在吃东西");");
                
                //反射执行该方法
                Class<?> aClass = ctClass.toClass();
                Method test = aClass.getDeclaredMethod("play");
                Object o = aClass.getConstructor().newInstance();
                Object invoke = test.invoke(o);
            } catch (NotFoundException e) {
                e.printStackTrace();
            } catch (CannotCompileException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    
  • 相关阅读:
    VS2013
    有了门面,程序会更加体面!- pos软件基于三层架构 -09
    无熟人难办事?- 闲聊设计模式-迪米特法则
    三层架构,分层开发
    Filezilla 错误
    归档和压缩
    在Linux系统下用dd命令制作ISO镜像U盘启动盘
    脚本语言
    node.js知识点提取
    npm cnpm
  • 原文地址:https://www.cnblogs.com/yanxiaoge/p/11625552.html
Copyright © 2011-2022 走看看