成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Grays Anatomy源碼淺析

verano / 791人閱讀

摘要:在里面,最大的改變使運(yùn)行時(shí)的成為可能。缺省情況下,將會(huì)在方法中計(jì)算這些,通過在加載這兩個(gè)類進(jìn)入虛擬機(jī)時(shí),使用反射來計(jì)算。通過重寫方法,更正獲取的方式,改成使用指定的方式進(jìn)行。

Grays Anatomy源碼淺析

標(biāo)簽(空格分隔):JAVA JVM 問題排查

在知乎上看到一個(gè)問題,被R大推薦了這個(gè)線上排查的工具,就下來用了用,感覺還不錯(cuò),知道是Java寫的后,就行看看源碼,相關(guān)知識(shí)比較欠缺,前后看了一個(gè)月左右,才知其大概原理,記錄下來分給大家.

讀源碼前,需要掌握幾點(diǎn)知識(shí),不然硬看是看不懂的.

Instrumentation

利用 Java 代碼,即 java.lang.instrument 做動(dòng)態(tài) Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能從本地代碼中解放出來,使之可以用 Java 代碼的方式解決問題。使用 Instrumentation,開發(fā)者可以構(gòu)建一個(gè)獨(dú)立于應(yīng)用程序的代理程序(Agent),用來監(jiān)測(cè)和協(xié)助運(yùn)行在 JVM 上的程序,甚至能夠替換和修改某些類的定義。有了這樣的功能,開發(fā)者就可以實(shí)現(xiàn)更為靈活的運(yùn)行時(shí)虛擬機(jī)監(jiān)控和 Java 類操作了,這樣的特性實(shí)際上提供了一種虛擬機(jī)級(jí)別支持的 AOP 實(shí)現(xiàn)方式,使得開發(fā)者無需對(duì) JDK 做任何升級(jí)和改動(dòng),就可以實(shí)現(xiàn)某些 AOP 的功能了。

實(shí)現(xiàn)方法:

1.實(shí)現(xiàn)ClassFileTransformer來完成AOP 2.編寫 premain 函數(shù)

編寫一個(gè) Java 類,包含如下兩個(gè)方法當(dāng)中的任何一個(gè),來指定哪些class需要做AOP

public static void premain(String agentArgs, Instrumentation inst); 
public static void premain(String agentArgs); 
3.jar 文件打包

將這個(gè) Java 類打包成一個(gè) jar 文件,并在其中的 manifest 屬性當(dāng)中加入” Premain-Class”來指定步驟 2 當(dāng)中編寫的那個(gè)帶有 premain 的 Java 類。(可能還需要指定其他屬性以開啟更多功能)

Manifest-Version: 1.0 
Premain-Class: Premain
4.運(yùn)行

用如下方式運(yùn)行帶有 Instrumentation 的 Java 程序:

java -javaagent:jar 文件的位置 [= 傳入 premain 的參數(shù) ]

在 Java SE 6 里面,instrumentation 包被賦予了更強(qiáng)大的功能:?jiǎn)?dòng)后的 instrument、本地代碼(native code)instrument,以及動(dòng)態(tài)改變 classpath 等等。這些改變,意味著 Java 具有了更強(qiáng)的動(dòng)態(tài)控制、解釋能力,它使得 Java 語(yǔ)言變得更加靈活多變。
在 Java SE6 里面,最大的改變使運(yùn)行時(shí)的 Instrumentation 成為可能。在 Java SE 5 中,Instrument 要求在運(yùn)行前利用命令行參數(shù)或者系統(tǒng)參數(shù)來設(shè)置代理類,在實(shí)際的運(yùn)行之中,虛擬機(jī)在初始化之時(shí)(在絕大多數(shù)的 Java 類庫(kù)被載入之前),instrumentation 的設(shè)置已經(jīng)啟動(dòng),并在虛擬機(jī)中設(shè)置了回調(diào)函數(shù),檢測(cè)特定類的加載情況,并完成實(shí)際工作。但是在實(shí)際的很多的情況下,我們沒有辦法在虛擬機(jī)啟動(dòng)之時(shí)就為其設(shè)定代理,這樣實(shí)際上限制了 instrument 的應(yīng)用。而 Java SE 6 的新特性改變了這種情況,通過 Java Tool API 中的 attach 方式,我們可以很方便地在運(yùn)行過程中動(dòng)態(tài)地設(shè)置加載代理類,以達(dá)到 instrumentation 的目的。
另外,對(duì) native 的 Instrumentation 也是 Java SE 6 的一個(gè)嶄新的功能,這使以前無法完成的功能 —— 對(duì) native 接口的 instrumentation 可以在 Java SE 6 中,通過一個(gè)或者一系列的 prefix 添加而得以完成。
最后,Java SE 6 里的 Instrumentation 也增加了動(dòng)態(tài)添加 class path 的功能。所有這些新的功能,都使得 instrument 包的功能更加豐富,從而使 Java 語(yǔ)言本身更加強(qiáng)大。

實(shí)現(xiàn)方法:

1.實(shí)現(xiàn)ClassFileTransformer來完成AOP 2.編寫 agentmain 函數(shù)

