欢迎光临散文网 会员登陆 & 注册

使用JAVA ASM 框架 实现一个简单AOP功能

2022-01-01 20:14 作者:独特的程序  | 我要投稿

 本文是一个介绍通过java ASM框架实现一个AOP环绕通知的简单实现。

一、简单介绍一下ASM框架

    ASM框架是个用来操作java字节码与创建字节码的工具,可以通过框架提供的API生成class文件,现在比较流行的框架底层基本都使用了ASM框架来动态编辑字节码。

二、简单介绍AOP

        AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的。

三、实现一个AOP环绕通知

        我们先定义一个AspectHandle类来实现记录被切入的方法的一些信息。

         类的定义如下:

          

public class AspectHandle {
  //记录被切入的对象实例
  private Object instance;
  //记录被切入的方法实参
  private Object[] arguments;
  //记录被切入的方法
  private Method method; 
  //调用实际的方法
  public Object invoke() {
    try {
      return this.method.invoke(this.instance, this.arguments);
    } catch (Throwable _e) {
      throw Exceptions.sneakyThrow(_e);
    }
  }
  ...
}

            

我们定义完AspectHandle类后接下来,我们看一下如何通过ASM框架实现AOP功能,我们先实现一个用来创建字节码的工具,先生成类基本构造函数等

public class GenerateUtils {
    /**
     * 定义一个接口用来提供除构造函数外的字节码操作
     */
  public interface Asm {
    void code(final ClassVisitor visitor, final Class<?> superClass, final Class<?>[] interfaces);
  }

public byte[] gen(final String className, final Class<?> superClass, final Class<?>[] interfaces, final GenerateUtils.Asm asm) {
     /**
       * 创建一个ClassWriter的对象,用于拼接字节码
       * COMPUTE_MAXS 告诉ASM 自动计算栈的最大值以及最大数量的方法的本地变量。
       * COMPUTE_FRAMES 标识让ASM 自动计算方法的栈桢
       */
    final ClassWriter classWriter = new ClassWriter((ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES));
    PrintWriter pw = new PrintWriter(System.err);
    /**
     * 创建一个TraceClassVisitor对象,将java字节码以文本的方式展现出来,方便查看生成的字节码
     */
    final ClassVisitor classVisitor = new TraceClassVisitor(classWriter, pw);
   /**
    * 将 class[] 类的数据转换为 String[] 
    */
    final Vector<String> interfaceStr = new Vector<String>();
    if ((interfaces != null)) {
      final Consumer<Class<?>> _function = (Class<?> inter) -> {
        interfaceStr.add(inter.getName().replace(".", "/"));
      };
      IterableExtensions.<Class<?>>filterNull(((Iterable<Class<?>>)Conversions.doWrapArray(interfaces))).forEach(_function);
    }
       /**
         * 判断如果superClass 为 null 就设置一个默认值 Object 就是
         * 目的是创建的子类如果没有父类 就默认继承Object类
         */
    Class<?> _xifexpression = null;
    if ((superClass == null)) {
      _xifexpression = Object.class;
    } else {
      _xifexpression = superClass;
    }
    final Class<?> superZlass = _xifexpression;
    //开始创建一个类,创建的类版本为 jdk1.8,类的作用域为public
    classVisitor.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className, null, superZlass.getTypeName().replace(".", "/"), ((String[])Conversions.unwrapArray(interfaceStr, String.class)));
       /**
         * 对继承的父类中的构建函数进行处理
         * 就是创建子类构造函数,并调用父类构造
         */
    final Consumer<Constructor<?>> _function_1 = (Constructor<?> c) -> {
      Method m = Method.getMethod(c);
      final GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, m, null, null, classVisitor);
      ga.loadThis();
      int _size = ((List<Parameter>)Conversions.doWrapArray(c.getParameters())).size();
      boolean _greaterThan = (_size > 0);
      if (_greaterThan) {
        int _size_1 = ((List<Parameter>)Conversions.doWrapArray(c.getParameters())).size();
        int _minus = (_size_1 - 1);
        ga.loadArgs(0, _minus);
      }
      ga.invokeConstructor(Type.getType(superZlass), m);
      ga.returnValue();
      ga.endMethod();
    };
    IterableExtensions.<Constructor<?>>filterNull(((Iterable<Constructor<?>>)Conversions.doWrapArray(superZlass.getConstructors()))).forEach(_function_1);
    //其他字节码操作
    if ((asm != null)) {
      asm.code(classVisitor, superZlass, interfaces);
    }
    //创建类结束
    classVisitor.visitEnd();
    //返回创建的字节码
    return classWriter.toByteArray();
  }
}

   创建字节的工具我们编写完了,接下来我们实现AspectUtils的类来实现我们的AOP的环绕通知功能

