javassist 运行期改类

https://www.cnblogs.com/baiqiantao/p/10235049.html

https://www.cnblogs.com/xiaofuge/p/12868742.html

 

 

结论:

1 加载前可以直接替换,加载(new,loadclass,forname)后要hotspot开端口 

2 函数入参变量使用$1, $2,数组参数$2[0];成员变量用变量名

3 函数中,所有类要用类全名(包括同包),除了String,可能属于启动类加载器

4 空格 \n \t无关紧要

5 泛型不可用

 

碰到的问题:

1 泛型不可用

2 javassist.CannotCompileException: [source error] no such class 用全限定名

 

package javassisttest;

import lc.A;
import javassisttest.ByRef;

/**
 * Created by joyce on 2020/7/29.
 */
public class ByMod {

    /**
     * 测试
     * 1 是否能加载器/加载后替换函数体
     * 加载前可以直接替换,加载后要hotspot开端口
     * 2 局部变量引用
     * 要用$1 $2
     * 3 局部数组变量引用
     * $x[0] $x[1]
     * 4 String.class是否能引用到,是否需要java.lang.String.class
     * 不需要,可能启动类加载器的不需要
     * 5 \n 空格  \t
     * 6 用全名引用类,import无用,同包无用
     * 7 泛型不可用
     */
    public static final String MODIFIED = "{System.out.println(\"javassist \" + mem +  $1 + $2[0] + String.class + \", modified\");\n" +
            "   \tSystem.out.println(\"done\");\n" +
            "   \tSystem.out.println(lc.A.class);\n" +
         //7   "Class<? extends java.lang.Object> o = null;" +
            "   \tSystem.out.println(javassisttest.ByRef.class);\n}";

    /**
     * 测试
     * 1 函数内如何引用成员变量
     */
    private String mem = "memval";

    /**
     * 测试
     * 1 数组类型入参
     * 2 函数内如何引用方法局部变量
     * @param xxx
     * @param objects
     */
    public void print(String xxx, Object [] objects) {
        System.out.println("not yet mod");
        Class<? extends Object> o = null;
    }
}

 

package javassisttest;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMember;
import javassist.CtMethod;

import static javassisttest.ByMod.MODIFIED;

/**
 * Created by joyce on 2020/7/29.
 */
public class ModerBeforeLoad {

    public static void main(String [] f) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("javassisttest.ByMod");
        CtMethod cm = cc.getDeclaredMethod("print", new CtClass[]{pool.get("java.lang.String"), pool.get("java.lang.Object[]")});
        cm.setBody(MODIFIED);
        cc.toClass();
        ByMod byMod = new ByMod();
        byMod.print("be", new String[]{"heefsf"});
    }
}

 输出:

javassist memvalbeheefsfclass java.lang.String, modified
done
class lc.A
class javassisttest.ByRef

 

package javassisttest;

import com.sun.org.apache.xpath.internal.operations.Mod;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.util.HotSwapper;
import lc2.E;

import static javassisttest.ByMod.MODIFIED;

/**
 * Created by joyce on 2020/7/29.
 */
public class ModerAfterLoad {
    public static void main(String [] f) throws Exception {
        /**
         * 这三种方式都会造成
         * by java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader):
         * attempted  duplicate class definition for name: "javassisttest/ByMod"
         */
//        ByMod byMod = new ByMod();
//        Class.forName("javassisttest.ByMod");
        ModerAfterLoad.class.getClassLoader().loadClass("javassisttest.ByMod");

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("javassisttest.ByMod");
        CtMethod cm = cc.getDeclaredMethod("print", new CtClass[]{pool.get("java.lang.String"), pool.get("java.lang.Object[]")});
        cm.setBody(MODIFIED);
        try {
            cc.toClass();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        ByMod byMod = new ByMod();
        byMod.print("he", new String[]{"xxx"});

        // idea VM : -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
        HotSwapper hs = new HotSwapper(8000);
        hs.reload(ByMod.class.getName(), cc.toBytecode());

        byMod.print("he", new String[]{"xxx"});
    }
}

 

输出:

Listening for transport dt_socket at address: 8000
by java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "javassisttest/ByMod"
not yet mod
javassist memvalhexxxclass java.lang.String, modified
done
class lc.A
class javassisttest.ByRef

 

 

实践:myorm【重点】$$27

 

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ToBeInterceptedForMapper {
    Class<? extends MapperProcessor> mapperProcessor();

  

abstract public class AbstractTimezoneProcessor extends DefaultMapperProcessor {

    @Override
    public Object execute() throws Exception {
        Object target = super.getValue();
        Class c = target.getClass();
        Method method = super.getMethod();
        logger.info("mapper aop {} - {}.{}", this.getClass().getName(), c, method);
        //return doExecute();
        return super.execute();
    }

    private Object doExecute() throws Exception {

        Object target = super.getValue();
        Class c = target.getClass();
        Method method = super.getMethod();
        Object[] args = super.getArgs();

        ToBeInterceptedForMapper annotation = (ToBeInterceptedForMapper)method.getAnnotation(ToBeInterceptedForMapper.class);

        if(annotation != null) {
            return doTimezoneProcessor();
        } else {
            logger.error("error timezone aop : {}, {}, {}", c, method, args);
            return method.invoke(target, args);
        }
    }

    abstract protected Object doTimezoneProcessor() throws Exception;

    abstract protected Date turnDate(Date origin, int dbZone);

    protected void setDate(Object obj, int dbZone) throws Exception {

        Class cl = obj.getClass();
        Field[] fields = cl.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);

            if(!java.util.Date.class.isAssignableFrom(field.getType()))
                continue;

            if (Modifier.isStatic(field.getModifiers()))
                continue;

            if (field.isAnnotationPresent(SCEF_DB_NO_FIELD.class))
                continue;

            if(field.get(obj) == null)
                continue;

            Date date = (Date)field.get(obj);
            Date tranzDate = turnDate(date, dbZone);
            field.set(obj, tranzDate);
        }
    }

  

public class TimezoneProcessorForInsertAndUpdate extends AbstractTimezoneProcessor {

    @Override
    protected Object doTimezoneProcessor() throws Exception {
        Object target = super.getValue();
        Method method = super.getMethod();
        Object[] args = super.getArgs();

        int dbZone = -4;

        setDate(args[0], dbZone);
        Object res = method.invoke(target, args);

        return res;
    }

    @Override
    protected Date turnDate(Date origin, int dbZone) {
        return TimezoneManager.timezoneJvmToDbWhenInsertAndUpdate(origin, dbZone);
    }
}

  

public class TimezoneProcessorForQuery extends AbstractTimezoneProcessor {

    @Override
    protected Object doTimezoneProcessor() throws Exception {
        Object target = super.getValue();
        Method method = super.getMethod();
        Object[] args = super.getArgs();
        int dbZone = -4;

        Object res = method.invoke(target, args);
        Class resType = res.getClass();
        if(List.class.isAssignableFrom(resType) || Set.class.isAssignableFrom(resType)) {
            Collection collection = (Collection) res;
            Iterator iterator = collection.iterator();
            while (iterator.hasNext()) {
                Object obj = iterator.next();
                setDate(obj, dbZone);
            }
        } else {
            setDate(res, dbZone);
        }

        return res;
    }

    @Override
    protected Date turnDate(Date origin, int dbZone) {
        return TimezoneManager.timezoneDbToJvmWhenQuery(origin, dbZone);
    }


}

  

 

posted on 2020-07-29 21:45  silyvin  阅读(761)  评论(0编辑  收藏  举报