利用反射做插件式系统的一次实践
以前做了一个在服务器上定时清理文件的 Console App - 通过任务计划的方式运行。前不久又因为一个需求:每隔一段时间要自动生成某些 JS 文件,这两个站点放在同一个服务器上,任务计划的执行时间也是一样,加之之前一直在琢磨着怎么样做一个插件式系统,于是我决定试一下。
一个原则:主系统不依赖于子系统,子系统依赖于主系统---也就是说主系统不知道你子系统要干什么。
我的思路:通过配置文件,在主程序里定义一个接口 IPlugin,通过配置文件 + 反射得到接口的实例的方式实现。有了思路,先上图看看我建的工程样子:
其中: CS.Project 是将执行的主程序; CS.Plugins.FileApp 和 CS.Plugs.ForHtml 分别是A站点的文件清理程序和 B站点的 Html/JS 文件的生成程序,它们是插件。
主站点的 IPlugin 的定义如下:
一个原则:主系统不依赖于子系统,子系统依赖于主系统---也就是说主系统不知道你子系统要干什么。
我的思路:通过配置文件,在主程序里定义一个接口 IPlugin,通过配置文件 + 反射得到接口的实例的方式实现。有了思路,先上图看看我建的工程样子:
其中: CS.Project 是将执行的主程序; CS.Plugins.FileApp 和 CS.Plugs.ForHtml 分别是A站点的文件清理程序和 B站点的 Html/JS 文件的生成程序,它们是插件。
主站点的 IPlugin 的定义如下:
IPlugin
namespace CS.Project
{
/*************************************
* 插件所必须实现的接口
* ***********************************/
public interface IPlugin
{
void Execute();
}
}
{
/*************************************
* 插件所必须实现的接口
* ***********************************/
public interface IPlugin
{
void Execute();
}
}
CS.Plugins.FileApp 的 FileClean 和 CS.Plugs.ForHtml 的 CreateHtml 工程里需分别实现这个接口。
FileClean 文件清理
using System;
using CS.Project;
namespace CS.Plugins.FileApp
{
/*********************************
* 文件清理
* *******************************/
public class FileClean : IPlugin
{
public FileClean() {
Write("CS.Plugins.FileClean 被创建");
}
public void Execute() {
Write("CS.Plugins.FileClean.Execute 将被执行 ");
}
private void Write(string msg) {
Console.WriteLine("ProId: {0} , {1}", AppDomain.CurrentDomain.Id, msg);
}
}
}
using CS.Project;
namespace CS.Plugins.FileApp
{
/*********************************
* 文件清理
* *******************************/
public class FileClean : IPlugin
{
public FileClean() {
Write("CS.Plugins.FileClean 被创建");
}
public void Execute() {
Write("CS.Plugins.FileClean.Execute 将被执行 ");
}
private void Write(string msg) {
Console.WriteLine("ProId: {0} , {1}", AppDomain.CurrentDomain.Id, msg);
}
}
}
CreateHtml 创建 JS 及 HTML 文件
using System;
using CS.Project;
namespace CS.Plugs.ForHtml
{
/*****************************************
* 创建 JS 及 HTML 文件
* **************************************/
public class CreateHtml : IPlugin
{
public CreateHtml() {
Write("CS.Plugs.ForHtml.CreateHtml 实例创建");
}
public void Execute() {
Write("CS.Plugs.ForHtml.Execute 方法执行 ");
}
private void Write(string msg) {
Console.WriteLine("ProId: {0} , {1}", AppDomain.CurrentDomain.Id, msg);
}
}
}
using CS.Project;
namespace CS.Plugs.ForHtml
{
/*****************************************
* 创建 JS 及 HTML 文件
* **************************************/
public class CreateHtml : IPlugin
{
public CreateHtml() {
Write("CS.Plugs.ForHtml.CreateHtml 实例创建");
}
public void Execute() {
Write("CS.Plugs.ForHtml.Execute 方法执行 ");
}
private void Write(string msg) {
Console.WriteLine("ProId: {0} , {1}", AppDomain.CurrentDomain.Id, msg);
}
}
}
最后,配置文件如下:
Plugs.xml 配置文件
<?xml version="1.0" encoding="utf-8" ?>
<Plugins>
<add Assembly="CS.Plugins.FileApp" PluginsName="CS.Plugins.FileApp.FileClean" />
<add Assembly="CS.Plugs.ForHtml" PluginsName="CS.Plugs.ForHtml.CreateHtml" />
</Plugins>
<Plugins>
<add Assembly="CS.Plugins.FileApp" PluginsName="CS.Plugins.FileApp.FileClean" />
<add Assembly="CS.Plugs.ForHtml" PluginsName="CS.Plugs.ForHtml.CreateHtml" />
</Plugins>
主程序如下:
主程序
using System;
using System.Reflection;
using System.Linq;
using System.Xml.Linq;
namespace CS.Project
{
internal class Program
{
static void Main(string[] args)
{
doWork();
Console.ReadKey();
}
static void doWork() {
// 从 配置文件 中读取相关
XDocument doc = XDocument.Load("Plugs.xml");
var plugs = from config in doc.Descendants("Plugins").Elements("add")
select new
{
assemblyName = config.Attribute("Assembly"),
PluginsName = config.Attribute("PluginsName")
};
foreach (var item in plugs)
{
Assembly assembly = Assembly.Load(item.assemblyName.Value);
IPlugin instance = assembly.CreateInstance(item.PluginsName.Value) as IPlugin;
//根据配置文件获取实例,这与以下获取实例的方法相同
//Type type = assembly.GetType(item.PluginsName.Value);
//IPlugin instance = Activator.CreateInstance(type) as IPlugin;
if (instance != null)
instance.Execute();
}
}
}
}
using System.Reflection;
using System.Linq;
using System.Xml.Linq;
namespace CS.Project
{
internal class Program
{
static void Main(string[] args)
{
doWork();
Console.ReadKey();
}
static void doWork() {
// 从 配置文件 中读取相关
XDocument doc = XDocument.Load("Plugs.xml");
var plugs = from config in doc.Descendants("Plugins").Elements("add")
select new
{
assemblyName = config.Attribute("Assembly"),
PluginsName = config.Attribute("PluginsName")
};
foreach (var item in plugs)
{
Assembly assembly = Assembly.Load(item.assemblyName.Value);
IPlugin instance = assembly.CreateInstance(item.PluginsName.Value) as IPlugin;
//根据配置文件获取实例,这与以下获取实例的方法相同
//Type type = assembly.GetType(item.PluginsName.Value);
//IPlugin instance = Activator.CreateInstance(type) as IPlugin;
if (instance != null)
instance.Execute();
}
}
}
}
编译,运行,其结果如下:
Oh, Yeah! 成功了!
后续的几点思考:
1. 多线程场景下如何实现;
2.如果是较大型复杂的系统中,权限的控制如何实现?
我的一点体会: 插件式系统方式的开发在后续的开发中应该逐渐地会成为主流, 在业务系统的开发中挺考验人的。