Java ClassLoader 实现程序的扩展性
今天在完成一个功能的时候,使用了ServiceLocate模式,
通过这个模式,在程序中可以自由的加载其他成员实现的功能模块
具体的做法:
1)定义标准的服务接口。
2)定义描述实现服务接口的xml文件。
3)程序读取该xml文件,使用Class.newInstance()实例化具体的服务对象。
4)建立一个特定服务和特定服务实现的对应的HashMap对象。完成注册任务。
5)主程序中根据具体的服务从HashMap中取得具体的对象进行服务。
这个方法还不错,可以完成基于Interface的开发要求,利于Test和程序的拓展性。
有新的要求出现后,只需要添加xml中的元素和具体的实现类就可以了。
接下来,继续想。又发现了一些问题:
1)xml是和程序一起发布的,如果用户随意改动了。很明显程序会崩溃。
解决方法:xml放在jar包中,使用getClass().getResourceAsStream(String name)
自己加载进来。用户完全不知道具体的情况。
2)如果把xml放在了jar包中“藏起来”,实际上原来带来的动态扩展的特性,
也就没有那么明显了。如何解决呢
细细想来,这个问题的关键在于,所有的服务实例对象的创建和注册都是在
主控程序中通过xml来完成的。如果可以把这个注册和实例化的过程从主控程序
中分离出来,通过每个服务实例对象自动注册来完成,那么才算是真正的可拓展的。
如果需要完成新的功能,只需要把新的服务对象Class发布,重新运行主控程序就会实现新的功能。(看起来就和Eclipse一样)
这真是一个不错的想法,但是怎么做呢?
看看Eclipse如何做的。
http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
首先要有一个规定的plugin deploy目录,这样主程序才知道从哪里加载。
要有一个plugin.xml文件描述这个plugin.这着文件中有属性:class="foo.bar.Plugin">
看上去和我们原来做的方式一样啊。但是它是如何把这个目录下的plugin都加载的呢?
(我没有看过Eclipse的源码,不知道他是怎么写的)
再想想,其实主要要解决的问题是不通过主框架程序注册服务实现,
应该由服务程序自己注册上来。按照这个思路,我想有两种解决方案。
1)服务接口添加registerService 方法。
* 在jar的METATINFO文件中定义类名。
* 从特定的目录中读取jar/class文件。
* 通过URLClassLoader.newInstance()加载
* 加载后把ServiceLoader作为参数出入 service.registerSevice()中
* service对象完成自己的注册。
2)服务添加static代码端在类被实例化的时候自动完成注册。
* 加载之前和上一个方法一样。
* 对SeviceLoader对象实现为单态的模式。提供静态的注册方法。
* 在servie对象中实现如下的代码段完成自动注册。
static
{
ServiceLoader.registerService(new service());
}
这样看来总算OK了吧。