編寫一個(gè) Java 類,包含如下兩個(gè)方法當(dāng)中的任何一個(gè),來指定哪些class需要做AOP

public static void agentmain (String agentArgs, Instrumentation inst); 
public static void agentmain (String agentArgs);    
3.jar 文件打包

將這個(gè) Java 類打包成一個(gè) jar 文件,并在其中的 manifest 屬性當(dāng)中加入” Agent-Class”來指定步驟 2 當(dāng)中編寫的那個(gè)帶有 agentmain 的 Java 類。(可能還需要指定其他屬性以開啟更多功能)

Manifest-Version: 1.0 
Agent-Class: AgentMain
4.運(yùn)行

用如下方式運(yùn)行帶有 Instrumentation 的 Java 程序:

java -javaagent:jar 文件的位置 [= 傳入 premain 的參數(shù) ]

這里只是簡(jiǎn)要概述下,具體請(qǐng)點(diǎn)標(biāo)題鏈接

ASM

ASM是一種字節(jié)碼增強(qiáng)技術(shù),即通過修改字節(jié)碼來實(shí)現(xiàn)修改類的行為的功能.沒有找到講ASM比較好的博客,還是官方文檔明晰透徹還會(huì)穿插著講講JVM的方法調(diào)用模型,建議閱讀之,這里就舉個(gè)ASM的小例子來說明它的使用.

類C如下所示

public class C {
        public void m() throws Exception {
          Thread.sleep(100);
          }
}

通過ASM動(dòng)態(tài)修改其字節(jié)碼將其方法加上計(jì)算時(shí)間調(diào)用的功能

 public class C {
        public static long timer;
        public void m() throws Exception {
          timer -= System.currentTimeMillis();
          Thread.sleep(100);
          timer += System.currentTimeMillis();
} }

ASM方法編寫如下,詳情請(qǐng)見ASM官方文檔Core API,其實(shí)若不考慮性能影響的話,Tree API更符合Java程序員的思維

public class AddTimerAdapter extends ClassVisitor {
    private String owner;
    private boolean isInterface;
    public AddTimerAdapter(ClassVisitor cv) {
        super(ASM4, cv);
    }
    @Override public void visit(int version, int access, String name,
                                String signature, String superName, String[] interfaces) {
        cv.visit(version, access, name, signature, superName, interfaces);
        owner = name;
        isInterface = (access & ACC_INTERFACE) != 0;
    }
    @Override public MethodVisitor visitMethod(int access, String name,
                                               String desc, String signature, String[] exceptions) {
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
                exceptions);
        if (!isInterface && mv != null && !name.equals("")) {
            mv = new AddTimerMethodAdapter(mv);
        }
        return mv;
    }
    @Override public void visitEnd() {
        if (!isInterface) {
            FieldVisitor fv = cv.visitField(ACC_PUBLIC + ACC_STATIC, "timer",
                    "J", null, null);
            if (fv != null) {
                fv.visitEnd();
            }
        }
        cv.visitEnd();
    }

    public class AddTimerMethodAdapter extends MethodVisitor {
        public AddTimerMethodAdapter(org.objectweb.asm.MethodVisitor mv) {
            super(ASM4,mv);
        }
        @Override
        public void visitCode() {
            mv.visitCode();
            mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
                    "currentTimeMillis", "()J");
            mv.visitInsn(LSUB);
            mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
        }
        @Override public void visitInsn(int opcode) {
            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
                mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
                        "currentTimeMillis", "()J");
                mv.visitInsn(LADD);
                mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
            }
            mv.visitInsn(opcode);
        }
        @Override public void visitMaxs(int maxStack, int maxLocals) {
            mv.visitMaxs(maxStack + 4, maxLocals);
        }
    }

    public static void main(String[] args) throws IOException {
        ClassReader cr = new ClassReader("com.asm.temp.C");
        ClassWriter cw = new ClassWriter(cr,0);
        TraceClassVisitor classVisitor = new TraceClassVisitor(cw,new PrintWriter(System.out));
        AddTimerAdapter addTimerAdapter = new AddTimerAdapter(classVisitor);
        cr.accept(addTimerAdapter,0);
        System.out.println(cw.toByteArray());
    }

}

通過輸出的字節(jié)碼,可以看出其已加上了計(jì)算調(diào)用的功能

GETSTATIC C.timer : J
INVOKESTATIC java/lang/System.currentTimeMillis()J LSUB
PUTSTATIC C.timer : J
LDC 100
INVOKESTATIC java/lang/Thread.sleep(J)V
GETSTATIC C.timer : J
INVOKESTATIC java/lang/System.currentTimeMillis()J LADD
PUTSTATIC C.timer : J
RETURN
MAXSTACK = 4
MAXLOCALS = 1
Grays Anatomy

Greys要實(shí)現(xiàn)的功能,是動(dòng)態(tài)的監(jiān)測(cè)JVM方法的執(zhí)行.很自然的就想到了它會(huì)去實(shí)現(xiàn)Instrumentation的preMain和agentMain.AgentLauncher實(shí)現(xiàn)了preMain和agentMain方法,其都調(diào)用了main方法,其主要功能透過反射實(shí)例化一個(gè)GaServer.這個(gè)Server主要實(shí)現(xiàn)的就是接受命令,處理命令,返回響應(yīng).

