使用ASM动态生成类的拦截代理类

简介: 使用ASM动态生成类的拦截代理类

使用ASM实现AOP功能,根据现有的CLASS类文件,动态生成它的代理类文件,并在所有的方法前后添加拦截处理,具体如下:

  • 生成被代理类的子类,并重写所有方法(除java.lang.Object类定义的方法和main),增加before和after拦截。
  • 重新定义被代理类的所有属性(不包括属性的赋值及父类属性)。

jdk自带的代理(InvocationHandler)的不足:

  • jdk只能代理接口(被代理的类必须实现至少一个接口)。
  • jdk提供的代理方法,不能拦截同一类中方法的调用。如A.method1(){this.method2();}
  • 此时method2将不会被拦截。

下面是简单的被代理类及父类(也可以不要父类):

public class Service{
  public String name;
  public void insert(Object o) {}
}
public class RoleService extends Service{
  public String roleName;
  public String executeOuter(Integer name) {
    System.out.println("executeOuter call super.query()");
    return query();
  }
  public String query() {
    System.out.println("execute (RoleService): query");
    return "query result (RoleService)";
  }
}

核心类ClassAdapter用于生成一个类的子类,并为所有代理类方法添加拦截方法,如下:

public class ClassAdapter extends ClassVisitor implements Opcodes{
  public static final String INIT = "<init>";
  private ClassWriter classWriter;
  private String originalClassName;
  private String enhancedClassName;
  private Class<?> originalClass;
 
  public ClassAdapter(String enhancedClassName,
      Class<?> targetClass, ClassWriter writer) {
    super(Opcodes.ASM4,writer);
    this.classWriter = writer;
    this.originalClassName = targetClass.getName();
    this.enhancedClassName = enhancedClassName;
    this.originalClass = targetClass;
  }
 
  @Override
  public void visit(int version, int access, String name, String signature,
      String superName, String[] interfaces) {
    cv.visit(version, Opcodes.ACC_PUBLIC,
        toAsmCls(enhancedClassName), signature, name,interfaces);
  }
 
  @Override
  public FieldVisitor visitField(int access, String name, String desc,
      String signature, Object value) {
    // 拷贝所有属性   可使用java反射给属性赋值(生成class后newInstance在赋值)
    return super.visitField(access, name, desc, signature, value);
  }
 
  @Override
  public MethodVisitor visitMethod(int access, String name, String desc,
      String signature, String[] exceptions) {
    // 删除所有方法
    return null;
  }
 
  private static String toAsmCls(String className) {
    return className.replace('.', '/');
  }
  
  private static void doBefore(MethodVisitor mWriter,String methodInfo) {
    mWriter.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");  
    mWriter.visitLdcInsn("before method : " + methodInfo);   
    mWriter.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");    
    // 或者直接调用静态方法
    // mWriter.visitLdcInsn(methodInfo);
    // mWriter.visitMethodInsn(INVOKESTATIC,toAsmCls(TxHandler.class.getName()),"before","(Ljava/lang/String;)V");
 
  }
  
