AsmMethodBean
This commit is contained in:
99
src/main/java/org/redkale/asm/AsmMethodBean.java
Normal file
99
src/main/java/org/redkale/asm/AsmMethodBean.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.redkale.asm;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.redkale.convert.json.JsonConvert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存放方法的字节信息
|
||||||
|
*
|
||||||
|
* @since 2.8.0
|
||||||
|
*/
|
||||||
|
public class AsmMethodBean {
|
||||||
|
|
||||||
|
private List<String> fieldNames;
|
||||||
|
|
||||||
|
private int access;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String desc;
|
||||||
|
|
||||||
|
private String signature;
|
||||||
|
|
||||||
|
private String[] exceptions;
|
||||||
|
|
||||||
|
public AsmMethodBean() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsmMethodBean(int access, String name, String desc, String signature, String[] exceptions) {
|
||||||
|
this.access = access;
|
||||||
|
this.name = name;
|
||||||
|
this.desc = desc;
|
||||||
|
this.signature = signature;
|
||||||
|
this.exceptions = exceptions;
|
||||||
|
this.fieldNames = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeEmptyNames() {
|
||||||
|
if (fieldNames != null) {
|
||||||
|
while (fieldNames.remove(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getFieldNames() {
|
||||||
|
return fieldNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFieldNames(List<String> fieldNames) {
|
||||||
|
this.fieldNames = fieldNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAccess() {
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccess(int access) {
|
||||||
|
this.access = access;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDesc() {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDesc(String desc) {
|
||||||
|
this.desc = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignature(String signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getExceptions() {
|
||||||
|
return exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExceptions(String[] exceptions) {
|
||||||
|
this.exceptions = exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JsonConvert.root().convertTo(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.redkale.asm;
|
package org.redkale.asm;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import org.redkale.annotation.Nullable;
|
import org.redkale.annotation.Nullable;
|
||||||
|
import org.redkale.util.Utility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生产动态字节码的方法扩展器, 可以进行方法加强动作
|
* 生产动态字节码的方法扩展器, 可以进行方法加强动作
|
||||||
@@ -24,6 +29,20 @@ public interface AsmMethodBoost<T> {
|
|||||||
return new AsmMethodBoosts(items);
|
return new AsmMethodBoosts(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* @param clazz
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Map<String, AsmMethodBean> getMethodBean(Class clazz) {
|
||||||
|
Map<String, AsmMethodBean> rs = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), clazz);
|
||||||
|
rs.values().forEach(AsmMethodBean::removeEmptyNames);
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对方法进行动态加强处理
|
* 对方法进行动态加强处理
|
||||||
*
|
*
|
||||||
@@ -101,4 +120,63 @@ public interface AsmMethodBoost<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class MethodParamClassVisitor extends ClassVisitor {
|
||||||
|
|
||||||
|
private final Map<String, AsmMethodBean> fieldMap;
|
||||||
|
|
||||||
|
public MethodParamClassVisitor(int api, final Map<String, AsmMethodBean> fieldmap) {
|
||||||
|
super(api);
|
||||||
|
this.fieldMap = fieldmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||||
|
if (java.lang.reflect.Modifier.isStatic(access)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String key = name + ":" + desc;
|
||||||
|
if (fieldMap.containsKey(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AsmMethodBean bean = new AsmMethodBean(access, name, desc, signature, exceptions);
|
||||||
|
List<String> fieldNames = bean.getFieldNames();
|
||||||
|
fieldMap.put(key, bean);
|
||||||
|
return new MethodVisitor(Opcodes.ASM6) {
|
||||||
|
@Override
|
||||||
|
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
|
||||||
|
if (index < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int size = fieldNames.size();
|
||||||
|
//index并不会按顺序执行的
|
||||||
|
if (index > size) {
|
||||||
|
for (int i = size; i < index; i++) {
|
||||||
|
fieldNames.add(" ");
|
||||||
|
}
|
||||||
|
fieldNames.set(index - 1, name);
|
||||||
|
}
|
||||||
|
fieldNames.set(index - 1, name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多
|
||||||
|
static Map<String, AsmMethodBean> getMethodParamNames(Map<String, AsmMethodBean> map, Class clazz) {
|
||||||
|
String n = clazz.getName();
|
||||||
|
InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class");
|
||||||
|
if (in == null) {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM6, map), 0);
|
||||||
|
} catch (Exception e) { //无需理会
|
||||||
|
}
|
||||||
|
Class superClass = clazz.getSuperclass();
|
||||||
|
if (superClass == Object.class) {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
return getMethodParamNames(map, superClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,15 @@ package org.redkale.cache.spi;
|
|||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import org.redkale.asm.AnnotationVisitor;
|
import org.redkale.asm.AnnotationVisitor;
|
||||||
|
import org.redkale.asm.AsmMethodBean;
|
||||||
import org.redkale.asm.AsmMethodBoost;
|
import org.redkale.asm.AsmMethodBoost;
|
||||||
import org.redkale.asm.Asms;
|
import org.redkale.asm.Asms;
|
||||||
import org.redkale.asm.ClassWriter;
|
import org.redkale.asm.ClassWriter;
|
||||||
|
import org.redkale.asm.Label;
|
||||||
import org.redkale.asm.MethodDebugVisitor;
|
import org.redkale.asm.MethodDebugVisitor;
|
||||||
import static org.redkale.asm.Opcodes.ACC_PRIVATE;
|
import static org.redkale.asm.Opcodes.ACC_PRIVATE;
|
||||||
import static org.redkale.asm.Opcodes.ACC_PROTECTED;
|
import static org.redkale.asm.Opcodes.ACC_PROTECTED;
|
||||||
@@ -63,22 +68,35 @@ public class CacheAsmMethodBoost implements AsmMethodBoost {
|
|||||||
acc = ACC_PRIVATE;
|
acc = ACC_PRIVATE;
|
||||||
nowMethodName = newMethodName;
|
nowMethodName = newMethodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String rsMethodName = method.getName() + "_cache";
|
final String rsMethodName = method.getName() + "_cache";
|
||||||
{
|
{
|
||||||
|
Map<String, AsmMethodBean> methodBeans = AsmMethodBoost.getMethodBean(serviceType);
|
||||||
|
String desc = Type.getMethodDescriptor(method);
|
||||||
|
AsmMethodBean methodBean = methodBeans.get(method.getName() + ":" + desc);
|
||||||
|
|
||||||
final String cacheDynDesc = Type.getDescriptor(CacheDyn.class);
|
final String cacheDynDesc = Type.getDescriptor(CacheDyn.class);
|
||||||
MethodDebugVisitor mv;
|
MethodDebugVisitor mv;
|
||||||
AnnotationVisitor av;
|
AnnotationVisitor av;
|
||||||
String[] exps = null;
|
String signature = null;
|
||||||
Class<?>[] expTypes = method.getExceptionTypes();
|
String[] exceptions = null;
|
||||||
if (expTypes.length > 0) {
|
if (methodBean == null) {
|
||||||
exps = new String[expTypes.length];
|
Class<?>[] expTypes = method.getExceptionTypes();
|
||||||
for (int i = 0; i < expTypes.length; i++) {
|
if (expTypes.length > 0) {
|
||||||
exps[i] = expTypes[i].getName().replace('.', '/');
|
exceptions = new String[expTypes.length];
|
||||||
|
for (int i = 0; i < expTypes.length; i++) {
|
||||||
|
exceptions[i] = expTypes[i].getName().replace('.', '/');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
signature = methodBean.getSignature();
|
||||||
|
exceptions = methodBean.getExceptions();
|
||||||
}
|
}
|
||||||
//需要定义一个新方法调用 this.rsMethodName
|
//需要定义一个新方法调用 this.rsMethodName
|
||||||
mv = new MethodDebugVisitor(cw.visitMethod(acc, nowMethodName, Type.getMethodDescriptor(method), null, exps));
|
mv = new MethodDebugVisitor(cw.visitMethod(acc, nowMethodName, Type.getMethodDescriptor(method), signature, exceptions));
|
||||||
//mv.setDebug(true);
|
//mv.setDebug(true);
|
||||||
|
Label l0 = new Label();
|
||||||
|
mv.visitLabel(l0);
|
||||||
{
|
{
|
||||||
av = mv.visitAnnotation(cacheDynDesc, true);
|
av = mv.visitAnnotation(cacheDynDesc, true);
|
||||||
av.visitEnd();
|
av.visitEnd();
|
||||||
@@ -100,6 +118,7 @@ public class CacheAsmMethodBoost implements AsmMethodBoost {
|
|||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
//传参数
|
//传参数
|
||||||
Class[] paramTypes = method.getParameterTypes();
|
Class[] paramTypes = method.getParameterTypes();
|
||||||
|
List<Integer> insns = new ArrayList<>();
|
||||||
int insn = 0;
|
int insn = 0;
|
||||||
for (Class pt : paramTypes) {
|
for (Class pt : paramTypes) {
|
||||||
insn++;
|
insn++;
|
||||||
@@ -116,6 +135,7 @@ public class CacheAsmMethodBoost implements AsmMethodBoost {
|
|||||||
} else {
|
} else {
|
||||||
mv.visitVarInsn(ALOAD, insn);
|
mv.visitVarInsn(ALOAD, insn);
|
||||||
}
|
}
|
||||||
|
insns.add(insn);
|
||||||
}
|
}
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, newDynName, rsMethodName, Type.getMethodDescriptor(method), false);
|
mv.visitMethodInsn(INVOKESPECIAL, newDynName, rsMethodName, Type.getMethodDescriptor(method), false);
|
||||||
if (method.getGenericReturnType() == void.class) {
|
if (method.getGenericReturnType() == void.class) {
|
||||||
@@ -136,6 +156,15 @@ public class CacheAsmMethodBoost implements AsmMethodBoost {
|
|||||||
mv.visitInsn(ARETURN);
|
mv.visitInsn(ARETURN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (methodBean != null && paramTypes.length > 0) {
|
||||||
|
Label l2 = new Label();
|
||||||
|
mv.visitLabel(l2);
|
||||||
|
//mv.visitLocalVariable("this", thisClassDesc, null, l0, l2, 0);
|
||||||
|
List<String> fieldNames = methodBean.getFieldNames();
|
||||||
|
for (int i = 0; i < paramTypes.length; i++) {
|
||||||
|
mv.visitLocalVariable(fieldNames.get(i), Type.getDescriptor(paramTypes[i]), null, l0, l2, insns.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
mv.visitMaxs(20, 20);
|
mv.visitMaxs(20, 20);
|
||||||
mv.visitEnd();
|
mv.visitEnd();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -560,6 +560,7 @@ public abstract class Sncp {
|
|||||||
Class clazz = serviceImplClass;
|
Class clazz = serviceImplClass;
|
||||||
Set<String> methodKeys = new HashSet<>();
|
Set<String> methodKeys = new HashSet<>();
|
||||||
do {
|
do {
|
||||||
|
Map<String, AsmMethodBean> methodBeans = AsmMethodBoost.getMethodBean(clazz);
|
||||||
for (final Method method : clazz.getDeclaredMethods()) {
|
for (final Method method : clazz.getDeclaredMethods()) {
|
||||||
String mk = Utility.methodKey(method);
|
String mk = Utility.methodKey(method);
|
||||||
if (methodKeys.contains(mk)) {
|
if (methodKeys.contains(mk)) {
|
||||||
@@ -569,16 +570,27 @@ public abstract class Sncp {
|
|||||||
methodKeys.add(mk);
|
methodKeys.add(mk);
|
||||||
String newMethodName = methodBoost.doMethod(cw, newDynName, FIELDPREFIX, method, null);
|
String newMethodName = methodBoost.doMethod(cw, newDynName, FIELDPREFIX, method, null);
|
||||||
if (newMethodName != null) {
|
if (newMethodName != null) {
|
||||||
String[] exps = null;
|
String desc = Type.getMethodDescriptor(method);
|
||||||
Class<?>[] expTypes = method.getExceptionTypes();
|
AsmMethodBean methodBean = methodBeans.get(method.getName() + ":" + desc);
|
||||||
if (expTypes.length > 0) {
|
String signature = null;
|
||||||
exps = new String[expTypes.length];
|
String[] exceptions = null;
|
||||||
for (int i = 0; i < expTypes.length; i++) {
|
if (methodBean == null) {
|
||||||
exps[i] = expTypes[i].getName().replace('.', '/');
|
Class<?>[] expTypes = method.getExceptionTypes();
|
||||||
|
if (expTypes.length > 0) {
|
||||||
|
exceptions = new String[expTypes.length];
|
||||||
|
for (int i = 0; i < expTypes.length; i++) {
|
||||||
|
exceptions[i] = expTypes[i].getName().replace('.', '/');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
signature = methodBean.getSignature();
|
||||||
|
exceptions = methodBean.getExceptions();
|
||||||
}
|
}
|
||||||
|
//注意: 新方法会丢失原方法中的泛型信息signature
|
||||||
//需要定义一个新方法调用 super.method
|
//需要定义一个新方法调用 super.method
|
||||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PRIVATE, newMethodName, Type.getMethodDescriptor(method), null, exps));
|
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PRIVATE, newMethodName, desc, signature, exceptions));
|
||||||
|
Label l0 = new Label();
|
||||||
|
mv.visitLabel(l0);
|
||||||
//mv.setDebug(true);
|
//mv.setDebug(true);
|
||||||
{ //给参数加上原有的Annotation
|
{ //给参数加上原有的Annotation
|
||||||
final Annotation[][] anns = method.getParameterAnnotations();
|
final Annotation[][] anns = method.getParameterAnnotations();
|
||||||
@@ -591,6 +603,7 @@ public abstract class Sncp {
|
|||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
//传参数
|
//传参数
|
||||||
Class[] paramTypes = method.getParameterTypes();
|
Class[] paramTypes = method.getParameterTypes();
|
||||||
|
List<Integer> insns = new ArrayList<>();
|
||||||
int insn = 0;
|
int insn = 0;
|
||||||
for (Class pt : paramTypes) {
|
for (Class pt : paramTypes) {
|
||||||
insn++;
|
insn++;
|
||||||
@@ -607,6 +620,7 @@ public abstract class Sncp {
|
|||||||
} else {
|
} else {
|
||||||
mv.visitVarInsn(ALOAD, insn);
|
mv.visitVarInsn(ALOAD, insn);
|
||||||
}
|
}
|
||||||
|
insns.add(insn);
|
||||||
}
|
}
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, supDynName, method.getName(), Type.getMethodDescriptor(method), false);
|
mv.visitMethodInsn(INVOKESPECIAL, supDynName, method.getName(), Type.getMethodDescriptor(method), false);
|
||||||
if (method.getGenericReturnType() == void.class) {
|
if (method.getGenericReturnType() == void.class) {
|
||||||
@@ -627,6 +641,15 @@ public abstract class Sncp {
|
|||||||
mv.visitInsn(ARETURN);
|
mv.visitInsn(ARETURN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (methodBean != null && paramTypes.length > 0) {
|
||||||
|
Label l2 = new Label();
|
||||||
|
mv.visitLabel(l2);
|
||||||
|
//mv.visitLocalVariable("this", thisClassDesc, null, l0, l2, 0);
|
||||||
|
List<String> fieldNames = methodBean.getFieldNames();
|
||||||
|
for (int i = 0; i < paramTypes.length; i++) {
|
||||||
|
mv.visitLocalVariable(fieldNames.get(i), Type.getDescriptor(paramTypes[i]), null, l0, l2, insns.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
mv.visitMaxs(20, 20);
|
mv.visitMaxs(20, 20);
|
||||||
mv.visitEnd();
|
mv.visitEnd();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user