public class AgentLauncher {


    public static void premain(String args, Instrumentation inst) {
        main(args, inst);
    }

    public static void agentmain(String args, Instrumentation inst) {
        main(args, inst);
    }
  private static synchronized void main(final String args, final Instrumentation inst) {
        try {

            // 傳遞的args參數(shù)分兩個(gè)部分:agentJar路徑和agentArgs
            // 分別是Agent的JAR包路徑和期望傳遞到服務(wù)端的參數(shù)
            final int index = args.indexOf(";");
            final String agentJar = args.substring(0, index);
            final String agentArgs = args.substring(index, args.length());

            // 將Spy添加到BootstrapClassLoader
            inst.appendToBootstrapClassLoaderSearch(
                    new JarFile(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile())
            );

            // 構(gòu)造自定義的類加載器,盡量減少Greys對(duì)現(xiàn)有工程的侵蝕
            final ClassLoader agentLoader = loadOrDefineClassLoader(agentJar);

            // Configure類定義
            final Class classOfConfigure = agentLoader.loadClass("com.github.ompc.greys.core.Configure");

            // GaServer類定義
            final Class classOfGaServer = agentLoader.loadClass("com.github.ompc.greys.core.server.GaServer");

            // 反序列化成Configure類實(shí)例
            final Object objectOfConfigure = classOfConfigure.getMethod("toConfigure", String.class)
                    .invoke(null, agentArgs);

            // JavaPid
            final int javaPid = (Integer) classOfConfigure.getMethod("getJavaPid").invoke(objectOfConfigure);

            // 獲取GaServer單例
            final Object objectOfGaServer = classOfGaServer
                    .getMethod("getInstance", int.class, Instrumentation.class)
                    .invoke(null, javaPid, inst);

            // gaServer.isBind()
            final boolean isBind = (Boolean) classOfGaServer.getMethod("isBind").invoke(objectOfGaServer);

            if (!isBind) {
                try {
                    classOfGaServer.getMethod("bind", classOfConfigure).invoke(objectOfGaServer, objectOfConfigure);
                } catch (Throwable t) {
                    classOfGaServer.getMethod("destroy").invoke(objectOfGaServer);
                    throw t;
                }

            }

        } catch (Throwable t) {
            t.printStackTrace();
        }

    }

GaServer的bind方法為啟動(dòng)服務(wù),啟動(dòng)后activeSelectorDaemon方法啟動(dòng)一個(gè)Daemon線程負(fù)責(zé)命令的處理,其中doRead是主要邏輯的實(shí)現(xiàn),其中又委托給了CommandHandler的executeCommand方法解析輸入行并執(zhí)行命令.

public class GaServer {
/**
     * 啟動(dòng)Greys服務(wù)端
     *
     * @param configure 配置信息
     * @throws IOException 服務(wù)器啟動(dòng)失敗
     */
    public void bind(Configure configure) throws IOException {
        if (!isBindRef.compareAndSet(false, true)) {
            throw new IllegalStateException("already bind");
        }

        try {

            serverSocketChannel = ServerSocketChannel.open();
            selector = Selector.open();

            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().setSoTimeout(configure.getConnectTimeout());
            serverSocketChannel.socket().setReuseAddress(true);
            serverSocketChannel.register(selector, OP_ACCEPT);

            // 服務(wù)器掛載端口
            serverSocketChannel.socket().bind(getInetSocketAddress(configure.getTargetIp(), configure.getTargetPort()), 24);
            logger.info("ga-server listening on network={};port={};timeout={};", configure.getTargetIp(),
                    configure.getTargetPort(),
                    configure.getConnectTimeout());

            activeSelectorDaemon(selector, configure);

        } catch (IOException e) {
            unbind();
            throw e;
        }

    }
    
    private void activeSelectorDaemon(final Selector selector, final Configure configure) {

        final ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);

