Community
Participate
Working Groups
loadtime weaving using WeavingURLClassLoader does not use current url classloader when lookupClass in function ObjectType.referencesClass. When weaving a class loaded by a WeavingURLClassLoader, it will call Utility.createConversion(getFactory(), BcelWorld.makeBcelType(returnType), callbackMethod.getReturnType(), world.isInJava5Mode()) in line 2922 in class BcelShadow, in this stack, ObjectType.referencesClass is invoked when judge a class is assignable from another class. But class lookup method is global static, by default, it will load class from system classpath, rather then current WeavingURLClassLoader, which will cause class not found. the classloader ref in LtwWorld is lost! public boolean referencesClass() { JavaClass jc = Repository.lookupClass(classname); if (jc == null) { return false; } else { return jc.isClass(); } } To fix this problem, we can define a ThreadLocalRepository as global repositry, and set a ClassloaderRepository instance to threadlocal when WeavingURLClassLoader.loadClass is invoked, code like this: public class ThreadLocalRepository implements Repository { private static ThreadLocal<WeakReference<Repository>> threadLocal = new ThreadLocal<>(); public static void setCurrentRepository(Repository repo) { threadLocal.set(new WeakReference<>(repo)); } public static Repository getCurrentRepository() { WeakReference<Repository> ref = threadLocal.get(); if (ref != null) { return ref.get(); } return null; } @Override public void storeClass(JavaClass clazz) { Repository repository = getCurrentRepository(); if (repository != null) { repositoryHit.incrementAndGet(); repository.storeClass(clazz); } else { repositoryMissed.incrementAndGet(); SyntheticRepository.getInstance().storeClass(clazz); } } @Override public void removeClass(JavaClass clazz) { Repository repository = getCurrentRepository(); if (repository != null) { repository.removeClass(clazz); } else { SyntheticRepository.getInstance().removeClass(clazz); } } @Override public JavaClass findClass(String className) { Repository repository = getCurrentRepository(); if (repository != null) { return repository.findClass(className); } else { return SyntheticRepository.getInstance().findClass(className); } } @Override public JavaClass loadClass(String className) throws ClassNotFoundException { Repository repository = getCurrentRepository(); if (repository != null) { return repository.loadClass(className); } else { return SyntheticRepository.getInstance().loadClass(className); } } @Override public JavaClass loadClass(Class clazz) throws ClassNotFoundException { Repository repository = getCurrentRepository(); if (repository != null) { return repository.loadClass(clazz); } else { return SyntheticRepository.getInstance().loadClass(clazz); } } @Override public void clear() { Repository repository = getCurrentRepository(); if (repository != null) { repository.clear(); } else { SyntheticRepository.getInstance().clear(); } } } WeavingURLClassLoader class code may like this: static { ClassLoader.registerAsParallelCapable(); // 修复aspectj bug Repository.setRepository(new ThreadLocalRepository()); } private org.aspectj.apache.bcel.util.Repository repository; public WeavingURLClassLoader(URL[] urls) { super(urls); repository = new NonCachingClassLoaderRepository(this); } protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { try { ThreadLocalRepository.setCurrentRepository(repository); // do load class } finally { ThreadLocalRepository.setCurrentRepository(null); } }
I created a ThreadLocalAwareRepository in anticipation of doing more here, but I'd really like a testcase or test program that shows the problem - if you have one? So we can be confident in the fix and that it doesn't get rebroken in the future.