所有的plugin模块都应该实现一个ModuleInterface接口:
public interface ModuleInterface
{
???public boolean handles(Object key);
}
这意味着所有的模块都有一个handles方法,这个方法提供了一个挑选模块的机制,根据传入的对象识别模块,例如传入一个String,根据这个String判断这是不是一个适用的模块然后返回true或false。
?
每个模块都应该在jar文件的meta-inf目录下的元数据文件中描述自己。例如在一个module.mf中:
module.name=Module1
module.type=TestInterface
module.class=TestModuleClass
module.type指明了这个模块的种类(TestInterface是一个接口或一个类,指明这个模块是哪一种模块,例如它是过滤器、协议、界面或功能扩展等等),它必须implements了ModuleInterface接口,module.class是该模块具体的类。在这个例子中module.type是应用程序的一部分,而module.class就是各个作为plugin的jar中的一部分了。
?
ModuleScanner用于搜索一个或多个指定目录,查找所有模块的jar文件,并从jar文件中获得模块的描述信息,然后用模块的文件引用和描述信息构造一个ModuleSpec对象,这样每一个ModuleSpec对象就对应一个模块,ModuleScanner维护着一个列表,保存了所有的ModuleSpec对象。
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.zip.*;
import java.util.jar.*;
?
public class ModuleScanner
{
? protected List moduleSpecList = new ArrayList();
?
? public void scan(File[] paths) throws IOException{
????? for(int i=0; i
??????? scan(paths[i]);
????? }
? }
?
? public void scan(File path) throws IOException{
??? File[] list = path.listFiles();
??? for(int i =0; i
????? File file = list[i];
????? if(file.isFile() && file.getName().toLowerCase().endsWith(".jar")){
??????? System.out.println(file.getName());
??????? Properties props = readModuleManifest(file);
??????? if(props == null) continue;
??????? ModuleSpec spec = new ModuleSpec( file.toURL(),props);
??????? moduleSpecList.add(spec);
??????? System.out.println(spec);
??? ??}
??? }
? }
?
? protected Properties readModuleManifest(File file) throws IOException{
??? JarFile jar = new JarFile(file);
??? JarEntry entry = findModuleEntry(jar,"meta-inf/module.mf");
??? if(entry == null) return null;
??? InputStream input = jar.getInputStream(entry);
??? Properties props = new Properties();
??? props.load(input);
??? input.close();
??? return props;
? }
?
? protected JarEntry findModuleEntry(JarFile jar,String expect){
??? Enumeration enum = jar.entries();
??? while(enum.hasMoreElements()){
????? JarEntry entry = (JarEntry)enum.nextElement();
????? String name = entry.getName();
????? if(name.equalsIgnoreCase(expect)){
??????? return entry;
????? }
??? }
??? return null;
? }
?
? public ModuleSpec[] getModuleSpecs(){
??? int size = moduleSpecList.size();
??? ModuleSpec[] specs = new ModuleSpec[size];
??? for(int i =0; i
????? specs[i] = (ModuleSpec)moduleSpecList.get(i);
??? }
??? return specs;??
? }
}
?
ModuleSpec类根据模块信息创建模块实例,它通过两个参数构造,一个是jar文件的File引用,一个是包含了module.mf中数据的Properties。然后根据这两个参数提供的信息通过ModuleClassLoader装载类创建实例。
?
ModuleClassLoader从jar文件中装载类,它从标准的URLClassLoader派生。
?
ModuleRegistry封装了所有的模块的ModuleSpec对象,为用户和一组ModuleSpec对象提供了一个高效的映射关系。
?
最后在ModuleFactory类中,ModuleScanner收集模块,将其注册到ModuleRegistry中,在getInstance中遍历从ModuleRegistry中获得的模块实例,调用每一个模块实例的handles方法来寻找合适的模块。
import java.io.*;
import java.net.*;
import java.util.*;
?
public class ModuleFactory
{
? protected static final ModuleFactory factory = new ModuleFactory();
?
? protected ModuleRegistry registry = new ModuleRegistry();
? protected ModuleScanner scanner = new ModuleScanner();
?
? public void loadModules(File path) throws IOException{
??? scanner.scan(path);
??? register(scanner.getModuleSpecs());
? }
?
? public void loadModules(File[] paths) throws IOException{
??? scanner.scan(paths);
??? register(scanner.getModuleSpecs());
? }
?
? public void register(ModuleSpec spec){
??? registry.registerModule(spec);
? }
?
? public void register(ModuleSpec[] specs){
??? for(int i =0; i
????? registry.registerModule(specs[i]);
??? }
? }
?
? public Object getInstance(Class type, Object key){
??? List modules = registry.getModulesForType(type);
??? int count = modules.size();
??? for(int i=0; i
????? ModuleSpec spec = (ModuleSpec)modules.get(i);
????? ModuleInterface module = spec.getInstance();
????? if(module.handles(key)) return module;
??? }
??? return null;
? }
?
? public static ModuleFactory getInstance(){
??? return factory;
? }
?
?