首先,我们先看看QT的插件系统。
QT的插件模型类似于在COM本质论前面部分内容里描述的模型(不过还没有去看具体的源码,实现机制是否一样还不确定)。动态链接库通过继承一个简单接口的纯虚类,在需要的时候动态载入,然后通过纯虚类的接口函数进行进一步的访问。从而为动态链接库提供一个统一的发现方式。
在QT的插件系统中,提供给对外使用的主要有IPlugin、PluginManager、PluginSpec这三个类,PluginView、PluginErrorView、PluginDetailsView三个辅助类。我们主要研究三个主要类IPlugin、PluginManager、PluginSpec。这三个类的具体实现由IPluginPrivate、PluginManagerPrivate、PluginSpecPrivate三个类实现,目的是对外屏蔽实现的具体细节。
1、IPlugin,IPluginPrivate 插件接口类
IPlugin类为插件的接口类(纯虚类),其作用是为动态链接库导出类提供统一的发现接口,从而达到插件的目的。
每个插件都需要至少提供initialize(const QStringList &arguments, QString *errorString)和extensionsInitialized()两个函数,为插件类提供初始化功能。
其中addObject、addAutoReleasedObject、removeObject三个函数为插件类的实例化提供计数功能。
2、PluginSpec、PluginSpecPrivate 插件说明类
插件说明类以插件的说明信息提供服务,包括名字、版本、位置、依赖关系,参数、状态等等。
3、PluginManager、PluginManagerPrivate 插件管理类
插件管理类提供对插件的管理功能,为我们这次主要研究的类。
PluginManager类对外提供的函数主要分为三类:对象池操作、插件操作、和对于参数的操作。
对于对象池的操作有addObject()添加对象、removeObject()移除对象、allObjects()获取所有对象、getObjects()获取某个类的所有对象、getObject()获取某个类的第一个对象。在getObjects()和getObject()中使用了Aggregation类的查询操作。并且在对象添加和移除后引发objectAdded和aboutToRemoveObject信号。
对于插件的操作主要有loadPlugins()载入插件、setPluginPaths()、pluginPaths()设置和获取插件路径、plugins()获取插件配置列表、setFileExtension()、fileExtension设置插件配置文件的类型。关于插件的操作是需要主要分析的对象,我按照在主程序中的操作依序分析相关的函数。
3.1、void setFileExtension(const QString &extension) 设置插件配置文件类型
void PluginManager::setFileExtension(const QString &extension) { d->extension = extension; }仅仅对细节实现类的extension插件配置文件类型属性进行了赋值。3.2、void setPluginPaths(const QStringList &paths) 设置插件路径列表void PluginManager::setPluginPaths(const QStringList &paths) { d->setPluginPaths(paths); }void PluginManagerPrivate::setPluginPaths(const QStringList &paths) { pluginPaths = paths; readPluginPaths(); }void PluginManagerPrivate::readPluginPaths() { qDeleteAll(pluginSpecs); pluginSpecs.clear(); QStringList specFiles; QStringList searchPaths = pluginPaths; while (!searchPaths.isEmpty()) { //找出所有的插件配置文件 const QDir dir(searchPaths.takeFirst()); const QFileInfoList files = dir.entryInfoList(QStringList() << QString("*.%1").arg(extension), QDir::Files); //后缀名为extension定义的文件 foreach (const QFileInfo &file, files) specFiles << file.absoluteFilePath(); const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot); foreach (const QFileInfo &subdir, dirs) searchPaths << subdir.absoluteFilePath(); //添加当前文件夹的子文件夹 } foreach (const QString &specFile, specFiles) { PluginSpec *spec = new PluginSpec; spec->d->read(specFile); //读取配置文件 pluginSpecs.append(spec); //添加到配置列表 } resolveDependencies(); //解析依赖关系 // ensure deterministic plugin load order by sorting qSort(pluginSpecs.begin(), pluginSpecs.end(), lessThanByPluginName); //按字母从小到大排序 emit q->pluginsChanged(); }可以看出在设置插件路径的函数中,首先找出了所有的插件的配置文件,再添加到配置列表中,然后再解析其依赖关系。
在解析其依赖关系的函数中bool PluginSpecPrivate::resolveDependencies(const QList
&specs),会检查每个所依赖的所有的项目,如果有项目不存在,则会报错。 3.3、pluginManager.parseOptions(arguments, appOptions, &foundAppOptions, &errorMessage)解析启动参数
bool PluginManager::parseOptions(const QStringList &args, const QMapbool> &appOptions, QMap *foundAppOptions, QString *errorString) { OptionsParser options(args, appOptions, foundAppOptions, errorString, d); return options.parse(); }
OptionsParser 类解析具体的参数,并将对于各个插件的参数填入插件配置类的信息中。
3.4、pluginManager.loadPlugins() 载入插件
void PluginManagerPrivate::loadPlugins() { QListqueue = loadQueue(); foreach (PluginSpec *spec, queue) { loadPlugin(spec, PluginSpec::Loaded); //载入插件 } foreach (PluginSpec *spec, queue) { loadPlugin(spec, PluginSpec::Initialized); //初始化插件 } QListIterator it(queue); it.toBack(); while (it.hasPrevious()) { loadPlugin(it.previous(), PluginSpec::Running);//运行插件 } emit q->pluginsChanged(); } void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destState) { if (spec->hasError()) return; if (destState == PluginSpec::Running) { spec->d->initializeExtensions(); //运行插件 return; } else if (destState == PluginSpec::Deleted) { spec->d->kill(); return; } //检查插件的依赖项 foreach (PluginSpec *depSpec, spec->dependencySpecs()) { if (depSpec->state() != destState) { spec->d->hasError = true; spec->d->errorString = PluginManager::tr("Cannot load plugin because dependency failed to load: %1(%2)/nReason: %3") .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString()); return; } } if (destState == PluginSpec::Loaded) spec->d->loadLibrary(); else if (destState == PluginSpec::Initialized) spec->d->initializePlugin(); else if (destState == PluginSpec::Stopped) spec->d->stop(); }对插件执行了加载、初始化、运行三个操作。对这三个操作具体分析。加载:bool PluginSpecPrivate::loadLibrary() { //一些检查和插件的动态链接库的名字的形成 .... PluginLoader loader(libName); if (!loader.load()) { ....} //插件载入得到插件接口类 IPlugin *pluginObject = qobject_cast(loader.instance()); if (!pluginObject) { .... } state = PluginSpec::Loaded; plugin = pluginObject; plugin->d->pluginSpec = q; //设置插件的配置文档 return true; }初始化:bool PluginSpecPrivate::initializePlugin() { ....//一些检查 if (!plugin->initialize(arguments, &err)) {//带参数的初始化 ... } state = PluginSpec::Initialized; return true; }其中插件接口类的initialize函数为纯虚函数,由每个具体的接口实现。运行:bool PluginSpecPrivate::initializeExtensions() { ...//一些检查 if (!plugin) { ... } plugin->extensionsInitialized(); //运行 state = PluginSpec::Running; return true; }其中插件接口类的extensionsInitialized函数为纯虚函数,由每个具体的接口实现。