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);
}
}
浙公网安备 33010602011771号