写swiftly的一个场景:需要加载非classpath路径下的jar包。其中用了spring来做容器管理,但是遇到一个问题:无法加载到自己想要的类。
因为是通过:ApplicationContext applicationContext = new ClassPathXmlApplicationContext(path); 来初始化所有的类。
spring默认的classloader会是自己定义的DefaultResourceLoader,并且会把 DefaultResourceLoader设置当前线程的默认加载器。当你在加载外部类的时候就会找不到类,因为加载外部类是在另一个ClassLoader中。先看看几种加载方式
1.加载到classpath中:
private static URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); private static Method addMethod = initAddMethod(); public static void addClassPath() throws FileNotFoundException { List<URL> urlList = loadAllURL(); if (CollectionUtils.isEmpty(urlList)) { return; } for (URL url : urlList) { addURL(url); } } private static List<URL> loadAllURL() throws FileNotFoundException { List<URL> urlList = new ArrayList<URL>(); File baseDir = new File(rootDir); if (!baseDir.exists()) { LOGGER.error("can not find defaultRepositoryPath"); throw new FileNotFoundException("base file not find ! file:" + rootDir); } findURLs(baseDir, urlList); return urlList; } private static void addURL(URL file) { try { addMethod.invoke(classLoader, new Object[] { file }); } catch (Exception e) { } } private static Method initAddMethod() { try { Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class }); add.setAccessible(true); return add; } catch (Exception e) { throw new RuntimeException(e); } }
第二种 通过自定义ClassLoader来解决加载问题
</pre> public static ClassLoader createClassLoader(ClassLoader parent) throws FileNotFoundException { StandardClassLoader classLoader = null; List<URL> urlList = new ArrayList<URL>(); File baseDir = new File(rootDir); if (!baseDir.exists()) { LOGGER.error("can not find defaultRepositoryPath"); throw new FileNotFoundException("base file not find ! file:" + rootDir); } findURLs(baseDir, urlList); URL[] urls = urlList.toArray(new URL[urlList.size()]); if (parent == null) { classLoader = new StandardClassLoader(urls); } else { classLoader = new StandardClassLoader(urls, parent); } return classLoader; } <pre>
在单独跑main方法的情况下都可以,通过ClassPathXmlApplicationContext方式来的时候,需要设置下默认的classloader
</pre> DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); beanFactory.setBeanClassLoader(classLoader); <pre>
这个问题困扰了我两天时间,主要还是对spring不熟悉导致。
0 条评论。