        final Thread gaServerSelectorDaemon = new Thread("ga-selector-daemon") {
            @Override
            public void run() {

                while (!isInterrupted()
                        && isBind()) {

                    try {

                        while (selector.isOpen()
                                && selector.select() > 0) {
                            final Iterator it = selector.selectedKeys().iterator();
                            while (it.hasNext()) {
                                final SelectionKey key = it.next();
                                it.remove();

                                // do ssc accept
                                if (key.isValid() && key.isAcceptable()) {
                                    doAccept(key, selector, configure);
                                }

                                // do sc read
                                if (key.isValid() && key.isReadable()) {
                                    doRead(byteBuffer, key);
                                }

                            }
                        }

                    } catch (IOException e) {
                        logger.warn("selector failed.", e);
                    } catch (ClosedSelectorException e) {
                        logger.debug("selector closed.", e);
                    }


                }

            }
        };
        gaServerSelectorDaemon.setDaemon(true);
        gaServerSelectorDaemon.start();
    }


private void doRead(final ByteBuffer byteBuffer, SelectionKey key) {
        final GaAttachment attachment = (GaAttachment) key.attachment();
        final SocketChannel socketChannel = (SocketChannel) key.channel();
        final Session session = attachment.getSession();
        try {

            // 若讀到EOF,則說明SocketChannel已經(jīng)關(guān)閉
            if (EOF == socketChannel.read(byteBuffer)) {
                logger.info("client={}@session[{}] was closed.", socketChannel, session.getSessionId());
                // closeSocketChannel(key, socketChannel);
                session.destroy();
                if(session.isLocked()) {
                    session.unLock();
                }
                return;
            }

            // decode for line
            byteBuffer.flip();
            while (byteBuffer.hasRemaining()) {
                switch (attachment.getLineDecodeState()) {
                    case READ_CHAR: {
                        final byte data = byteBuffer.get();

                        if ("
" == data) {
                            attachment.setLineDecodeState(READ_EOL);
                        }

                        // 遇到中止命令(CTRL_D),則標(biāo)記會(huì)話為不可寫,讓后臺(tái)任務(wù)停下
                        else if (CTRL_D == data
                                || CTRL_X == data) {
                            session.unLock();
                            break;
                        }

                        // 普通byte則持續(xù)放入到緩存中
                        else {
                            if ("
" != data) {
                                attachment.put(data);
                            }
                            break;
                        }

                    }

                    case READ_EOL: {
                        final String line = attachment.clearAndGetLine(session.getCharset());

                        executorService.execute(new Runnable() {
                            @Override
                            public void run() {

                                // 會(huì)話只有未鎖定的時(shí)候才能響應(yīng)命令
                                if (session.tryLock()) {
                                    try {

                                        // 命令執(zhí)行
                                        commandHandler.executeCommand(line, session);

                                        // 命令結(jié)束之后需要傳輸EOT告訴client命令傳輸已經(jīng)完結(jié),可以展示提示符
                                        socketChannel.write(ByteBuffer.wrap(new byte[]{EOT}));

                                    } catch (IOException e) {
                                        logger.info("network communicate failed, session[{}] will be close.",
                                                session.getSessionId());
                                        session.destroy();
                                    } finally {
                                        session.unLock();
                                    }
                                } else {
                                    logger.info("session[{}] was locked, ignore this command.",
                                            session.getSessionId());
                                }
                            }
                        });

                        attachment.setLineDecodeState(READ_CHAR);
                        break;
                    }
                }
            }//while for line decode

            byteBuffer.clear();

        }

        // 處理
        catch (IOException e) {
            logger.warn("read/write data failed, session[{}] will be close.", session.getSessionId(), e);
            closeSocketChannel(key, socketChannel);
            session.destroy();
        }
    }
}

接下來看看CommandHandler接口的默認(rèn)實(shí)現(xiàn)DefaultCommandHandler,其executeCommand方法的主邏輯由excute實(shí)現(xiàn),我們最關(guān)心的類增強(qiáng)部分是通過EnhancerAffect實(shí)現(xiàn)的.

public class DefaultCommandHandler implements CommandHandler {

    @Override
    public void executeCommand(final String line, final Session session) throws IOException {
            final Command command = Commands.getInstance().newCommand(line);
            execute(session, command);
    }
    
     /*
     * 執(zhí)行命令
     */
    private void execute(final Session session, final Command command) throws GaExecuteException, IOException {
            

            // 需要做類增強(qiáng)的動(dòng)作
            else if (action instanceof GetEnhancerAction) {

                affect = new EnhancerAffect();

                // 執(zhí)行命令動(dòng)作 & 獲取增強(qiáng)器
                final Command.GetEnhancer getEnhancer = ((GetEnhancerAction) action).action(session, inst, printer);
                final int lock = session.getLock();
                final AdviceListener listener = getEnhancer.getAdviceListener();
                final EnhancerAffect enhancerAffect = Enhancer.enhance(
                        inst,
                        lock,
                        listener instanceof InvokeTraceable,
                        getEnhancer.getPointCut()
                );
            }
    }
}

其增強(qiáng)其是AdviceListener的實(shí)現(xiàn)類,AdviceListener由before,afterReturning等圍繞著方法執(zhí)行階段的方法組成,按道理這時(shí)候就該是ASM登場(chǎng),修改類行為的時(shí)刻了.

     /**
     * 前置通知
     *
     * @param loader     類加載器
     * @param className  類名
     * @param methodName 方法名
     * @param methodDesc 方法描述
     * @param target     目標(biāo)類實(shí)例
     *                   若目標(biāo)為靜態(tài)方法,則為null
     * @param args       參數(shù)列表
     * @throws Throwable 通知過程出錯(cuò)
     */
    void before(
            ClassLoader loader, String className, String methodName, String methodDesc,
            Object target, Object[] args) throws Throwable;

    /**
     * 返回通知
     *
     * @param loader       類加載器
     * @param className    類名
     * @param methodName   方法名
     * @param methodDesc   方法描述
     * @param target       目標(biāo)類實(shí)例
     *                     若目標(biāo)為靜態(tài)方法,則為null
     * @param args         參數(shù)列表
     * @param returnObject 返回結(jié)果
     *                     若為無返回值方法(void),則為null
     * @throws Throwable 通知過程出錯(cuò)
     */
    void afterReturning(
            ClassLoader loader, String className, String methodName, String methodDesc,
            Object target, Object[] args,
            Object returnObject) throws Throwable;              

