美文网首页
java动态增加方法

java动态增加方法

作者: 繁星若尘啊 | 来源:发表于2017-10-25 23:02 被阅读1034次

    java动态增加方法

    package com.lux.study.assist;
    
    import javassist.*;
    
    /**
     * @author: lux
     * @date: 2017/10/25 14:54
     */
    public class App {
        public static void main(String[] args) {
            try {
                String className = "com.lux.study.assist.UserInfo";
                UserInfo userInfo = new UserInfo();
                userInfo.setName("test");
                userInfo.setId(1);
                System.out.println("before:" + userInfo);
                ClassPool pool = ClassPool.getDefault();
                CtClass cc = pool.get(className);
                CtMethod mthd = CtNewMethod.make("public String test() { return \"test() is called \"+ toString();  }", cc);
                cc.addMethod(mthd);
    
                AppClassLoader appClassLoader = AppClassLoader.getInstance();
                Class<?> clazz = appClassLoader.findClassByBytes(className, cc.toBytecode());
    //            clazz.getDeclaredConstructor().newInstance();
                Object obj = appClassLoader.getObj(clazz,userInfo);
                System.out.println("after:" + obj);
                //测试反射调用添加的方法
                System.out.println(obj.getClass().getDeclaredMethod("test").invoke(obj));
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    }
    
    
    package com.lux.study.assist;
    
    
    import java.lang.reflect.Field;
    
    /**
     * @author: lux
     * @date: 2017/10/24 13:48
     */
    public class AppClassLoader extends ClassLoader {
    
        private static class SingletonHolder {
            public final static AppClassLoader instance = new AppClassLoader();
        }
    
        public static AppClassLoader getInstance() {
            return SingletonHolder.instance;
        }
    
    
        private AppClassLoader() {
    
        }
    
        /**
         * 通过classBytes加载类
         *
         * @param className
         * @param classBytes
         * @return
         */
        public Class<?> findClassByBytes(String className, byte[] classBytes) {
            return defineClass(className, classBytes, 0, classBytes.length);
        }
    
        /**
         * 复制对象所有属性值,并返回一个新对象
         *
         * @param srcObj
         * @return
         */
        public Object getObj(Class<?> clazz, Object srcObj) {
            try {
                Object newInstance = clazz.getDeclaredConstructor().newInstance();
                Field[] fields = srcObj.getClass().getDeclaredFields();
                for (Field oldInstanceField : fields) {
                    String fieldName = oldInstanceField.getName();
                    oldInstanceField.setAccessible(true);
                    Field newInstanceField = newInstance.getClass().getDeclaredField(fieldName);
                    newInstanceField.setAccessible(true);
                    newInstanceField.set(newInstance, oldInstanceField.get(srcObj));
                }
                return newInstance;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    
    package com.lux.study.assist;
    
    /**
     * @author: lux
     * @date: 2017/10/24 13:51
     */
    public class UserInfo {
        private Integer id;
        private String name;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "UserInfo{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    
    before:UserInfo{id=1, name='test'}
    after:UserInfo{id=1, name='test'}
    test() is called UserInfo{id=1, name='test'}
    

    借鉴了spring devtool的热部署思想,通过使用javassist或者cglib来实现字节码的生成,然后通过自定义的类加载器加载修改之后的类,最后使用反射实现属性值的拷贝,最后就可以得到一个和之前看起来差不多的类,但是却有我们自定义方法的对象。

    相关文章

      网友评论

          本文标题: java动态增加方法

          本文链接:https://www.haomeiwen.com/subject/smacpxtx.html