AOP常见的动态生成和静态生成
动态AOP是通过java动态代理或者字节码增强技术在runtime期间进行增强。
静态AOP在这儿定义为在应用启动完成之前,就通过字节码生成技术对代码进行增加。
缺点:动态AOP对原生不能aop ,且遇到USER user = new USER(),user.test()这种方法时无法对其test在不更改代码时进行拦截,静态AOP是可以这样做到。
通常,生成字节码技术的方式有两种,一种类于javaproxy产生的字节码样式(1)。一种是在进入方法和限出方法时注入代码实现aop代码增强(2)。
1.Asm实现静态AOP的两种方式-生成java-proxy类
2.本次实现在进入方法和限出方法时注入代码实现aop代码增强(相关依赖代码见 1)
public synchronized Throwable getCause() { ReturnWrapper var1 = GlobalAopContext.onMethodEnter(3, Throwable.class, this, new Object[0]); if(var1 != null) { return (Throwable)var1.returnObject; } else { Throwable var10000 = this.cause == this?null:this.cause; GlobalAopContext.onMethodExit(); return var10000; } }
字节码如下:
public synchronized getCause()Ljava/lang/Throwable; ICONST_3 LDC Ljava/lang/Throwable;.class ALOAD 0 ICONST_0 ANEWARRAY java/lang/Object INVOKESTATIC com/tmall/doom/tools/aop/common/GlobalAopContext.onMethodEnter (ILjava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Lcom/tmall/doom/tools/aop/common/ReturnWrapper; ASTORE 1 ALOAD 1 IFNULL L0 ALOAD 1 GETFIELD com/tmall/doom/tools/aop/common/ReturnWrapper.returnObject : Ljava/lang/Object; CHECKCAST java/lang/Throwable ARETURN L0 LINENUMBER 415 L0 ALOAD 0 GETFIELD java/lang/Throwable.cause : Ljava/lang/Throwable; ALOAD 0 IF_ACMPNE L1 ACONST_NULL GOTO L2 L1 FRAME APPEND [java/lang/Object] ALOAD 0 GETFIELD java/lang/Throwable.cause : Ljava/lang/Throwable; L2 FRAME SAME1 java/lang/Throwable INVOKESTATIC com/tmall/doom/tools/aop/common/GlobalAopContext.onMethodExit ()V ARETURN MAXSTACK = 4 MAXLOCALS = 2
接下来,我们来讲一下具体实现
GeneralAopInjector为增强入口类
public class GeneralAopInjector { public static void inject(InjectContext injectContext) throws Exception { checkArgs(injectContext); ClassLoader loader = injectContext.getClassLoader(); InputStream is = loader.getResourceAsStream(AsmClassUtil.forPath(injectContext.getFullClassName())); if (is == null) { throw new ClassNotFoundException(injectContext.getFullClassName()); } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); int b = -1; while ((b = is.read()) != -1) { outputStream.write(b); } byte[] classByte = outputStream.toByteArray(); byte[] transfer = buildInjectCode(injectContext, classByte); AsmClassUtil.reload(transfer, injectContext.getFullClassName(), loader); } private static void checkArgs(InjectContext injectContext) { if (injectContext.getInterceptor() == null || injectContext.getPatterns() == null || injectContext.getClassLoader() == null) { throw new IllegalArgumentException("injectContext is " + injectContext); } } public static byte[] buildInjectCode(InjectContext injectContext, byte[] classBytes) throws Exception { checkArgs(injectContext); List<MethodInfo> matchedMethods = AsmClassUtil.buildMatchedMethod(injectContext); if (matchedMethods.isEmpty()) { throw new IllegalStateException("enhanced method not found of " + injectContext); } out.println("[asm-aop]" + injectContext.getFullClassName() + "@" + matchedMethods); byte[] transfer = new CommonAopMaker().createInterceptorCodes(injectContext.getFullClassName() , matchedMethods, injectContext.getInterceptor(), injectContext.getClassLoader(), classBytes); String[] arr = injectContext.getFullClassName().split("\\."); String savedFileName = arr[arr.length - 1] + ".class"; CommonUtils.writeByteToFile(savedFileName, transfer); return transfer; } }
CommonAopMaker类承接过滤类
public class CommonAopMaker implements AopMaker { @Override public byte[] createInterceptorCodes( String targetClazz, List<MethodInfo> cepMethods, InvocationInterceptor interceptor, ClassLoader targetClassLoader, byte[] classToTransfer) throws Exception { try { return createInterceptorCodes(targetClazz, cepMethods, interceptor, targetClassLoader, classToTransfer, false); } catch (Exception e) { return createInterceptorCodes(targetClazz, cepMethods, interceptor, targetClassLoader, classToTransfer, true); } } public byte[] createInterceptorCodes( String targetClazz, List<MethodInfo> cepMethods, InvocationInterceptor interceptor, ClassLoader targetClassLoader, byte[] classToTransfer, boolean isUseComputeFrame) throws Exception { ByteArrayInputStream inputStream = new ByteArrayInputStream(classToTransfer); ClassReader cr = new ClassReader(inputStream); int flag = ClassWriter.COMPUTE_MAXS; if (isUseComputeFrame) { flag |= ClassWriter.COMPUTE_FRAMES; } ClassWriter classWriter = new EasyClassWriter(flag); ClassVisitor classVisitor = new AopClassAdaptor(classWriter, cepMethods, interceptor, targetClazz); cr.accept(classVisitor, ClassReader.EXPAND_FRAMES); return classWriter.toByteArray(); } }
真正的aop实现类AopClassAdaptor,这个是classVisitor
public class AopClassAdaptor extends ClassVisitor implements Opcodes { private Map<String, MethodInfo> methodInfoMap = new HashMap<String, MethodInfo>(); private InvocationInterceptor invocationInterceptor; private String targetClassName; public AopClassAdaptor(ClassVisitor classVisitor, List<MethodInfo> methodInfoList, InvocationInterceptor invocationInterceptor, String targetClassName) throws ClassNotFoundException { super(AsmAop.ASM_VERSION, classVisitor); for (MethodInfo methodInfo : methodInfoList) { methodInfoMap.put(methodInfo.getCode(), methodInfo); } this.invocationInterceptor = invocationInterceptor; this.targetClassName = targetClassName; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { //isInterface = (access & ACC_INTERFACE) != 0; //要49版本以上的class才支持ldc_w out.println("class version" + version + " 49@" + name); if (version < 49) { out.println("change class version from" + version + "to 49@" + name); version = 49; } if (Modifier.isInterface(access)) { throw new IllegalStateException("can't aop a interface:" + name); } super.visit(version, access, name, signature, superName, interfaces); } @Override public void visitAttribute(Attribute attribute) { super.visitAttribute(attribute); } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); String code = name + "_" + desc; MethodInfo methodInfo = methodInfoMap.get(code); if (methodInfo == null) { return mv; } CepMethod cepMethod = new CepMethod(methodInfo.getRelativeClass()); cepMethod.setName(methodInfo.getName()); cepMethod.setParamClassNames(methodInfo.getParamClassNames()); cepMethod.setReturnClassName(methodInfo.getReturnClassName()); cepMethod.setSubName(methodInfo.getSubMethodName()); cepMethod.setDescriptorNormal(methodInfo.getDescriptorNormal()); cepMethod.setDescriptorSrc(methodInfo.getDescriptorSrc()); MethodAopContext methodContext = GlobalAopContext.createMethodContext(targetClassName, cepMethod, invocationInterceptor); AopMethodAdaptor methodAdaptor = new AopMethodAdaptor(AsmAop.ASM_VERSION, mv, access, name, desc, methodContext); return methodAdaptor; } }
AopMethodAdaptor继承于原生的asm 的AdviceAdapter类,方法的AOP拦截可以在这个类实现
public class AopMethodAdaptor extends AdviceAdapter { private MethodAopContext methodAopContext; public static Method ENTER_METHOD = Method.getMethod("com.tmall.doom.tools.aop.common.ReturnWrapper onMethodEnter(int,Class,Object,Object[])"); public static Method EXIT_METHOD = Method.getMethod("void onMethodExit()"); protected AopMethodAdaptor(int api, MethodVisitor mv, int access, String name, String desc, MethodAopContext methodAopContext) { super(api, mv, access, name, desc); this.methodAopContext = methodAopContext; } @Override protected void onMethodEnter() { push(methodAopContext.index); visitLdcInsn(Type.getType(String.format("L%s;", methodAopContext.className.replace(".", "/")))); if ((methodAccess & ACC_STATIC) == 0) { loadThis(); } else { push((String) null); } loadArgArray(); //aop入口 invokeStatic(Type.getType(GlobalAopContext.class), ENTER_METHOD); //如果是首次调用则继续,否则直接放回上次调用结果 int local = newLocal(Type.getType(Object.class)); storeLocal(local); loadLocal(local); Label label = newLabel(); ifNull(label); CepMethod methodInfo = methodAopContext.methodInfo; if (!"void".equals(methodInfo.getReturnClassName())) { loadLocal(local); getField(Type.getType(ReturnWrapper.class), "returnObject", Type.getType(Object.class)); MethodInfo transformMethod = new MethodInfo(methodInfo.getDescriptorNormal(), methodInfo.getDescriptorSrc()); Type type = transformMethod.getAsmReturnType(); AsmClassUtil.convertAndReturn(mv, type); } else { returnValue(); } visitLabel(label); } @Override protected void onMethodExit(int opcode) { invokeStatic(Type.getType(GlobalAopContext.class), EXIT_METHOD); super.onMethodExit(opcode); } }
里面aop逻辑在GlobalAopContext类中。
public class GlobalAopContext { private static volatile ThreadLocal<Integer> methodVisitFlag = new ThreadLocal(); private static AtomicInteger currentIndex = new AtomicInteger(0); public static volatile ThreadLocal<Boolean> skipAopFlag = new ThreadLocal(); private static final int MAX_AOP_METHOD_COUNT = 100000; private static final Map<String, String> DESCRIPTORS = new HashMap(); private static MethodAopContext[] methodAopContext; public GlobalAopContext() { } public static ReturnWrapper onMethodEnter(int idx, Class<?> clazz, Object target, Object[] args) throws Throwable { if(skipAopFlag.get() != null && ((Boolean)skipAopFlag.get()).booleanValue()) { return null; } else { Integer visitFlag = (Integer)methodVisitFlag.get(); if(visitFlag == null) { visitFlag = Integer.valueOf(0); } if(visitFlag.intValue() % 2 == 0) { methodVisitFlag.set(visitFlag = Integer.valueOf(visitFlag.intValue() + 1)); MethodAopContext aopContext = methodAopContext[idx]; if(aopContext.method == null) { CepMethod ret = aopContext.methodInfo; ClassLoader rw = clazz.getClassLoader(); if(rw == null) { rw = ClassLoader.getSystemClassLoader(); } ret.init(rw); aopContext.method = clazz.getDeclaredMethod(ret.getName(), ret.getParamTypes()); } Object ret1 = aopContext.invocationInterceptor.invoke(target, aopContext.method, args); methodVisitFlag.set(Integer.valueOf(visitFlag.intValue() - 1)); ReturnWrapper rw1 = new ReturnWrapper(ret1); return rw1; } else { methodVisitFlag.set(Integer.valueOf(visitFlag.intValue() + 1)); return null; } } } public static void onMethodExit() { if(skipAopFlag.get() == null || !((Boolean)skipAopFlag.get()).booleanValue()) { Integer visitFlag = (Integer)methodVisitFlag.get(); methodVisitFlag.set(Integer.valueOf(visitFlag.intValue() - 1)); } } public static MethodAopContext createMethodContext(String className, CepMethod methodInfo, InvocationInterceptor interceptor) { MethodAopContext aopContext = new MethodAopContext(); aopContext.index = currentIndex.incrementAndGet(); if(aopContext.index >= 100000) { throw new RuntimeException("current intercept method count is over 100000"); } else { aopContext.invocationInterceptor = interceptor; aopContext.methodInfo = methodInfo; aopContext.className = className; methodAopContext[aopContext.index] = aopContext; return aopContext; } } public static Integer getVisitFlag() { return (Integer)methodVisitFlag.get(); } static { DESCRIPTORS.put("void", "V"); DESCRIPTORS.put("byte", "B"); DESCRIPTORS.put("char", "C"); DESCRIPTORS.put("double", "D"); DESCRIPTORS.put("float", "F"); DESCRIPTORS.put("int", "I"); DESCRIPTORS.put("long", "J"); DESCRIPTORS.put("short", "S"); DESCRIPTORS.put("boolean", "Z"); methodAopContext = new MethodAopContext[100000]; } }
到这儿AOP相关核心实现就完了。
用的时候直接在premain方法中就可以通过Instrumentation的API实现,这儿实现api中有两种方式。
l 用publicinterface ClassFileTransformer
l byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException; }
这个方法在类加载的时候会触发执行。
l 调用instrumentation.redefineClasses(definition)api执行,这个代码会在代码加载完成后重新替换,例如我们需要对exception增加,就可以像这样,就可以对原生的Throwable进行字节码增强。
public void redefineClass(Instrumentation instrumentation) throws Throwable { byte[] bytes = CommonUtils.loadBytes(ClassLoader.getSystemClassLoader(), Throwable.class.getName()); byte[] convertedBytes = buildExceptionInterceptBytes(bytes); ClassDefinition definition = new ClassDefinition(Throwable.class, convertedBytes); //对系统ClassLoader进行拦截,实现字节码定义及类查找核心能力 instrumentation.redefineClasses(definition); }