Asm实现静态AOP的两种方式-在进入方法和限出方法时注入代码实现aop代码增强

 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(classVisitorClassReader.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);
    
}

 


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值