浏览 13651 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-11-20
在外部以jar包的形式存放若干个插件,其中包含插件的类,以及spring配置文件;jar包不在classpath里 要实现这个需求,需要用到自定义的ClassLoader,并调用一些spring提供的API 首先是jar包的结构: 其中net文件夹下面,放了要从外部加载的目标类 package net.kyfxbl.test; public class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void sayName() { System.out.println(getName()); } } 配置文件spring-plugin.xml内容是: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="myUser" class="net.kyfxbl.test.User"> <property name="name" value="kyfxbl" /> </bean> </beans> 现在目标,就是读取hehe.jar中的spring-plugin.xml文件,然后实例化一个User对象 示例代码如下,要说明的是,代码很简陋,仅供参考。主要是由于本人spring学艺不精,所以API调用那块,肯定不是最佳实践,绝对是可以优化的: public class Main { public static void main(String[] args) { try { ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "spring-config.xml"); DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext .getAutowireCapableBeanFactory(); String configurationFilePath = "jar:file:/C:/hehe.jar!/spring-plugin.xml"; URL url = new URL(configurationFilePath); UrlResource urlResource = new UrlResource(url); XmlBeanFactory xbf = new XmlBeanFactory(urlResource); String[] beanIds = xbf.getBeanDefinitionNames(); for (String beanId : beanIds) { BeanDefinition bd = xbf.getMergedBeanDefinition(beanId); beanFactory.registerBeanDefinition(beanId, bd); } // 以下这行设置BeanFactory的ClassLoader,以加载外部类 setBeanClassLoader(beanFactory); Object pluginBean = applicationContext.getBean("myUser"); tryInvoke(pluginBean); } catch (Exception exc) { exc.printStackTrace(); } } private static void setBeanClassLoader( DefaultListableBeanFactory beanFactory) throws MalformedURLException { String jarFilePath = "c://hehe.jar"; URL jarUrl = new File(jarFilePath).toURI().toURL(); URL[] urls = new URL[] { jarUrl }; URLClassLoader cl = new URLClassLoader(urls); beanFactory.setBeanClassLoader(cl); } private static void tryInvoke(Object bean) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { Class<?> paramTypes[] = new Class[0]; Method method = bean.getClass() .getDeclaredMethod("sayName", paramTypes); Object paramValues[] = new Object[0]; method.invoke(bean, paramValues); } } 如果注释掉 setBeanClassLoader(beanFactory); 则报异常: Caused by: java.lang.ClassNotFoundException: net.kyfxbl.test.User 所以关键就是通过setBeanClassLoader()方法,修改beanFactory默认的ClassLoader 原理比较简单:如果不做特殊配置的话,spring将使用默认的ClassLoader(也就是App ClassLoader),那么就不会加载hehe.jar中的类。所以通过自定义URLClassLoader,并将其设置为BeanFactory的BeanClassLoader,就可以将hehe.jar加载进来了。有兴趣的朋友可以自行阅读源码,获得更多细节 附带的,为了证明spring默认的ClassLoader就是App ClassLoader,补充了几行测试代码 // 打印ClassLoader看看 printClassLoader(beanFactory); // 以下这行设置BeanFactory的ClassLoader,以加载外部类 setBeanClassLoader(beanFactory); private static void printClassLoader(DefaultListableBeanFactory beanFactory) { ClassLoader defaultBeanClassLoader = beanFactory.getBeanClassLoader(); System.out.println(defaultBeanClassLoader); ClassLoader currentClassLoader = Main.class.getClassLoader(); System.out.println(currentClassLoader); } 运行结果: sun.misc.Launcher$AppClassLoader@addbf1 sun.misc.Launcher$AppClassLoader@addbf1 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-11-21
最后修改:2012-11-21
我记得spring不是有个FileSystemXmlApplicationContext可以直接加载磁盘路劲上的任何spring配置文件哦
|
|
返回顶楼 | |
发表时间:2012-11-21
可以看看这个项目
https://github.com/kamranzafar/JCL |
|
返回顶楼 | |
发表时间:2012-11-22
kjj 写道 我记得spring不是有个FileSystemXmlApplicationContext可以直接加载磁盘路劲上的任何spring配置文件哦
恩,对~~不过要加载CLASS,设置ClassLoader还是必须的,跟用什么ApplicationContext无关 |
|
返回顶楼 | |
发表时间:2012-11-22
kjj 写道 我记得spring不是有个FileSystemXmlApplicationContext可以直接加载磁盘路劲上的任何spring配置文件哦
楼主的意思是,在系统运行后,通过添加插件的方式,加载到现行的系统中去。也就是想表达:自定义ClassLoader来动态装载类文件。 |
|
返回顶楼 | |
发表时间:2012-11-22
最后修改:2012-11-22
我也碰到了类似的场景,有多个plugin,每个plugin有自己的loader
这样处理就有问题了,beanFactory.setBeanClassLoader(loader);后,loader被改写,最终是最后调用这个方法的plugin的loader,于是get前面几个插件的Bean的时候就会报classNotFound方法。 |
|
返回顶楼 | |
发表时间:2012-11-22
kyfxbl 写道 kjj 写道 我记得spring不是有个FileSystemXmlApplicationContext可以直接加载磁盘路劲上的任何spring配置文件哦
恩,对~~不过要加载CLASS,设置ClassLoader还是必须的,跟用什么ApplicationContext无关 哦,我明白了,突然你这个思路解决了我很久以前的疑惑,我曾想把一个应用拆成一个个模块,通过插件的形式,根据需要加载使用,一直没有好的方案,曾想osgi,但这个也没用成熟的方案,楼主这样也许是个思路啊 |
|
返回顶楼 | |
发表时间:2012-11-22
kyfxbl 写道 kjj 写道 我记得spring不是有个FileSystemXmlApplicationContext可以直接加载磁盘路劲上的任何spring配置文件哦
恩,对~~不过要加载CLASS,设置ClassLoader还是必须的,跟用什么ApplicationContext无关 楼主,我无意中发现这样的代码也可以用 URL url = new URL("jar:file:G:/maven/repo/com/google/code/gson/gson/2.2.1/gson-2.2.1.jar!/"); @SuppressWarnings("resource") URLClassLoader uc = new URLClassLoader(new URL[]{url}); Class<?> cls = uc.loadClass("com.google.gson.Gson"); Object obj = cls.newInstance(); logger.info(obj); 而输出结果也是正确的,请问你的classloader又有何区别呢 引用 {serializeNulls:falsefactories:[Factory[type=java.lang.String,adapter=com.google.gson.internal.bind.TypeAdapters$13@7a8dfdd4], Factory[type=java.lang.Integer+int,adapter=com.google.gson.internal.bind.TypeAdapters$7@13a828], Factory[type=java.lang.Boolean+boolean,adapter=com.google.gson.internal.bind.TypeAdapters$3@a84da22], Factory[type=java.lang.Byte+byte,adapter=com.google.gson.internal.bind.TypeAdapters$5@180cf393], Factory[type=java.lang.Short+short,adapter=com.google.gson.internal.bind.TypeAdapters$6@7a4b443a], Factory[type=java.lang.Long+long,adapter=com.google.gson.internal.bind.TypeAdapters$8@636c7a8f], Factory[type=java.lang.Double+double,adapter=com.google.gson.Gson$4@66e27547], Factory[type=java.lang.Float+float,adapter=com.google.gson.Gson$5@1d81bd16], com.google.gson.internal.Excluder@30a4fe7c, Factory[type=java.lang.Number,adapter=com.google.gson.internal.bind.TypeAdapters$11@203f97d7], Factory[type=java.lang.Character+char,adapter=com.google.gson.internal.bind.TypeAdapters$12@2f09b4cb], Factory[type=java.lang.StringBuilder,adapter=com.google.gson.internal.bind.TypeAdapters$16@263c938d], Factory[type=java.lang.StringBuffer,adapter=com.google.gson.internal.bind.TypeAdapters$17@52fb2197], Factory[type=com.google.gson.JsonElement,adapter=com.google.gson.internal.bind.TypeAdapters$25@655a6b10], com.google.gson.internal.bind.ObjectTypeAdapter$1@132b1b6c, Factory[type=java.math.BigDecimal,adapter=com.google.gson.internal.bind.TypeAdapters$14@73155948], Factory[type=java.math.BigInteger,adapter=com.google.gson.internal.bind.TypeAdapters$15@7e98c0fe], com.google.gson.internal.bind.CollectionTypeAdapterFactory@5ea6a4a0, Factory[type=java.net.URL,adapter=com.google.gson.internal.bind.TypeAdapters$18@e8234ec], Factory[type=java.net.URI,adapter=com.google.gson.internal.bind.TypeAdapters$19@6607965], Factory[type=java.util.UUID,adapter=com.google.gson.internal.bind.TypeAdapters$21@27c1d928], Factory[type=java.util.Locale,adapter=com.google.gson.internal.bind.TypeAdapters$24@2797e57c], Factory[typeHierarchy=java.net.InetAddress,adapter=com.google.gson.internal.bind.TypeAdapters$20@655b4432], Factory[type=java.util.BitSet,adapter=com.google.gson.internal.bind.TypeAdapters$2@4ada629a], com.google.gson.internal.bind.DateTypeAdapter$1@477f92da, Factory[type=java.util.Calendar+java.util.GregorianCalendar,adapter=com.google.gson.internal.bind.TypeAdapters$23@8824ae2], com.google.gson.internal.bind.TimeTypeAdapter$1@22023fcb, com.google.gson.internal.bind.SqlDateTypeAdapter$1@41aa39de, com.google.gson.internal.bind.TypeAdapters$22@ebd3f80, com.google.gson.internal.bind.MapTypeAdapterFactory@2a93f40f, com.google.gson.internal.bind.ArrayTypeAdapter$1@577c1a9f, com.google.gson.internal.bind.TypeAdapters$26@14b7e998, Factory[type=java.lang.Class,adapter=com.google.gson.internal.bind.TypeAdapters$1@364904c8], com.google.gson.internal.bind.ReflectiveTypeAdapterFactory@73d9024f],instanceCreators:{}} - 2012-11-22 22:56:11,090 [org.atomsoft.ci.ciscaffolding.AppTest] [Line:66] |
|
返回顶楼 | |