Javassist动态创建修改Class

2,653 阅读3分钟

Javassist 是什么 ?

javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

使用Javassist

1.动态生成接口实现类

先来看看我们的接口Person,就是一个简单的接口

package com.yad.javassitdemo.t1;

public interface Person {
    void  say (String msg );
}

现在我们不使用硬编码而是通过Javassist来生成接口实现类

package com.yad.javassitdemo.t1;

import javassist.*;

import java.lang.reflect.Method;
/*
  使用javassist 来动态生成实现接口的Class
 */
public class Test2 {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        //com.yad.javassitdemo.t1.Man
        String clazzName = Person.class.getName()+".Man";

        // public class Man {}
        CtClass targetClass = pool.makeClass(clazzName);

        //获得接口的CtClass
        CtClass interf = pool.getCtClass(Person.class.getName());
        CtClass[] interfs = new CtClass[]{interf};
        // public class Man implements Person{}
        targetClass.setInterfaces(interfs);

        CtMethod[] personMethods = interf.getDeclaredMethods();
        for (CtMethod method : personMethods){
            CtMethod m = new CtMethod(method.getReturnType(),method.getName(),method.getParameterTypes(),targetClass);
            //$0 = this $1 , $2 表示方法参数
            m.setBody("{System.out.println($1);}");
            targetClass.addMethod(m);
        }
        //生成Class文件
        targetClass.writeFile("C:\\Users\\yad\\Code\\OpenSource\\yorm\\src\\main\\resources");
        //生成Class载入内存
        Class target = targetClass.toClass();
        //实例化
        Object instance = target.newInstance();
        //调用方法
        Method method = instance.getClass().getMethod("say",String.class);
        method.invoke(instance,"hello world");
    }
}

来看看生成的Class文件

package com.yad.javassitdemo.t1;

public class Man implements Person {
    public void say(String var1) {
        System.out.println(var1);
    }

    public Man() {
    }
}

2.动态生成抽象类的实现类

先创建一个抽象类

package com.yad.javassitdemo.t1;

public abstract class Computer {
    public  abstract  int  sum(int a ,int b);
}

用Javassist来生成抽象类的实现类

package com.yad.javassitdemo.t1;

import javassist.*;

import java.lang.reflect.Field;

/**
 * 动态继承抽象类并实现
 */
public class Test3 {
    public static void main(String[] args) throws  Exception {
        ClassPool pool = ClassPool.getDefault();

        //com.yad.javassitdemo.t1.Computer
        String abstractName = Computer.class.getName();
        //相当于com.yad.javassitdemo.t1.ComputerV1
        String instanceName = abstractName +"V1";
        //获得Computer 抽象类的 CtClass
        CtClass abstractClass = pool.get(abstractName);
        //public  class ComputerV1  extends Computer{}
        CtClass targetClass = pool.makeClass(instanceName,abstractClass);


        //这里就只添加 sum 方法进行实现
        CtMethod[] methods = abstractClass.getDeclaredMethods();
        for (CtMethod m : methods){
            if (m.getName().equals("sum")){
                CtMethod newMethod = new CtMethod(m.getReturnType(),m.getName(),m.getParameterTypes(),targetClass);
                //相当于 return a + b;
                newMethod.setBody("{ return $1+$2;}");
                targetClass.addMethod(newMethod);
            }
        }

        //添加属性
        CtField version = CtField.make("public int version ;",targetClass);
        targetClass.addField(version,CtField.Initializer.constant(1)); //设置初始化属性


        targetClass.writeFile("C:\\Users\\yad\\Code\\OpenSource\\yorm\\src\\main\\resources");

        //测试方法
        Class realClass = targetClass.toClass();
        Computer instance = (Computer) realClass.newInstance();
        int sum = instance.sum(1, 2);
        System.out.println(sum);
        //测试属性
        Field Fversion = instance.getClass().getField("version");
        Integer versionValue = (Integer) Fversion.get(instance);
        System.out.println(versionValue);

    }
}

来看看生成的字节码

package com.yad.javassitdemo.t1;

public class ComputerV1 extends Computer {
    public int version = 1;
    public int sum(int var1, int var2) {
        return var1 + var2;
    }
    public ComputerV1() {}
}

3.动态的给一个类的方法插入代码

首先我们来看看我们要进行修改的类

package com.yad.javassitdemo.t1;

public class Man implements Person {
    public  Man(){}
    @Override
    public void say(String msg) {
    	//通过Javassist 在执行方法前插入代码
        System.out.println(msg);
    	//通过Javassist 在执行方法后插入代码
    }
}

使用Javassit 进行修改

package com.yad.javassitdemo.t1;

import javassist.*;
import java.lang.reflect.Method;

public class Test4 {
    public static void update() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        // 参数不能是 Man.class.getName()  因为这样子ClassLoader会把 Man.class 直接载入内存
        CtClass manCtClass = pool.get("com.yad.javassitdemo.t1.Man");

        CtMethod method =  manCtClass.getDeclaredMethod("say",new CtClass[]{pool.get(String.class.getName())});
        //修改方法
        method.insertBefore("System.out.println(\" 执行方法之前前 \");");
        method.insertAfter("System.out.println(\" 执行方法之后后 \");");


        manCtClass.writeFile("C:\\Users\\yad\\Code\\OpenSource\\yorm\\src\\main\\resources");
        Class manCLass = manCtClass.toClass();
        Object instance = manCLass.newInstance();

        Method say = Man.class.getMethod("say", String.class);
        say.invoke(instance,"hello world");
    }
    public static void main(String[] args) {
        try {
            update();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

来看看修改后的Class 文件

package com.yad.javassitdemo.t1;

public class Man implements Person {
    public Man() {
    }

    public void say(String msg) {
        System.out.println(" 执行方法之前前 ");
        System.out.println(msg);
        Object var3 = null;
        System.out.println(" 执行方法之后后 ");
    }
}

可以看到原来的类被修改了,这个应该就能够实现AOP

上面通过Javassist提供的API我们很方便的进行对Class的动态创建和修改
这样我们可以摆脱重复的硬编码,使用反射和Javassist 灵活的创建修改我们想要的Class