Kilim源码分析之二 ---- 织入入口及可织入判断

阅读更多

1、织入入口,配置

    1.1、织入入口 
        kilim.tools.Weaver是织入的主类,通过程序参数设置要织入的代码路径,可以是class文件、jar包、其他(是什么)、目录(目录中可以是jar包、class文件)。
        如果传入的是class文件,会直接织入;调用kilim.tools.Weaver.weaveFile(String, InputStream, Detector)织入
        如果是jar包,会解析jar文件,for循环时候,jdk会把for编译成iterator接口实现,对于jar文件,实际提取文件内容的是kilim.analysis.JarIterator,从java.util.jar.JarFile中直接获取所有entries,for循环中返回的每一项(class文件或者目录)都是FileLister.Entry的子类JEntry,可以获取文件名、文件流和文件大小。然后循环jar文件中每一项,如果是class文件就织入,调用接口同传入class文件一样。
          如果是目录,则会递归目录,但是也只会处理目录下的class文件,不会处理目录下的jar包。处理方式类似jar包,会调用迭代器DirIterator处理,迭代器没有用递归实现,而是自己放入了个Stack。迭代时候每次返回FileLister.Entry的子类DirEntry。jar包验证,处理.             
    1.2、ant配置
 

	
	
		
	
	
	
	
	
	
	
     

2、可织入分析 && 织入文件

    2.1、织入接口kilim.tools.Weaver.weaveFile(String, InputStream, Detector)

        在1中的织入入口会调用到,包括织入代码,把织入后的代码写到磁盘:

static void weaveFile(String name, InputStream is, Detector detector) throws IOException {
        try {
            ClassWeaver cw = new ClassWeaver(is, detector);
            cw.weave();//织入
            writeClasses(cw);//把织入后的代码字节数据从ClassInfo写入到class文件
        } catch (KilimException ke) {
            System.err.println("***** Error weaving " + name + ". " + ke.getMessage());
            // ke.printStackTrace();
            err = 1;
        } catch (RuntimeException re) {
            System.err.println("***** Error weaving " + name + ". " + re.getMessage());
            re.printStackTrace();
            err = 1;
        } catch (IOException ioe) {
            err = 1;
            System.err.println("***** Unable to find/process '" + name + "'\n" + ioe.getMessage());
        }
    }
              创建ClassWeaver的时候,传入的decetor是Detector.DEFAULT,一个默认的实现,这个类主要用来判断类是否需要织入、织入状态灯。
        ClassWeaver封装了ClassFlow,ClassFlow是继承了ClassNode的,ClassNode是asm拿着类文件中所有内容构造的一棵树。在构造ClassWeaver的时候,就会构造一个ClassFlow对象:
    public ClassWeaver(InputStream is, Detector detector) throws IOException {
        classFlow = new ClassFlow(is, detector);
    }
         cw.weave()[kilim.analysis.ClassWeaver.weave()]是织入的地方,包括对字节码的分析、通过ClassWriter织入、把植入后的字节码转换成ClassInfo对象:
public void weave() throws KilimException {
        classFlow.analyze(false);//分析
        if (needsWeaving() && classFlow.isPausable()) {
            ClassWriter cw = new ClassWriter(false);
            accept(cw);//对需要进行织入的方法进行织入
            addClassInfo(new ClassInfo(classFlow.getClassName(), cw.toByteArray()));//把需要植入后的字节码从ClassWriter放入到ClassInfo
        }
    }
      ClassFlow.analyze(false),对一个方法是否需要织入的分析,方法isPausable为true的就是可织入的:
public ArrayList analyze(boolean forceAnalysis) throws KilimException {
        Detector save = Detector.setDetector(detector);
        try {
            cr.accept(this, false);//把当前ClassFlow实例传入到ClassReader中,会把当前类信息填充到ClassFlow中
            for (Object o : this.fields) {
                FieldNode fn = (FieldNode) o;
                if (fn.name.equals(Constants.WOVEN_FIELD)) {//织入的时候会写入的一个变量,如果已经有了,则表明已经织入过
                    isWoven = true;
                    break;
                }
            }
            if (isWoven && !forceAnalysis) 
                return new ArrayList(); // This is a hack. 如果织入过并且不强制织入,就返回。强制呢? 
            cr = null; // We don't need this any more.
            classDesc = TypeDesc.getInterned("L" + name + ';');//类型描述符
            ArrayList flows = new ArrayList(methods.size());
            String msg = "";
            for (Object o : methods) {
                try {
                    MethodFlow mf = (MethodFlow) o;
                    if (mf.isBridge()) {//方法access_flag包含bridge的方法,方法由编译器产生
                        MethodFlow mmf = getOrigWithSameSig(mf);//获取bridge方法对应的原始方法
                        if (mmf != null)
                            mf.setPausable(mmf.isPausable());//把birdge方法的pausable属性设置给原始方法
                    }
                    mf.verifyPausables();//验证可织入性,如果已经织入或者不需要织入是直接返回了的
                    if (mf.isPausable())
                        isPausable = true;
                    if ((mf.isPausable() || forceAnalysis) && (!mf.isAbstract())) {
                        mf.analyze();
                    }
                    flows.add(mf);
                } catch (KilimException ke) {
                    msg = msg + ke.getMessage() + "\n-------------------------------------------------\n";
                }
            }
            if (msg.length() > 0) {
                throw new KilimException(msg);
            }
            methodFlows = flows;
            return flows;
        } finally {
            Detector.setDetector(save);
        }
    }
          mf.isBridge()里边是判断方法access flag是否有bridge标记,这种方法是由编译器生成,是JDK1.5引入泛型后,为了是java泛型方法生成的字节码和1.5之前的字节码相兼容,由编译器[为某个方法]自动生成的。更多关于bridge的信息,猛击这里和这里。

你可能感兴趣的:(bridge,kilim,pausable,织入,weave)