Reface.AppStarter 基本示例
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 的功能。
相关链接
Reface.AppStarter 基本示例的更多相关文章
- Reface.AppStarter 框架初探
Reface.AppStarter 是一种基于 .NetFramework 的应用程序启动模式,使用该启动模式,你可以轻松的得到以下功能 : IOC / DI 自动注册与装配 简化配置 垂直模块化你的 ...
- Reface.AppStarter 类型扫描 —— 获得系统中所有的实体类型
类型扫描 是 Reface.AppStarter 提供的最基本.最核心的功能. AutoConfig , ComponentScan 等功能都是基于该功能完成的. 每一个使用 Reface.AppSt ...
- 事件总线功能库,Reface.EventBus 详细使用教程
Reface.AppStarter 中的事件总线功能是通过 Reface.EventBus 提供的. 参考文章 : Reface.AppStarter 框架初探 使用 Reface.EventBus ...
- Reface.NPI 方法名称解析规则详解
在上次的文章中简单介绍了 Reface.NPI 中的功能. 本期,将对这方法名称解析规则进行详细的解释和说明, 以便开发者可以完整的使用 Reface.NPI 中的各种功能. 基本规则 方法名称以 I ...
- EF 太重,MyBatis 太轻,ORM 框架到底怎么选 ?
以 EF 为代表的基于 Linq 的 ORM 框架总是 很重. 他们的功能早已超出了一个 ORM 的范畴, ORM 是 Object Relational Mapping ,从名字上看,其初衷是将 数 ...
- 如何将 .NetFramework WebApi 按业务拆分成多个模块
在 .NetFramework 中使用 WebApi ,在不讨论 微服务 的模式下,大部分都是以层来拆分库的 : 基础设施 数据存储层 服务层 WeApi 层 一些其它的功能库 项目结构可能会像下面这 ...
- 代理模式是什么?如何在 C# 中实现代理模式
代理模式 并不是日常开发工作中常常用到的一种设计模式,也是一种不易被理解的一种设计模式.但是它会广泛的应用在系统框架.业务框架中. 定义 它的 定义 就如其它同大部分 设计模式 的定义类似,即不通俗也 ...
- Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)
本篇博客算是一个开头,接下来会持续更新使用Swift3.0开发服务端相关的博客.当然,我们使用目前使用Swift开发服务端较为成熟的框架Perfect来实现.Perfect框架是加拿大一个创业团队开发 ...
- .NET跨平台之旅:将示例站点升级至 ASP.NET Core 1.1
微软今天在 Connect(); // 2016 上发布了 .NET Core 1.1 ,ASP.NET Core 1.1 以及 Entity Framework Core 1.1.紧跟这次发布,我们 ...
随机推荐
- go 项目目录结构
网上有很多误人子弟的教程, 说项目下必须要有src, 傻逼玩意. 正确的路径应该是这样的: 所有go项目路径 src 项目1 项目2 项目N pkg bin 不是所有项目下必须建src, pk ...
- CSS中 隐藏元素的常用方法
在CSS中,使某个元素在页面中消失的方法有很多,今天为大家介绍几种我常用的方式 . 一.display:none; 让元素直接从页面消失,不占用尺寸,会改变页面布局. 代码演示: 页面演示:在页面 ...
- opencv C++ Mat构造函数
cv::Scalar scalar1(v); cv::Mat mat3(size,CV_8UC1,scalar1); std::cout<<mat3<<std::endl; s ...
- 我们是如何做go语言系统测试覆盖率收集的?
工程效能领域,测试覆盖率度量总是绕不开的话题,我们也不例外.在七牛云,我们主要使用go语言构建云服务,在考虑系统测试覆盖率时,最早也是通过围绕原生go test -c -cover的能力来构建.这个方 ...
- 深入理解Java闭包概念
闭包又称词法闭包 闭包最早定义为一种包含<环境成分>和<控制成分>的实体. 解释一:闭包是引用了自由变量的函数,这个被引用的变量将和这个函数一同存在. 解释二:闭包是函数和相关 ...
- 【题解】[USACO17JAN]Balanced Photo G
题目链接:https://www.luogu.com.cn/problem/P3608 方法一 用树状数组求逆序对先后扫两遍,一次从前往后,一次从后往前,算出每头奶牛左右两边比她高的数量. 最后统计一 ...
- eclipse在debug模式下鼠标移动到变量上不显示值的问题
在eclipse中调试时,鼠标移动到变量上不显示值,使用ctrl+shift+i,或者通过配置达到目的: Window->Preferences->Java->Editor-> ...
- javamelody对Java Application进行监控
前面写过对于webapp,用javamelody来监控.分析性能是挺方便的:那要对普通的java应用进行监控,只需要在应用上启动一个嵌入式web容器就可以了. javamelody里面的war包就用了 ...
- 【博弈】HDU - 5963 朋友
题目 B君在围观一群男生和一群女生玩游戏,具体来说游戏是这样的: 给出一棵n个节点的树,这棵树的每条边有一个权值,这个权值只可能是0或1. 在一局游戏开始时,会确定一个节点作为根.接下来从女生开始,双 ...
- JavaScript基础函数的声明(014)
1.函数的重要地位 函数(functions)在JavaScript里有着重要的地位,其原因有二: 它们是一种特殊的对象 它们提供作用域 说函数在JavaScript里是特殊的对象,因为: 程序的执行 ...