zoukankan      html  css  js  c++  java
  • Javassist简介

    Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。它可以用来检查、”动态”修改以及创建 Java类。其功能与jdk自带的反射功能类似,但比反射功能更强大。

       

    package com.javassis.test;
    
    public class CoolGuy {
        private String hotGirlfriendName;
        private String niceCarModel;
        public String getHotGirlfriendName() {
            return hotGirlfriendName;
        }
        public void setHotGirlfriendName(String hotGirlfriendName) {
            this.hotGirlfriendName = hotGirlfriendName;
        }
        public String getNiceCarModel() {
            return niceCarModel;
        }
        public void setNiceCarModel(String niceCarModel) {
            this.niceCarModel = niceCarModel;
        }
        
    }
    
    public class Person {
        private int age;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
        
    }
    package com.javassis.test;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtField;
    import javassist.CtMethod;
    import javassist.CtNewMethod;
    import javassist.Loader;
    import javassist.NotFoundException;
    
    public class Main {
        public static void main(String[] args) throws NotFoundException, NoSuchMethodException, SecurityException,
                InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
                CannotCompileException, IOException, ClassNotFoundException {
            ClassPool classPool = ClassPool.getDefault();
            /**
             * CoolGuy是已经存在的类
             */
            CtClass ctc = classPool.get("com.javassis.test.CoolGuy");
            Class<?> c = ctc.toClass();
            Object cg = (Object) c.newInstance();
            Method m = c.getDeclaredMethod("setHotGirlfriendName", String.class);
            m.invoke(cg, "柳岩");
            System.out.println("CoolGuy's girlfriend is " + ((CoolGuy) cg).getHotGirlfriendName());
            /**
             * 动态构造一个SuckGuy类
             */
            CtClass suckCt = classPool.makeClass("com.javassis.test.SuckGuy");
            // 添加域
            CtField girlfriendField = new CtField(classPool.get("java.lang.String"), "hotGirlfriendName", suckCt);
            CtField carField = new CtField(classPool.get("java.lang.String"), "niceCarModel", suckCt);
            suckCt.addField(girlfriendField);
            suckCt.addField(carField);
            // 添加方法
            CtMethod getMethod = CtNewMethod.make("public String getHotGirlfriendName() { return this.hotGirlfriendName;}",
                    suckCt);
            CtMethod setMethod = CtNewMethod
                    .make("public void setHotGirlfriendName(String girl) { this.hotGirlfriendName = girl;}", suckCt);
            suckCt.addMethod(getMethod);
            suckCt.addMethod(setMethod);
            getMethod = CtNewMethod.make("public String getNiceCarModel() { return this.niceCarModel;}", suckCt);
            setMethod = CtNewMethod
                    .make("public void setNiceCarModel(String niceCarModel) { this.niceCarModel = niceCarModel;}", suckCt);
            suckCt.addMethod(getMethod);
            suckCt.addMethod(setMethod);
            Class<?> cuckClass = suckCt.toClass();
            Object suckGuy = cuckClass.newInstance();
            m = cuckClass.getDeclaredMethod("setHotGirlfriendName", String.class);
            m.invoke(suckGuy, "凤姐");
            String suckGirlfriend = (String) cuckClass.getMethod("getHotGirlfriendName", null).invoke(suckGuy, null);
            System.out.println("SuckGuy's girlfriend is " + suckGirlfriend);
            /**
             * 设置CoolGuy父类为Person
             */
            //ctc.writeFile();
            ctc.defrost(); // 先解冻,不然会报class is frozen异常,
            // CtClass对象通过writeFile()、toClass()、toBytecode()转化为Class后,Javassist冻结了CtClass对象,因此,JVM不允许再次加载Class文件,所以不允许对其修改
            ctc.setSuperclass(classPool.get("com.javassis.test.Person"));
            Loader cl = new Loader(classPool);// 同一个classloader下不能加载两次class,所以新new一个loader
            // c = ctc.toClass();
            c = cl.loadClass("com.javassis.test.CoolGuy");
            cg = (Object) c.newInstance();
            m = c.getMethod("setAge", int.class);
            m.invoke(cg, 25);
            m = c.getMethod("getAge", null);
            System.out.println("CoolGuy's age is " + m.invoke(cg, null));
        }
    }

     输出:

    CoolGuy's girlfriend is 柳岩
    SuckGuy's girlfriend is 凤姐
    CoolGuy's age is 25

    几个需要注意的地方:

    1、笔者用的Javassist版本是3.22.0-GA,比较坑爹的是它必须使用java 9,笔者一开始用java 8会报java.lang.NoClassDefFoundError: java/lang/StackWalker$Option

    2、CtClass对象通过writeFile()、toClass()、toBytecode()转化为Class后,Javassist冻结了CtClass对象,因此,JVM不允许再次加载Class文件,所以不允许对其修改

    ,再次对其修改时需要defrost() 先解冻,不然会报class is frozen异常。

    3、解冻后对类进行修改,之后使用修改后的类需要再次加载,此时虽然已经经过defrost() ,但同一个classloader下不能加载两次class,再次使用toClass()会报错,所以新new一个loader,使用loader.loadClass()完成Class加载。

    接下来看看对如何动态的去修改类的方法:

    思路就是把需要修改的方法改个新名字,然后新建一个新方法,名字命名为原名字,新方法copy原方法的内容并加入新的代码。

    package com.javassis.test;
    
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    import javassist.CtNewMethod;
    
    /*
     * 动态修改CoolGuy的一个方法
     * */
    public class UpdateTest {
        public static void main(String[] args) throws Exception {
            ClassPool classPool = ClassPool.getDefault();
            CtClass ctc = classPool.get("com.javassis.test.CoolGuy");
            String mName = "getHotGirlfriendName";
            CtMethod m = ctc.getDeclaredMethod(mName);
            m.setName(mName + "$old");
            CtMethod nm = CtNewMethod.copy(m, mName, ctc, null);
            StringBuffer mbody = new StringBuffer();
            mbody.append("{");
            mbody.append("
     return hotGirlfriendName");
            mbody.append(" + "和鞠婧祎"; 
    ");
            mbody.append("}");
            nm.setBody(mbody.toString());
            ctc.addMethod(nm);
            Class<?> c = ctc.toClass();
            CoolGuy coolGuy = (CoolGuy)c.newInstance();
            coolGuy.setHotGirlfriendName("柳岩");
            System.out.println("CoolGuy's girlfriend is " + coolGuy.getHotGirlfriendName());
        }
    }

    运行结果:

    CoolGuy's girlfriend is 柳岩和鞠婧祎

     

  • 相关阅读:
    动态生成 Excel 文件供浏览器下载的注意事项
    JavaEE 中无用技术之 JNDI
    CSDN 泄露用户密码给我们什么启示
    刚发布新的 web 单点登录系统,欢迎下载试用,欢迎提建议
    jQuery jqgrid 对含特殊字符 json 数据的 Java 处理方法
    一个 SQL 同时验证帐号是否存在、密码是否正确
    PostgreSQL 数据库在 Windows Server 2008 上安装注意事项
    快速点评 Spring Struts Hibernate
    Apache NIO 框架 Mina 使用中出现 too many open files 问题的解决办法
    解决 jQuery 版本升级过程中出现 toLowerCase 错误 更改 doctype
  • 原文地址:https://www.cnblogs.com/lyhero11/p/8441250.html
Copyright © 2011-2022 走看看