public class AspectUtils extends ClassLoader {
  private final HashMap<String, Class<?>> aspectClassMap = new HashMap<String, Class<?>>();
  
  private static final AspectUtils instance = new AspectUtils();
  
  private AspectUtils() {
  }
  
  public static synchronized AspectUtils getInstance() {
    return AspectUtils.instance;
  }
 /**
  * dest 要添加切面的类
  * aspect 切面类
  * methodName 指定切面类的方法
  */
public Class<?>  aspect(final Class<?> dest, final Class<?> aspect, final String methodName) {
     // 根据dest创建子类 
    GenerateUtils g = new GenerateUtils();
    //创建一个子类名
    String _replace = UUID.randomUUID().toString().replace("-", "");
    String uuid = ("FigAspect_" + _replace);
    /添加一个重写父类中的方法,并创建AspectHandle给 切面类
    final GenerateUtils.Asm _function = new GenerateUtils.Asm() {
      public void code(final ClassVisitor v, final Class<?> s, final Class<?>[] i) {
        final Consumer<Method> _function = new Consumer<Method>() {
          public void accept(final Method m) {
            try {
              final org.objectweb.asm.commons.Method method = org.objectweb.asm.commons.Method.getMethod(m);
              final GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, method, null, null, v);
               // 实例aspect
              ga.newInstance(Type.getType(aspect));
              ga.dup();
              //调用实例的构造方法
              ga.invokeConstructor(Type.getType(aspect), org.objectweb.asm.commons.Method.getMethod("void <init>()"));
              //将创建的切面类实例赋值给本地变量
              int l1 = ga.newLocal(Type.getType(aspect));
              ga.storeLocal(l1, Type.getType(aspect));
              // 创建AspectHandle
              ga.newInstance(Type.getType(AspectHandle.class));
              ga.dup();
              //调用实例的构造方法
              ga.invokeConstructor(Type.getType(AspectHandle.class), org.objectweb.asm.commons.Method.getMethod("void <init>()"));
              //将实例给本地变量 l2
              int l2 = ga.newLocal(Type.getType(AspectHandle.class));
              ga.storeLocal(l2, Type.getType(AspectHandle.class));
              //加载本地变量l2
              ga.loadLocal(l2, Type.getType(AspectHandle.class));
              //创建object数组,数组大小为 getArgumentTypes 的大小
              ga.push(((List<Type>)Conversions.doWrapArray(method.getArgumentTypes())).size());
              ga.newArray(Type.getType(Object.class));
              //构建实参,将创建的方法的参数值付给Object数组
              AspectUtils.this.buildArguments(method, ga);
              //调用AspectHandle 中的setArguments方法并传入Object数组
              ga.invokeVirtual(Type.getType(AspectHandle.class), org.objectweb.asm.commons.Method.getMethod("void setArguments(Object[])"));
              //加载本地变量l2
              ga.loadLocal(l2, Type.getType(AspectHandle.class));
              ga.push(Type.getType(dest));
              ga.push(method.getName());
              ga.push(((List<Type>)Conversions.doWrapArray(method.getArgumentTypes())).size());
              ga.newArray(Type.getType(Class.class));
              int _size = ((List<Type>)Conversions.doWrapArray(method.getArgumentTypes())).size();
              boolean _greaterThan = (_size > 0);
              if (_greaterThan) {
                for (int index = 0; (index < ((List<Type>)Conversions.doWrapArray(method.getArgumentTypes())).size()); index++) {
                  {
                    ga.dup();
                    ga.push(index);
                    ga.push(method.getArgumentTypes()[index]);
                    ga.visitInsn(Opcodes.AASTORE);
                  }
                }
              } else {
                ga.visitInsn(Opcodes.ACONST_NULL);
              }
              ga.invokeVirtual(Type.getType(Class.class), 
                org.objectweb.asm.commons.Method.getMethod("java/lang/reflect/Method getDeclaredMethod (java/lang/String,java/lang/Class[])", 
                  true));
              ga.invokeVirtual(Type.getType(AspectHandle.class), 
                org.objectweb.asm.commons.Method.getMethod("void setMethod(java/lang/reflect/Method)", true));
              ga.newInstance(Type.getType(dest));
              ga.dup();
              ga.invokeConstructor(Type.getType(dest), org.objectweb.asm.commons.Method.getMethod("void <init>()"));
              int l3 = ga.newLocal(Type.getType(dest));
              ga.storeLocal(l3, Type.getType(dest));
              ga.loadLocal(l2, Type.getType(AspectHandle.class));
              ga.loadLocal(l3, Type.getType(dest));
              ga.invokeVirtual(Type.getType(AspectHandle.class), org.objectweb.asm.commons.Method.getMethod("void setInstance(Object)"));
              ga.loadLocal(l1, Type.getType(aspect));
              ga.loadLocal(l2, Type.getType(dest));
              //调用切面类中的方法
              Method aspectMethodName = aspect.getDeclaredMethod(methodName, AspectHandle.class);
              ga.invokeVirtual(Type.getType(aspect), org.objectweb.asm.commons.Method.getMethod(aspectMethodName));
              //构建返回值类型
              AspectUtils.this.buildReturnValue(m, ga);
              ga.returnValue();
              ga.endMethod();
            } catch (Throwable _e) {
              throw Exceptions.sneakyThrow(_e);
            }
          }
        };
        ((List<Method>)Conversions.doWrapArray(dest.getDeclaredMethods())).forEach(_function);
      }
    };
    //生成子类
    byte[] newClassBytes = g.gen(uuid, dest, null, _function);
    return this.defineClass(uuid, newClassBytes, 0, newClassBytes.length);
  }
  
  /**
   * 构建返回值
   */
  protected void buildReturnValue(final Method m, final GeneratorAdapter ga) {
    Class<?> _returnType = m.getReturnType();
    boolean _matched = false;
    if (Objects.equal(_returnType, int.class)) {
      _matched=true;
      ga.checkCast(Type.getType(Integer.class));
      ga.invokeVirtual(Type.getType(Integer.class), org.objectweb.asm.commons.Method.getMethod("int intValue()"));
    }
    if (!_matched) {
      if (Objects.equal(_returnType, long.class)) {
        _matched=true;
        ga.checkCast(Type.getType(Long.class));
        ga.invokeVirtual(Type.getType(Long.class), org.objectweb.asm.commons.Method.getMethod("long longValue()"));
      }
    }
    if (!_matched) {
      if (Objects.equal(_returnType, double.class)) {
        _matched=true;
        ga.checkCast(Type.getType(Double.class));
        ga.invokeVirtual(Type.getType(Double.class), org.objectweb.asm.commons.Method.getMethod("double doubleValue()"));
      }
    }
    if (!_matched) {
      if (Objects.equal(_returnType, float.class)) {
        _matched=true;
        ga.checkCast(Type.getType(Float.class));
        ga.invokeVirtual(Type.getType(Float.class), org.objectweb.asm.commons.Method.getMethod("float floatValue()"));
      }
    }
    if (!_matched) {
      if (Objects.equal(_returnType, boolean.class)) {
        _matched=true;
        ga.checkCast(Type.getType(Boolean.class));
        ga.invokeVirtual(Type.getType(Boolean.class), org.objectweb.asm.commons.Method.getMethod("boolean booleanValue()"));
      }
    }
    if (!_matched) {
      if (Objects.equal(_returnType, short.class)) {
        _matched=true;
        ga.checkCast(Type.getType(Short.class));
        ga.invokeVirtual(Type.getType(Short.class), org.objectweb.asm.commons.Method.getMethod("short shortValue()"));
      }
    }
    if (!_matched) {
      if (Objects.equal(_returnType, char.class)) {
        _matched=true;
        ga.checkCast(Type.getType(Character.class));
        ga.invokeVirtual(Type.getType(Character.class), org.objectweb.asm.commons.Method.getMethod("char charValue()"));
      }
    }
    if (!_matched) {
      if (Objects.equal(_returnType, byte.class)) {
        _matched=true;
        ga.checkCast(Type.getType(Byte.class));
        ga.invokeVirtual(Type.getType(Byte.class), org.objectweb.asm.commons.Method.getMethod("byte byteValue()"));
      }
    }
    if (!_matched) {
      if (Objects.equal(_returnType, void.class)) {
        _matched=true;
      }
    }
    if (!_matched) {
      ga.checkCast(Type.getType(m.getReturnType()));
    }
  }
  
  /**
   * 构建AspectHandle的参数
   */
  protected void buildArguments(final org.objectweb.asm.commons.Method method, final GeneratorAdapter ga) {
    int _size = ((List<Type>)Conversions.doWrapArray(method.getArgumentTypes())).size();
    boolean _greaterThan = (_size > 0);
    if (_greaterThan) {
      for (int index = 0; (index < ((List<Type>)Conversions.doWrapArray(method.getArgumentTypes())).size()); index++) {
        {
          ga.dup();
          ga.push(index);
          String _descriptor = (method.getArgumentTypes()[index]).getDescriptor();
          boolean _matched = false;
          if (Objects.equal(_descriptor, "Z")) {
            _matched=true;
            ga.loadArg(index);
            ga.invokeStatic(Type.getType(Boolean.class), org.objectweb.asm.commons.Method.getMethod("Boolean valueOf(boolean)"));
          }
          if (!_matched) {
            if (Objects.equal(_descriptor, "C")) {
              _matched=true;
              ga.loadArg(index);
              ga.invokeStatic(Type.getType(Character.class), org.objectweb.asm.commons.Method.getMethod("Character valueOf(char)"));
            }
          }
          if (!_matched) {
            if (Objects.equal(_descriptor, "B")) {
              _matched=true;
              ga.loadArg(index);
              ga.invokeStatic(Type.getType(Byte.class), org.objectweb.asm.commons.Method.getMethod("Byte valueOf(byte)"));
            }
          }
          if (!_matched) {
            if (Objects.equal(_descriptor, "S")) {
              _matched=true;
              ga.loadArg(index);
              ga.invokeStatic(Type.getType(Short.class), org.objectweb.asm.commons.Method.getMethod("Short valueOf(short)"));
            }
          }
          if (!_matched) {
            if (Objects.equal(_descriptor, "I")) {
              _matched=true;
              ga.loadArg(index);
              ga.invokeStatic(Type.getType(Integer.class), org.objectweb.asm.commons.Method.getMethod("Integer valueOf(int)"));
            }
          }
          if (!_matched) {
            if (Objects.equal(_descriptor, "F")) {
              _matched=true;
              ga.loadArg(index);
              ga.invokeStatic(Type.getType(Float.class), org.objectweb.asm.commons.Method.getMethod("Float valueOf(float)"));
            }
          }
          if (!_matched) {
            if (Objects.equal(_descriptor, "J")) {
              _matched=true;
              ga.loadArg(index);
              ga.invokeStatic(Type.getType(Float.class), org.objectweb.asm.commons.Method.getMethod("Long valueOf(long)"));
            }
          }
          if (!_matched) {
            if (Objects.equal(_descriptor, "D")) {
              _matched=true;
              ga.loadArg(index);
              ga.invokeStatic(Type.getType(Float.class), org.objectweb.asm.commons.Method.getMethod("Double valueOf(double)"));
            }
          }
          if (!_matched) {
            ga.visitVarInsn(Opcodes.ALOAD, (index + 1));
          }
          ga.visitInsn(Opcodes.AASTORE);
        }
      }
    } else {
      ga.visitInsn(Opcodes.ACONST_NULL);
    }
  }
}

以上是我们实现的AOP功能的全部代码,接下来我写个小例子,验证一下我们

public class AspectTest2 {
	public static class Dest{
		public void print(String msg) {
			System.out.println("我是dest的print方法");
		}
	}
	
	public static class Aspect {
		public void handle(AspectHandle ah) {
			if(ah.getArguments()[0].equals("Hello World")) {
				System.out.println("我是Aspect的handle方法");
			}else {
				ah.invoke();
			}
		}
	}
	
	
	public static void main(String[] args) throws InstantiationException, IllegalAccessException {
		Class<?> cl = AspectUtils.getInstance().aspect(Dest.class, Aspect.class, "handle");
		Dest dest = (Dest)cl.newInstance();
		dest.print("Hello Dest");
	}
}

以上测试代码,输出的结果为 “ 我是dest的print方法 “ ,如果将Hello Dest 改为iHello World,输出的结果为 “ 我是Aspect的handle方法 “ 至此我们就使用ASM框架实现了一个AOP环绕通知的功能

使用JAVA ASM 框架 实现一个简单AOP功能的评论 (共 条)

分享到微博请遵守国家法律