接下來看起增強(qiáng)方法,可見真正的增強(qiáng)class是Enhancer

    public static synchronized EnhancerAffect enhance(
            final Instrumentation inst,
            final int adviceId,
            final boolean isTracing,
            final PointCut pointCut) throws UnmodifiableClassException {

        final EnhancerAffect affect = new EnhancerAffect();
        final Map, Matcher> enhanceMap = toEnhanceMap(pointCut);

        // 構(gòu)建增強(qiáng)器
        final Enhancer enhancer = new Enhancer(adviceId, isTracing, enhanceMap, affect);
        try {
            inst.addTransformer(enhancer, true);

            // 批量增強(qiáng)
            if (GlobalOptions.isBatchReTransform) {
                final int size = enhanceMap.size();
                final Class[] classArray = new Class[size];
                arraycopy(enhanceMap.keySet().toArray(), 0, classArray, 0, size);
                if (classArray.length > 0) {
                    inst.retransformClasses(classArray);
                }
            }


            // for each 增強(qiáng)
            else {
                for (Class clazz : enhanceMap.keySet()) {
                    try {
                        inst.retransformClasses(clazz);
                    } catch (Throwable t) {
                        logger.warn("reTransform {} failed.", clazz, t);
                        if (t instanceof UnmodifiableClassException) {
                            throw (UnmodifiableClassException) t;
                        } else if (t instanceof RuntimeException) {
                            throw (RuntimeException) t;
                        } else {
                            throw new RuntimeException(t);
                        }
                    }
                }
            }
        } finally {
            inst.removeTransformer(enhancer);
        }
        return affect;
    }

