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 + -
显示快捷键?