前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Javassist来动态创建,修改和代理类

使用Javassist来动态创建,修改和代理类

作者头像
算法之名
发布2019-08-20 10:30:43
2.9K0
发布2019-08-20 10:30:43
举报
文章被收录于专栏:算法之名算法之名

要使用Javassist,要先在POM中添加

代码语言:javascript
复制
<dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.3</version>
</dependency>

我们先使用Javassist来动态创建一个类,代码如下

代码语言:javascript
复制
public class JavassistMain {
    public static void main(String[] args) throws Exception {
        //创建ClassPool
        ClassPool cp = ClassPool.getDefault();
        //生成的类的名称为com.guanjian.assist.JavassistTest
        CtClass clazz = cp.makeClass("com.guanjian.assist.JavassistTest");
        StringBuffer body = null;
        //创建字段,指定了字段类型、字段名称、字段所属的类
        CtField field = new CtField(cp.get("java.lang.String"), "prop", clazz);
        //指定该字段使用private修饰
        field.setModifiers(Modifier.PRIVATE);
        //设置prop字段的getter/setter方法
        clazz.addMethod(CtNewMethod.getter("getProp",field));
        clazz.addMethod(CtNewMethod.setter("setProp",field));
        //设置prop字段的初始化值,并将prop字段添加到clazz中
        clazz.addField(field,CtField.Initializer.constant("MyName"));
        //创建构造方法,指定了构造方法的参数类型和构造方法所属的类
        CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, clazz);
        //设置方法体
        body = new StringBuffer();
        body.append("{\n prop=\"MyName\";\n}");
        ctConstructor.setBody(body.toString());
        //将构造方法添加到clazz中
        clazz.addConstructor(ctConstructor);
        //创建execute()方法,指定了方法的返回值、方法名称、方法参数列表以及方法所属的类
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "execute", new CtClass[]{}, clazz);
        //指定方法使用public修饰
        ctMethod.setModifiers(Modifier.PUBLIC);
        //设置方法体
        body = new StringBuffer();
        body.append("{\n System.out.println(\"execute():\" + this.prop);");
        body.append("\n}");
        ctMethod.setBody(body.toString());
        //将execute()方法添加到clazz中
        clazz.addMethod(ctMethod);
        //将上面定义的JavassistTest类保存到指定的目录
        clazz.writeFile("E:\\IOC\\target\\classes");
        //加载clazz类,并创建对象
        Class<?> c = clazz.toClass();
        Object o = c.newInstance();
        //调用execute()方法
        Method method = o.getClass().getMethod("execute", new Class[]{});
        method.invoke(o,new Object[]{});
    }
}

执行结果:

execute():MyName

执行以后我们可以在E:\IOC\target\classes\com\guanjian\assist中看见这个.class文件

双击该class文件,我们可以看见它的反编译的字节码

代码语言:javascript
复制
package com.guanjian.assist;

public class JavassistTest {
    private String prop = "MyName";

    public void getProp(String var1) {
        this.prop = var1;
    }

    public String setProp() {
        return this.prop;
    }

    public JavassistTest() {
        this.prop = "MyName";
    }

    public void execute() {
        System.out.println("execute():" + this.prop);
    }
}

现在我们来修改该类,添加一个属性整形age

代码语言:javascript
复制
public class JavassistUpdate {
    public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass clazz = cp.get("com.guanjian.assist.JavassistTest");
        //创建字段,指定了字段类型、字段名称、字段所属的类
        CtField field = new CtField(cp.get("java.lang.Integer"), "age", clazz);
        //指定该字段使用private修饰
        field.setModifiers(Modifier.PRIVATE);
        //设置age字段的getter/setter方法
        clazz.addMethod(CtNewMethod.setter("getAge",field));
        clazz.addMethod(CtNewMethod.getter("setAge",field));
        //将age字段添加到clazz中
        clazz.addField(field);
        clazz.writeFile("E:\\IOC\\target\\classes");
    }
}

运行后我们来看一下JavassistTest反编译的字节码

代码语言:javascript
复制
package com.guanjian.assist;

public class JavassistTest {
    private String prop = "MyName";
    private Integer age;

    public void getProp(String var1) {
        this.prop = var1;
    }

    public String setProp() {
        return this.prop;
    }

    public JavassistTest() {
        this.prop = "MyName";
    }

    public void execute() {
        System.out.println("execute():" + this.prop);
    }

    public void getAge(Integer var1) {
        this.age = var1;
    }

    public Integer setAge() {
        return this.age;
    }
}

这个时候我们可以看到age属性被添加了进去

前面我们讲过Java动态代理,CGLib动态代理,具体请参考 AOP原理与自实现

现在来看看Javassist的动态代理

代码语言:javascript
复制
public class JavassistProxy {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        ProxyFactory factory = new ProxyFactory();
        //指定父类,ProxyFactory会动态生成继承该父类的子类
        //因为没有.java源文件,此处会飘红,但有.class文件可以执行
        factory.setSuperclass(JavassistTest.class);
        //设置过滤器,判断哪些方法调用需要被拦截
        factory.setFilter(new MethodFilter() {
            @Override
            public boolean isHandled(Method method) {
                if (method.getName().equals("execute")) {
                    return true;
                }
                return false;
            }
        });
        //设置拦截处理
        factory.setHandler(new MethodHandler() {
            @Override
            public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
                System.out.println("前置处理");
                Object result = proceed.invoke(self, args);
                System.out.println("执行结果:" + result);
                System.out.println("后置处理");
                return result;
            }
        });
        //创建JavassistTest代理类,并创建代理对象
        //因为没有.java源文件,以下多处会飘红,但有.class文件可以执行
        Class<?> c = factory.createClass();
        JavassistTest javassistTest = (JavassistTest)c.newInstance();
        //执行execute()方法,会被拦截
        javassistTest.execute();
        System.out.println(javassistTest.getProp());
    }
}

运行结果:

前置处理

execute():MyName

执行结果:null

后置处理

MyName

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档