使用JAVA ASM 框架 实现一个简单AOP功能
本文是一个介绍通过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环绕通知的功能