  private static void doAfter(MethodVisitor mWriter,String methodInfo) {
    mWriter.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");  
    mWriter.visitLdcInsn("after method : " + methodInfo);   
    mWriter.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",   
          "(Ljava/lang/String;)V");  
  }
  
  public static boolean needOverride(Method m) {
    // object类本身的方法不做重写
    if (m.getDeclaringClass().getName().equals(Object.class.getName())) {
      return false;
    }
    // "main" 方法不做重写
    if (Modifier.isPublic(m.getModifiers())
        && Modifier.isStatic(m.getModifiers())
        && m.getReturnType().getName().equals("void")
        && m.getName().equals("main")) {
      return false;
    }
 
    return true;
  }
  
  @Override
  public void visitEnd() {
    // 如果originalClass定义了私有成员变量,那么直接在visitMethod中复制originalClass的<init>会报错。
    // 创建<init>并调用父类的<init>
    // 调用originalClassName的<init>方法,否则class不能实例化
    MethodVisitor mvInit = classWriter.visitMethod(ACC_PUBLIC, INIT, "()V", null, null); 
    // pushes the 'this' variable
    mvInit.visitVarInsn(ALOAD, 0);
    mvInit.visitMethodInsn(INVOKESPECIAL, toAsmCls(originalClassName), INIT, "()V");
    mvInit.visitInsn(RETURN);
    // this code uses a maximum of one stack element and one local variable
    mvInit.visitMaxs(0, 0);
    mvInit.visitEnd(); 
    
    // 获取所有方法,并重写(main方法 和 Object的方法除外)
    Method[] methods = originalClass.getMethods();
    for(Method m : methods) {
      if(!needOverride(m)) {
        continue;
      }
      // mt.toString() == ()Ljava/lang/String
      Type mt = Type.getType(m);
 
      // 生成打印信息
      StringBuilder methodInfo = new StringBuilder(originalClassName);
      methodInfo.append(".").append(m.getName());
      methodInfo.append("|");
      
      Class<?>[] paramTypes = m.getParameterTypes();
      for(Class<?> t : paramTypes) {
        methodInfo.append(t.getName()).append(",");
      }
      if(paramTypes.length > 0) {
        methodInfo.deleteCharAt(methodInfo.length() - 1);
      }
      
      // 方法 description
      MethodVisitor mWriter = classWriter.visitMethod(ACC_PUBLIC, m.getName(), mt.toString(), null, null); 
      // insert code here (before)
      doBefore(mWriter,methodInfo.toString());
      
      int i = 0;
      // 如果不是静态方法 load this对象
      if(!Modifier.isStatic(m.getModifiers())) {
        mWriter.visitVarInsn(ALOAD, i++);
      }
      //StringBuilder sb = new StringBuilder(m.getName());
      // load 出方法的所有参数
      for(Class<?> tCls : m.getParameterTypes()) {
        Type t = Type.getType(tCls);
        //sb.append(loadCode(t)).append(",");
        mWriter.visitVarInsn(loadCode(t), i++); 
        // long和double 用64位表示,要后移一个位置,否则会报错
        if(t.getSort() == Type.LONG || t.getSort() == Type.DOUBLE) {
          i++;
        }
      }
      
      // 方法所属类全名
      String declaringCls = toAsmCls(m.getDeclaringClass().getName());
      // super.xxx();
      mWriter.visitMethodInsn(INVOKESPECIAL,declaringCls,m.getName(),mt.toString());
      
      // 处理返回值类型
      Type rt = Type.getReturnType(m);
      // 没有返回值
      if(rt.toString().equals("V")) {
        doAfter(mWriter,methodInfo.toString());
        mWriter.visitInsn(RETURN);
      }
      // 把return xxx() 转变成 : Object o = xxx(); return o;
      // store/load 存储并载入变量
      else {
        int storeCode = storeCode(rt);
        int loadCode = loadCode(rt);
        int returnCode = rtCode(rt);
        
        mWriter.visitVarInsn(storeCode, i);
        doAfter(mWriter,methodInfo.toString());
        mWriter.visitVarInsn(loadCode, i);
        mWriter.visitInsn(returnCode);
      }
      
          mWriter.visitMaxs(i, ++i);
          mWriter.visitEnd(); 
    }
    cv.visitEnd();
  }
  
  public static int storeCode(Type type) {
    int sort = type.getSort();
    switch (sort) {
    case Type.ARRAY:
      sort = ASTORE;
      break;
    case Type.BOOLEAN:
      sort = ISTORE;
      break;
    case Type.BYTE:
      sort = ISTORE;
      break;
    case Type.CHAR:
      sort = ISTORE;
      break;
    case Type.DOUBLE:
      sort = DSTORE;
      break;
    case Type.FLOAT:
      sort = FSTORE;
      break;
    case Type.INT:
      sort = ISTORE;
      break;
    case Type.LONG:
      sort = LSTORE;
      break;
    case Type.OBJECT:
      sort = ASTORE;
      break;
    case Type.SHORT:
      sort = ISTORE;
      break;
    default:
      break;
    }
    return sort;
  }
  
  public static int loadCode(Type type) {
    int sort = type.getSort();
    switch (sort) {
    case Type.ARRAY:
      sort = ALOAD;
      break;
    case Type.BOOLEAN:
      sort = ILOAD;
      break;
    case Type.BYTE:
      sort = ILOAD;
      break;
    case Type.CHAR:
      sort = ILOAD;
      break;
    case Type.DOUBLE:
      sort = DLOAD;
      break;
    case Type.FLOAT:
      sort = FLOAD;
      break;
    case Type.INT:
      sort = ILOAD;
      break;
    case Type.LONG:
      sort = LLOAD;
      break;
    case Type.OBJECT:
      sort = ALOAD;
      break;
    case Type.SHORT:
      sort = ILOAD;
      break;
    default:
      break;
    }
    return sort;
  }
  
  public static int rtCode(Type type) {
    int sort = type.getSort();
    switch (sort) {
    case Type.ARRAY:
      sort = ARETURN;
      break;
    case Type.BOOLEAN:
      sort = IRETURN;
      break;
    case Type.BYTE:
      sort = IRETURN;
      break;
    case Type.CHAR:
      sort = IRETURN;
      break;
    case Type.DOUBLE:
      sort = DRETURN;
      break;
    case Type.FLOAT:
      sort = FRETURN;
      break;
    case Type.INT:
      sort = IRETURN;
      break;
    case Type.LONG:
      sort = LRETURN;
      break;
    case Type.OBJECT:
      sort = ARETURN;
      break;
    case Type.SHORT:
      sort = IRETURN;
      break;
    default:
      break;
    }
    return sort;
  }
}

