Reface.AppStarter 向应用层提供以下几项 核心 功能
- 以模块化组织你的应用程序
- 自动注册组件至 IOC 容器
- 自动映射配置文件至配置类
- 在模块定义类中额外追加组件至 IOC 容器
- 在模块定义类中额外追加配置类
- 事件总线
这些功能允许让开发者将功能拆分至各个小粒度模块,
当使用某个模块时,只需要向模块添加一个依赖,即可开启所有功能,
实现了整个模块的开箱即用。
本文将介绍 Reface.AppStarter 中最常用的三个功能 自动注册组件至 IOC 容器 、 自动映射配置文件 和 模块化。
自动注册组件至 IOC 容器
使用 IOC 容器 往往分为两个阶段
- 配置阶段
- 使用阶段
在 配置阶段 我们可能会选择 配置文件 、对所有依赖的程序集反射 、 对指定程序反射 、 硬编码 等方式对组件进行 注册 。
对所有依赖的程序集反射 ,怎么看都是一种即笨重又有些呆板的方法。
然而另外几个方案在多模块化的项目里中 ,
也无法很好的工作,
它们各自需要在系统启动时,明确一些信息 :
- 一共需要读取哪些配置文件,这些文件在各个子模块的哪儿
- 一共需要对哪些程序集进行反射
- 那些负责硬编码注册的类型都在哪儿
如果上述的信息没有被收集完整,那么程序会因为没有注册全部组件而无法正确运行。
事实上
在大多数情况下,开发者在编写一个 接口 和一个 实现 时,就已经确定 注册关系 。
只有那些可能面临 策略模式 ,或者存在多客户端适配的情况下,这种 注册关系 无法确定。
所以通过 Attribute 加 扫描,是一种很好的 自动化注册 手段。
假定我们需要实现下面的接口
// Services/IUserService.cs
public interface IUserService
{
void CreateUser(User user);
}
实现过程大致如下
// Services/DefaultUserService.cs
public class DefaultUserService : IUserService
{
private readonly IRepository<User> userRepo ;
public DefaultUserService(IRepository<User> userRepo)
{
this.userRepo = userRepo;
}
public void CreateUser(User user)
{
this.userRepo.Insert(user);
}
}
使用 Reface.AppStarter ,你不再需要编写额外的代码将这个 DefaultUserService 注册到 IUserService 上。
你只需要为 DefaultUserService 添加一个 ComponentAttribute 。
// Services/DefaultUserService.cs
[Component]
public class DefaultUserService : IUserService
{
// ...
}
这个 特征 就是通知 Reface.AppStarter 将该类型注册到 IOC 容器 中,并注册到它所有实现的接口类型上。
在由 Reface.AppStarter 构建的系统中,程序不是单纯的由 程序集 组成,而是由 应用模块 组成。
应用模块 不仅包含了 程序集 原有的功能,还能够体系与其它 应用模块 的依赖关系。
现在我们为刚才 DefaultUserService 所在的 程序集 编写一个 应用模块 的类型。
// UserAppModule.cs
[ComponentScanAppModule]
public class UserAppModule : AppModule
{
}
这就是一个 用户应用模块 ( 所有的 应用模块 应当从 AppModule 继承 ),
它依赖了一个 ComponentScanAppModule 的 应用模块 。
ComponentScanAppModule 会将 目标应用模块 中标记了 ComponentAttribute 的类型注册到 IOC 容器中。
运行程序
public static void Main(String[] args)
{
var app = AppSetup.Start<UserAppModule>();
IUserService service = app.CreateComponent<IUserService>();
// you can use service now;
}
除了直接启动 UserAppModule ,
也可以将 UserAppModule 加到别的 应用模块 上,这个 自动注册 同样有效。
基于这样的方式, UserAppModule 完全可以开箱即用。
也可以说,任何一个 应用模块 都可以开箱即用。
自动映射配置文件
配置文件总是存在的, .NetFramework 提供了一整套 Configuration 方案。
但是这个方案略显笨重,并且在配置过程完全没有提示,必须依赖大量的文档说明。
很明显 JSON 比 XML 轻巧很多,
并且还有 JsonSchema 可以向 IDE 提供提示功能。
所以 Reface.AppStarter 选择了 JSON 作为配置文件的格式 ,
Reface.AppStarter 会读取一个包含了所有配置的 JSON 文件,
将配置中的值映射的 对应 的类型和属性中,
并将这些 类型 以单例的模式注册到 IOC 容器中。
以 UserAppModule 为例,我们为其开发一个 配置类,
配置类 就是 Reface.AppStarter 在解析 JSON 文件后反序列化的目标类型。
为一个类型添加 ConfigAttribute 就会通知 Reface.AppStarter : "嘿,这有一个配置类"。
下面的例子表示,我们可以通过配置文件指定 根用户的信息
// Configs/UserConfig
[Config("User")]
public class UserConfig
{
public string RootUserName { get; set; } = "admin";
public string RootUserPassword { get; set; } = "888888";
}
ConfigAttribute 的构造函数中指定的 User 表示从配置文件的 User 节点中读取配置,配置文件大至如下:
{
"User" : {
"RootUserName" : "ROOT",
"RootUserPassword" : "123456789"
}
}
如果没有配置文件,或者配置文件中没有编写 User 则会使用属性中指定的默认值将配置类实例注册到 IOC 容器 中。
通过构造函数注入配置类就可以使用它们。
// Services/DefaultUserService.cs
public class DefaultUserService : IUserService
{
private readonly IRepository<User> userRepo;
private readonly UserConfig userConfig;
public DefaultUserService(IRepository<User> userRepo, UserConfig userConfig)
{
this.userRepo = userRepo;
this.userConfig = userConfig;
}
public void CreateUser(User user)
{
if(user.Name == userConfig.RootUserName)
throw new SomeException("无效的用户名,不能创建该用户");
this.userRepo.Insert(user);
}
}
与 ComponentScanAppModule 一样,需要为 UserAppModule 添加一样依赖来开启 自动映射配置文件 的功能。
// UserAppModule.cs
[ComponentScanAppModule] // 开启自动扫描注册组件
[AutoConfigAppModule] // 开启自动映射配置文件
public class UserAppModule : AppModule
{
}
现在启动 UserAppModule
或者将 UserAppModule 添加到其它 应用模块 的依赖项中,
这些功能都会被启用。
AppSetup.Start() 默认读取 app.json 文件,
若配置文件不存在,则直接使用 配置类 的默认属性值。
关于配置智能提示
通过 AppSetup.Start<T>() 运行程序后,会在输出目录下产生一个 app.schema.json 文件。
为你的 app.json 添加 $schema 属性,值指向 app.schema.json 文件,就可以得到智能提示功能:
你也可以通过上图显示的属性关闭生成 app.schema.json 的功能。
相关链接