Enhancer實(shí)現(xiàn)了Java Instrumentation的接口ClassFileTransformer,來看其核心方法transform

    @Override
    public byte[] transform(
            final ClassLoader inClassLoader,
            final String className,
            final Class classBeingRedefined,
            final ProtectionDomain protectionDomain,
            final byte[] classfileBuffer) throws IllegalClassFormatException {

        // 過濾掉不在增強(qiáng)集合范圍內(nèi)的類
        if (!enhanceMap.containsKey(classBeingRedefined)) {
            return null;
        }

        final ClassReader cr;

        // 首先先檢查是否在緩存中存在Class字節(jié)碼
        // 因?yàn)橐С侄嗳藚f(xié)作,存在多人同時(shí)增強(qiáng)的情況
        final byte[] byteOfClassInCache = classBytesCache.get(classBeingRedefined);
        if (null != byteOfClassInCache) {
            cr = new ClassReader(byteOfClassInCache);
        }

        // 如果沒有命中緩存,則從原始字節(jié)碼開始增強(qiáng)
        else {
            cr = new ClassReader(classfileBuffer);
        }

        // 獲取這個(gè)類所對(duì)應(yīng)的asm方法匹配
        final Matcher asmMethodMatcher = enhanceMap.get(classBeingRedefined);

        // 字節(jié)碼增強(qiáng)
        final ClassWriter cw = new ClassWriter(cr, COMPUTE_FRAMES | COMPUTE_MAXS) {

            /*
             * 注意,為了自動(dòng)計(jì)算幀的大小,有時(shí)必須計(jì)算兩個(gè)類共同的父類。
             * 缺省情況下,ClassWriter將會(huì)在getCommonSuperClass方法中計(jì)算這些,通過在加載這兩個(gè)類進(jìn)入虛擬機(jī)時(shí),使用反射API來計(jì)算。
             * 但是,如果你將要生成的幾個(gè)類相互之間引用,這將會(huì)帶來問題,因?yàn)橐玫念惪赡苓€不存在。
             * 在這種情況下,你可以重寫getCommonSuperClass方法來解決這個(gè)問題。
             *
             * 通過重寫 getCommonSuperClass() 方法,更正獲取ClassLoader的方式,改成使用指定ClassLoader的方式進(jìn)行。
             * 規(guī)避了原有代碼采用Object.class.getClassLoader()的方式
             */
            @Override
            protected String getCommonSuperClass(String type1, String type2) {
                Class c, d;
                try {
                    c = Class.forName(type1.replace("/", "."), false, inClassLoader);
                    d = Class.forName(type2.replace("/", "."), false, inClassLoader);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                if (c.isAssignableFrom(d)) {
                    return type1;
                }
                if (d.isAssignableFrom(c)) {
                    return type2;
                }
                if (c.isInterface() || d.isInterface()) {
                    return "java/lang/Object";
                } else {
                    do {
                        c = c.getSuperclass();
                    } while (!c.isAssignableFrom(d));
                    return c.getName().replace(".", "/");
                }
            }

        };

        try {

            // 生成增強(qiáng)字節(jié)碼
            cr.accept(new AdviceWeaver(adviceId, isTracing, cr.getClassName(), asmMethodMatcher, affect, cw), EXPAND_FRAMES);
            final byte[] enhanceClassByteArray = cw.toByteArray();

            // 生成成功,推入緩存
            classBytesCache.put(classBeingRedefined, enhanceClassByteArray);

            // dump the class
            dumpClassIfNecessary(className, enhanceClassByteArray, affect);

            // 成功計(jì)數(shù)
            affect.cCnt(1);

            // 排遣間諜
            try {
                spy(inClassLoader);
            } catch (Throwable t) {
                logger.warn("print spy failed. classname={};loader={};", className, inClassLoader, t);
                throw t;
            }

            return enhanceClassByteArray;
        } catch (Throwable t) {
            logger.warn("transform loader[{}]:class[{}] failed.", inClassLoader, className, t);
        }

        return null;
    }

其最主要的邏輯應(yīng)該是派遣間諜了

     /*
     * 派遣間諜混入對(duì)方的classLoader中
     */
    private void spy(final ClassLoader targetClassLoader)
            throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {

        // 如果對(duì)方是bootstrap就算了
        if (null == targetClassLoader) {
            return;
        }


        // Enhancer類只可能從greysClassLoader中加載
        // 所以找他要ClassLoader是靠譜的
        final ClassLoader greysClassLoader = Enhancer.class.getClassLoader();

        final String spyClassName = GaStringUtils.SPY_CLASSNAME;

        // 從GreysClassLoader中加載Spy
        final Class spyClassFromGreysClassLoader = loadSpyClassFromGreysClassLoader(greysClassLoader, spyClassName);
        if (null == spyClassFromGreysClassLoader) {
            return;
        }

        // 從目標(biāo)ClassLoader中嘗試加載或定義ClassLoader
        Class spyClassFromTargetClassLoader = null;
        try {

            // 去目標(biāo)類加載器中找下是否已經(jīng)存在間諜
            // 如果間諜已經(jīng)存在就算了
            spyClassFromTargetClassLoader = targetClassLoader.loadClass(spyClassName);
            logger.info("Spy already in targetClassLoader : " + targetClassLoader);

        }

        // 看來間諜不存在啊
        catch (ClassNotFoundException cnfe) {

            try {// 在目標(biāo)類加載起中混入間諜
                spyClassFromTargetClassLoader = defineClass(
                        targetClassLoader,
                        spyClassName,
                        toByteArray(Enhancer.class.getResourceAsStream("/" + spyClassName.replace(".", "/") + ".class"))
                );
            } catch (InvocationTargetException ite) {
                if (ite.getCause() instanceof java.lang.LinkageError) {
                    // CloudEngine 由于 loadClass 不到,會(huì)導(dǎo)致 java.lang.LinkageError: loader (instance of  com/alipay/cloudengine/extensions/equinox/KernelAceClassLoader): attempted  duplicate class definition for name: "com/taobao/arthas/core/advisor/Spy"
                    // 這里嘗試忽略
                    logger.debug("resolve #112 issues", ite);
                } else {
                    throw ite;
                }
            }

        }


        // 無論從哪里取到spyClass,都需要重新初始化一次
        // 用以兼容重新加載的場(chǎng)景
        // 當(dāng)然,這樣做會(huì)給渲染的過程帶來一定的性能開銷,不過能簡(jiǎn)化編碼復(fù)雜度
        finally {

            if (null != spyClassFromTargetClassLoader) {
                // 初始化間諜
                invokeStaticMethod(
                        spyClassFromTargetClassLoader,
                        "init",
                        greysClassLoader,
                        getField(spyClassFromGreysClassLoader, "ON_BEFORE_METHOD").get(null),
                        getField(spyClassFromGreysClassLoader, "ON_RETURN_METHOD").get(null),
                        getField(spyClassFromGreysClassLoader, "ON_THROWS_METHOD").get(null),
                        getField(spyClassFromGreysClassLoader, "BEFORE_INVOKING_METHOD").get(null),
                        getField(spyClassFromGreysClassLoader, "AFTER_INVOKING_METHOD").get(null),
                        getField(spyClassFromGreysClassLoader, "THROW_INVOKING_METHOD").get(null)
                );
            }

        }

    }

接下來看Spy的實(shí)現(xiàn),發(fā)現(xiàn)其沒什么特別的啊,怎么實(shí)現(xiàn)織入呢,期間在這迷了很久

    public class Spy {
    // -- 各種Advice的鉤子引用 --
    public static volatile Method ON_BEFORE_METHOD;
    public static volatile Method ON_RETURN_METHOD;
    public static volatile Method ON_THROWS_METHOD;
    public static volatile Method BEFORE_INVOKING_METHOD;
    public static volatile Method AFTER_INVOKING_METHOD;
    public static volatile Method THROW_INVOKING_METHOD;

    /**
     * 代理重設(shè)方法
     */
    public static volatile Method AGENT_RESET_METHOD;

    /*
     * 用于普通的間諜初始化
     */
    public static void init(
            @Deprecated
            ClassLoader classLoader,
            Method onBeforeMethod,
            Method onReturnMethod,
            Method onThrowsMethod,
            Method beforeInvokingMethod,
            Method afterInvokingMethod,
            Method throwInvokingMethod) {
        ON_BEFORE_METHOD = onBeforeMethod;
        ON_RETURN_METHOD = onReturnMethod;
        ON_THROWS_METHOD = onThrowsMethod;
        BEFORE_INVOKING_METHOD = beforeInvokingMethod;
        AFTER_INVOKING_METHOD = afterInvokingMethod;
        THROW_INVOKING_METHOD = throwInvokingMethod;
    }

    /*
     * 用于啟動(dòng)線程初始化
     */
    public static void initForAgentLauncher(
            @Deprecated
            ClassLoader classLoader,
            Method onBeforeMethod,
            Method onReturnMethod,
            Method onThrowsMethod,
            Method beforeInvokingMethod,
            Method afterInvokingMethod,
            Method throwInvokingMethod,
            Method agentResetMethod) {
        ON_BEFORE_METHOD = onBeforeMethod;
        ON_RETURN_METHOD = onReturnMethod;
        ON_THROWS_METHOD = onThrowsMethod;
        BEFORE_INVOKING_METHOD = beforeInvokingMethod;
        AFTER_INVOKING_METHOD = afterInvokingMethod;
        THROW_INVOKING_METHOD = throwInvokingMethod;
        AGENT_RESET_METHOD = agentResetMethod;
    }


    public static void clean() {
        ON_BEFORE_METHOD = null;
        ON_RETURN_METHOD = null;
        ON_THROWS_METHOD = null;
        BEFORE_INVOKING_METHOD = null;
        AFTER_INVOKING_METHOD = null;
        THROW_INVOKING_METHOD = null;
        AGENT_RESET_METHOD = null;
    }

}

迷久了,偶爾查看其方法的調(diào)用,發(fā)現(xiàn)奧妙,其真正值得織入邏輯原來是在AdviceWeaver的相關(guān)方法內(nèi)

   private static ClassLoader loadOrDefineClassLoader(String agentJar) throws Throwable {

        final ClassLoader classLoader;

        // 如果已經(jīng)被啟動(dòng)則返回之前啟動(dòng)的classloader
        if (null != greysClassLoader) {
            classLoader = greysClassLoader;
        }

        // 如果未啟動(dòng)則重新加載
        else {
            classLoader = new AgentClassLoader(agentJar);

            // 獲取各種Hook
            final Class adviceWeaverClass = classLoader.loadClass("com.github.ompc.greys.core.advisor.AdviceWeaver");

            // 初始化全局間諜
            Spy.initForAgentLauncher(
                    classLoader,
                    adviceWeaverClass.getMethod("methodOnBegin",
                            int.class,
                            ClassLoader.class,
                            String.class,
                            String.class,
                            String.class,
                            Object.class,
                            Object[].class),
                    adviceWeaverClass.getMethod("methodOnReturnEnd",
                            Object.class,
                            int.class),
                    adviceWeaverClass.getMethod("methodOnThrowingEnd",
                            Throwable.class,
                            int.class),
                    adviceWeaverClass.getMethod("methodOnInvokeBeforeTracing",
                            int.class,
                            Integer.class,
                            String.class,
                            String.class,
                            String.class),
                    adviceWeaverClass.getMethod("methodOnInvokeAfterTracing",
                            int.class,
                            Integer.class,
                            String.class,
                            String.class,
                            String.class),
                    adviceWeaverClass.getMethod("methodOnInvokeThrowTracing",
                            int.class,
                            Integer.class,
                            String.class,
                            String.class,
                            String.class,
                            String.class),
                    AgentLauncher.class.getMethod("resetGreysClassLoader")
            );
        }

        return greysClassLoader = classLoader;
    }

抽出一個(gè)方法來看,其最終還是委托l(wèi)istner的before來實(shí)現(xiàn)的,MonitorCommand只是實(shí)現(xiàn)一個(gè)invokeCost.Begin.可是沒有字節(jié)碼增強(qiáng)啊,怎么能動(dòng)態(tài)實(shí)現(xiàn)呢

     /**
     * 方法開始
* 用于編織通知器,外部不會(huì)直接調(diào)用 * * @param loader 類加載器 * @param adviceId 通知ID * @param className 類名 * @param methodName 方法名 * @param methodDesc 方法描述 * @param target 返回結(jié)果 * 若為無返回值方法(void),則為null * @param args 參數(shù)列表 */ public static void methodOnBegin( int adviceId, ClassLoader loader, String className, String methodName, String methodDesc, Object target, Object[] args) { if (!advices.containsKey(adviceId)) { return; } if (isSelfCallRef.get()) { return; } else { isSelfCallRef.set(true); } try { // 構(gòu)建執(zhí)行幀棧,保護(hù)當(dāng)前的執(zhí)行現(xiàn)場(chǎng) final GaStack frameStack = new ThreadUnsafeFixGaStack(FRAME_STACK_SIZE); frameStack.push(loader); frameStack.push(className); frameStack.push(methodName); frameStack.push(methodDesc); frameStack.push(target); frameStack.push(args); final AdviceListener listener = getListener(adviceId); frameStack.push(listener); // 獲取通知器并做前置通知 before(listener, loader, className, methodName, methodDesc, target, args); // 保護(hù)當(dāng)前執(zhí)行幀棧,壓入線程幀棧 threadFrameStackPush(frameStack); } finally { isSelfCallRef.set(false); } }

其實(shí)上面都是幌子,真正的增強(qiáng)是透過visitMethod實(shí)現(xiàn)的,其又委托了AdviceAdapter實(shí)現(xiàn),其onMethodEnter方法是真正的before類增強(qiáng)(參見ASM官方文檔),
這里面不只用了字節(jié)碼增強(qiáng),還直接操作了堆棧,這部分看的云里霧里的.你有好的資料推薦我學(xué)習(xí),我會(huì)很感謝的,如果我實(shí)現(xiàn)的話應(yīng)該就如上述ASM例子中的實(shí)現(xiàn),加字節(jié)碼之類的吧.

            @Override
            protected void onMethodEnter() {

                codeLockForTracing.lock(new CodeLock.Block() {
                    @Override
                    public void code() {

                        final StringBuilder append = new StringBuilder();
                        _debug(append, "debug:onMethodEnter()");

                        // 加載before方法
                        loadAdviceMethod(KEY_GREYS_ADVICE_BEFORE_METHOD);
                        _debug(append, "loadAdviceMethod()");

                        // 推入Method.invoke()的第一個(gè)參數(shù)
                        pushNull();

                        // 方法參數(shù)
                        loadArrayForBefore();
                        _debug(append, "loadArrayForBefore()");

                        // 調(diào)用方法
                        invokeVirtual(ASM_TYPE_METHOD, ASM_METHOD_METHOD_INVOKE);
                        pop();
                        _debug(append, "invokeVirtual()");

                    }
                });

                mark(beginLabel);

            }

最近看了幾個(gè)阿里開源的框架或工具,希望能有機(jī)會(huì)去阿里碼代碼,和優(yōu)秀的人一起共事.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67483.html

相關(guān)文章

  • JVM解剖公園

    摘要:為此,引入轉(zhuǎn)換查找緩沖緩存最近的轉(zhuǎn)換記錄。這個(gè)優(yōu)化技術(shù),可以看到將原本對(duì)對(duì)象的字段訪問,替換為一個(gè)局部變量的訪問。當(dāng)所有線程都在已知的位置停止的時(shí)候,被認(rèn)為是到達(dá)了安全點(diǎn)。檢查安全點(diǎn)請(qǐng)求的代碼 showImg(https://segmentfault.com/img/bVbwfcz?w=1024&h=576); 1、JVM鎖粗化和循環(huán)原文標(biāo)題:JVM Anatomy Quark #1:...

    imingyu 評(píng)論0 收藏0
  • 淺析webpack源碼之前言(一)

    為什么讀webpack源碼 因?yàn)榍岸丝蚣茈x不開webpack,天天都在用的東西啊,怎能不研究 讀源碼能學(xué)到很多做項(xiàng)目看書學(xué)不到的東西,比如說架構(gòu),構(gòu)造函數(shù),es6很邊緣的用法,甚至給函數(shù)命名也會(huì)潛移默化的影響等 想寫源碼,不看源碼怎么行,雖然現(xiàn)在還不知道寫什么,就算不寫什么,看看別人寫的總可以吧 知道世界的廣闊,那么多插件,那么多軟件開發(fā)師,他們?cè)谧鍪裁矗瑯邮菍慾s的,怎么他們能這么偉大 好奇...

    suosuopuo 評(píng)論0 收藏0
  • 淺析HashMap源碼(1)

    摘要:前言本文的目的是閱讀理解的源碼,作為集合中重要的一個(gè)角色,平時(shí)用到十分多的一個(gè)類,深入理解它,知其所以然很重要。 前言 本文的目的是閱讀理解HashMap的源碼,作為集合中重要的一個(gè)角色,平時(shí)用到十分多的一個(gè)類,深入理解它,知其所以然很重要。本文基于Jdk1.7,因?yàn)镴dk1.8改變了HashMap的數(shù)據(jù)結(jié)構(gòu),進(jìn)行了優(yōu)化,我們先從基礎(chǔ)閱讀,之后再閱讀理解Jdk1.8的內(nèi)容 HashMa...

    wwolf 評(píng)論0 收藏0
  • 淺析`redux-thunk`中間件源碼

    摘要:大多的初學(xué)者都會(huì)使用中間件來處理異步請(qǐng)求,其理解簡(jiǎn)單使用方便具體使用可參考官方文檔。源碼的源碼非常簡(jiǎn)潔,出去空格一共只有行,這行中如果不算上則只有行。官方文檔中的一節(jié)講解的非常好,也確實(shí)幫我理解了中間件的工作原理,非常推薦閱讀。 總覺得文章也應(yīng)該是有生命力的,歡迎關(guān)注我的Github上的博客,這里的文章會(huì)依據(jù)我本人的見識(shí),逐步更新。 大多redux的初學(xué)者都會(huì)使用redux-thunk...

    wing324 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<