groovyclassloader.java

来自「Groovy动态语言 运行在JVM中的动态语言 可以方便的处理业务逻辑变化大的业」· Java 代码 · 共 854 行 · 第 1/3 页

JAVA
854
字号
    public Class parseClass(GroovyCodeSource codeSource, boolean shouldCacheSource) throws CompilationFailedException {
        synchronized (classCache) {
            Class answer = (Class) sourceCache.get(codeSource.getName());
            if (answer!=null) return answer;
            
            // Was neither already loaded nor compiling, so compile and add to
            // cache.
            try {
                CompilationUnit unit = createCompilationUnit(config, codeSource.getCodeSource());
                SourceUnit su = null;
                if (codeSource.getFile()==null) {
                    su = unit.addSource(codeSource.getName(), codeSource.getInputStream());
                } else {
                    su = unit.addSource(codeSource.getFile());
                }
                
                ClassCollector collector = createCollector(unit,su);
                unit.setClassgenCallback(collector);
                int goalPhase = Phases.CLASS_GENERATION;
                if (config != null && config.getTargetDirectory()!=null) goalPhase = Phases.OUTPUT;
                unit.compile(goalPhase);
                
                answer = collector.generatedClass;
                for (Iterator iter = collector.getLoadedClasses().iterator(); iter.hasNext();) {
                    Class clazz = (Class) iter.next();
                    setClassCacheEntry(clazz);
                }
                if (shouldCacheSource) sourceCache.put(codeSource.getName(), answer);
            } finally {
                try {
                    InputStream is = codeSource.getInputStream();
                    if (is!=null) is.close();
                } catch (IOException e) {
                    throw new GroovyRuntimeException("unable to close stream",e);
                }
            }
            return answer;
        }
    }
    
    /**
     * gets the currently used classpath. 
     * @return a String[] containing the file information of the urls 
     * @see #getURLs()
     */
    protected String[] getClassPath() {
        //workaround for Groovy-835
        URL[] urls = getURLs();
        String[] ret = new String[urls.length];
        for (int i = 0; i < ret.length; i++) {
            ret[i] =  urls[i].getFile();
        }
        return ret;
    }

    /**
     * expands the classpath
     * @param pathList an empty list that will contain the elements of the classpath
     * @param classpath the classpath specified as a single string
     * @deprecated
     */
    protected void expandClassPath(List pathList, String base, String classpath, boolean isManifestClasspath) {
        throw new DeprecationException("the method groovy.lang.GroovyClassLoader#expandClassPath(List,String,String,boolean) is no longer used internally and removed");
    }

    /**
     * A helper method to allow bytecode to be loaded. spg changed name to
     * defineClass to make it more consistent with other ClassLoader methods
     * @deprecated
     */
    protected Class defineClass(String name, byte[] bytecode, ProtectionDomain domain) {
        throw new DeprecationException("the method groovy.lang.GroovyClassLoader#defineClass(String,byte[],ProtectionDomain) is no longer used internally and removed");
    }
    
    public static class InnerLoader extends GroovyClassLoader{
        private GroovyClassLoader delegate;
    	public InnerLoader(GroovyClassLoader delegate) {
    		super(delegate);
            this.delegate = delegate;
    	}
        public void addClasspath(String path) {
            delegate.addClasspath(path);
        }
        public void clearCache() {
            delegate.clearCache();
        }
        public URL findResource(String name) {
            return delegate.findResource(name);
        }
        public Enumeration findResources(String name) throws IOException {
            return delegate.findResources(name);
        }
        public Class[] getLoadedClasses() {
            return delegate.getLoadedClasses();
        }
        public URL getResource(String name) {
            return delegate.getResource(name);
        }
        public InputStream getResourceAsStream(String name) {
            return delegate.getResourceAsStream(name);
        }
        public GroovyResourceLoader getResourceLoader() {
            return delegate.getResourceLoader();
        }
        public URL[] getURLs() {
            return delegate.getURLs();
        }
        public Class loadClass(String name, boolean lookupScriptFiles, boolean preferClassOverScript, boolean resolve) throws ClassNotFoundException, CompilationFailedException {
            Class c = findLoadedClass(name);
            if (c!=null) return c;
            return delegate.loadClass(name, lookupScriptFiles, preferClassOverScript, resolve);
        }
        public Class parseClass(GroovyCodeSource codeSource, boolean shouldCache) throws CompilationFailedException {
            return delegate.parseClass(codeSource, shouldCache);
        }
        public void setResourceLoader(GroovyResourceLoader resourceLoader) {
            delegate.setResourceLoader(resourceLoader);
        }
        public void addURL(URL url) {
            delegate.addURL(url);
        }        
    }
    
    /**
     * creates a new CompilationUnit. If you want to add additional
     * phase operations to the CompilationUnit (for example to inject
     * additional methods, variables, fields), then you should overwrite
     * this method.
     * 
     * @param config the compiler configuration, usually the same as for this class loader
     * @param source the source containing the initial file to compile, more files may follow during compilation
     * 
     * @return the CompilationUnit
     */
    protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) {
        return new CompilationUnit(config, source, this);
    }

    /**
     * creates a ClassCollector for a new compilation.
     * @param unit the compilationUnit
     * @param su  the SoruceUnit
     * @return the ClassCollector
     */
    protected ClassCollector createCollector(CompilationUnit unit,SourceUnit su) {
    	InnerLoader loader = (InnerLoader) AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                return new InnerLoader(GroovyClassLoader.this);
            }
        }); 
        return new ClassCollector(loader, unit, su);
    }

    public static class ClassCollector extends CompilationUnit.ClassgenCallback {
        private Class generatedClass;
        private GroovyClassLoader cl;
        private SourceUnit su;
        private CompilationUnit unit;
        private Collection loadedClasses = null;

        protected ClassCollector(InnerLoader cl, CompilationUnit unit, SourceUnit su) {
            this.cl = cl;
            this.unit = unit;
            this.loadedClasses = new ArrayList();
            this.su = su;
        }
        protected GroovyClassLoader getDefiningClassLoader(){
            return cl;
        }
        protected Class createClass(byte[] code, ClassNode classNode) {
            GroovyClassLoader cl = getDefiningClassLoader();
            Class theClass = cl.defineClass(classNode.getName(), code, 0, code.length, unit.getAST().getCodeSource());
            cl.resolveClass(theClass);
            this.loadedClasses.add(theClass);

            if (generatedClass == null) {
                ModuleNode mn = classNode.getModule();
                SourceUnit msu = null;
                if (mn!=null) msu = mn.getContext();
                ClassNode main = null;
                if (mn!=null) main = (ClassNode) mn.getClasses().get(0);
                if (msu==su && main==classNode) generatedClass = theClass;
            }

            return theClass;
        }
        
        protected Class onClassNode(ClassWriter classWriter, ClassNode classNode) {
            byte[] code = classWriter.toByteArray();
            return createClass(code,classNode);
        }

        public void call(ClassVisitor classWriter, ClassNode classNode) {
            onClassNode((ClassWriter) classWriter, classNode);
        }

        public Collection getLoadedClasses() {
            return this.loadedClasses;
        }
    }

    /**
     * open up the super class define that takes raw bytes
     *
     */
    public Class defineClass(String name, byte[] b) {
        return super.defineClass(name, b, 0, b.length);
    }
    
    /**
     * loads a class from a file or a parent classloader.
     * This method does call loadClass(String, boolean, boolean, boolean)
     * with the last parameter set to false.
     * @throws CompilationFailedException 
     */
    public Class loadClass(final String name, boolean lookupScriptFiles, boolean preferClassOverScript)
        throws ClassNotFoundException, CompilationFailedException
    {
        return loadClass(name,lookupScriptFiles,preferClassOverScript,false);
    }

    /**
     * gets a class from the class cache. This cache contains only classes loaded through
     * this class loader or an InnerLoader instance. If no class is stored for a
     * specific name, then the method should return null. 
     *  
     * @param name of the class
     * @return the class stored for the given name 
     * @see #removeClassCacheEntry(String)
     * @see #setClassCacheEntry(Class)
     * @see #clearCache()
     */    
    protected Class getClassCacheEntry(String name) {
        if (name==null) return null;
        synchronized (classCache) {
            Class cls = (Class) classCache.get(name);
            return cls;
        }
    }
    
    /**
     * sets an entry in the class cache. 
     * @param cls the class
     * @see #removeClassCacheEntry(String)
     * @see #getClassCacheEntry(String)
     * @see #clearCache()
     */
    protected void setClassCacheEntry(Class cls) {
        synchronized (classCache) {
            classCache.put(cls.getName(),cls);
        }
    }    
    
    /**
     * removes a class from the class cache.
     * @param name of the class
     * @see #getClassCacheEntry(String)
     * @see #setClassCacheEntry(Class)
     * @see #clearCache()
     */
    protected void removeClassCacheEntry(String name) {
        synchronized (classCache) {
            classCache.remove(name);
        }
    }    
    
    /**
     * adds a URL to the classloader.
     * @param url the new classpath element 
     */
    public void addURL(URL url) {
        super.addURL(url);
    }
    
    /**
     * Indicates if a class is recompilable. Recompileable means, that the classloader
     * will try to locate a groovy source file for this class and then compile it again,
     * adding the resulting class as entry to the cache. Giving null as class is like a
     * recompilation, so the method should always return true here. Only classes that are
     * implementing GroovyObject are compileable and only if the timestamp in the class
     * is lower than Long.MAX_VALUE.  
     * 
     *  NOTE: First the parent loaders will be asked and only if they don't return a
     *  class the recompilation will happen. Recompilation also only happen if the source
     *  file is newer.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?