具体调用类如下:

public class AsmFactory {
  public static final String SUFIX = "$EnhancedByCc";
  public static BytecodeLoader classLoader = new BytecodeLoader();
 
  /**
   * 根据字节码加载class
   */
  public static class BytecodeLoader extends ClassLoader {
    public Class<?> defineClass(String className, byte[] byteCodes) {
      return super.defineClass(className, byteCodes, 0, byteCodes.length);
    }
  }
 
  /**
   * 返回代理类
   */
  @SuppressWarnings("unchecked")
  protected static <T> Class<T> getEnhancedClass(Class<T> clazz){
    String enhancedClassName = clazz.getName() + SUFIX;
    try {
      return (Class<T>) classLoader.loadClass(enhancedClassName);
    } catch (ClassNotFoundException classNotFoundException) {
      ClassReader reader = null;
      try {
        reader = new ClassReader(clazz.getName());
      } catch (IOException ioexception) {
        throw new RuntimeException(ioexception);
      }
      ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
      ClassVisitor visitor = new ClassAdapter(enhancedClassName, clazz,
          writer);
      reader.accept(visitor, 0);
      byte[] byteCodes = writer.toByteArray();
      writeClazz(enhancedClassName, byteCodes);
      Class<T> result = (Class<T>) classLoader.defineClass(
          enhancedClassName, byteCodes);
      return result;
    }
  }
  
  public static void writeClazz(String name, byte[] data) {
    try {
      File file = new File("D:/com/" + name + ".class");
      FileOutputStream fout = new FileOutputStream(file);
 
      fout.write(data);
      fout.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  
  public static void main(String[] args) throws InstantiationException, IllegalAccessException {
    Class<RoleService> rsCls = getEnhancedClass(RoleService.class);
    rsCls.newInstance().executeOuter(1);
  }
}


相关文章
|
4月前
|
SQL Java
访问者模式问题之在ASM中,实现一个访问者来删除指定的类属性,如何解决
访问者模式问题之在ASM中,实现一个访问者来删除指定的类属性,如何解决
|
6月前
|
Java API
使用ASM为一个类增加属性工具类
使用ASM为一个类增加属性工具类
56 0
|
6月前
|
Java 数据库连接 mybatis
使用ASM动态创建接口实现类
使用ASM动态创建接口实现类
48 0
|
6月前
|
Java API Android开发
ASM 框架:字节码操作的常见用法(生成类,修改类,方法插桩,方法注入)
ASM 框架:字节码操作的常见用法(生成类,修改类,方法插桩,方法注入)
103 0
|
Java
[ASM教程]#1分析类
ASM是一种通用Java字节码操作和分析框架,它可以用于修改现有的class文件或动态生成class文件。
139 0
[ASM教程]#3增加移除类成员
可以委托一个ClassWriter。
76 0
[ASM教程]#2生成类
使用classWriter生成类
71 0
|
Kubernetes 监控 安全
What this!理清服务网格中Sidecar代理的流量拦截配置
作为业内首个全托管Istio兼容的阿里云服务网格产品ASM,一开始从架构上就保持了与社区、业界趋势的一致性,控制平面的组件托管在阿里云侧,与数据面侧的用户集群独立。ASM产品是基于社区Istio定制实现的,在托管的控制面侧提供了用于支撑精细化的流量管理和安全管理的组件能力。通过托管模式,解耦了Istio组件与所管理的K8s集群的生命周期管理,使得架构更加灵活,提升了系统的可伸缩性。从2022年4月
What this!理清服务网格中Sidecar代理的流量拦截配置
|
网络协议 数据可视化 NoSQL
【服务网格架构】Envoy架构概览(8):统计,运行时配置,追踪和TCP代理
【服务网格架构】Envoy架构概览(8):统计,运行时配置,追踪和TCP代理
|
Java
ASM 库的 classVisitor 类解析
ASM 库的 classVisitor 类解析
466 0