项目框架构建之6:编写通用主机基础类

news2024/11/29 0:45:52

本文是“项目框架构建”系列之6,本文介绍如何编写通用主机基础类。

1.为了构建通用主机,我们先创建主机接口IAppHost接口

接口需要有配置项,我们定义为HostConfiguration,比如我们希望用户可以设定他的工作目录,就可以放在这里


接口需要有加载的.json的应用程序配置IConfiguration对象
接口还需要有服务管理器IServiceProvider对象
以及接口需要有日志管理ILoggerManager对象


2.构建主机,需要执行方法,所以我们添加了Build()和Run()两个方法
Build()方法是为了生成主机,这是必要方法。
Run()方法是为了启动应用程序,但这并不是必要的,因为主机生成后,您本身可以随时随地去启动你的程序。


3.生命周期以及事件

像主机这种底层结构,为了兼顾扩展性,我们需要提供周期的事件,以便让用户在启动的时候进行参与。


本程序抛砖引玉,提供了4个事件:
HostConfiguring:正在配置IConfiguration中,您可以继续加入其它初始化工作,如加载其它的.json文件
HostConfigurationInitialized:主机的配置已完成且抽象接口IConfiguration已构建
HostServicesInitializing:主机正在构建服务中,您可以继续加入其它服务一起构建
HostReady:主机构建完成已准备就绪


4.扩展依赖注入

为了防止重复添加服务,我们还需要扩展新增一些方法,用于防止重复添加服务。


5.扩展appsettings.cs文件的内容更改

程序运行后,有时可能需要在运行中修改appsettings.json中的配置,但IConfiguration本身不带有这种功能,所以我们需要自行编写扩展


6.编写主机接口IAppHost的抽象实现

我们是通用框架项目,所以需要提供一个抽象的基础实现,依据之前的设计,我们实现它的方法、事件、属性。

我们限定构造函数


程序初始化,初始化应用程序配置以及基础服务


编写Build()方法实现主机的生成


由于Run()函数不是必须的,所以我们把它定义为虚方法


 

下面通过编写事件的虚方法,以便子类能够参与过程

最后,附上完整的主机项目结构图:

也许各位朋友会想要源码,不要慌,等系列文章差不多了后,会开放源码下载。现在也没多少代码,没啥看头的。

下面附上IAppHost的具体实现:

using System;
using System.IO;
using Xejen.Hosting.Extensions;
using Xejen.Logger;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Xejen.Hosting
{
    /// <summary>
    /// <inheritdoc cref="IAppHost"/>基类 
    /// </summary>
    /// <creator>marc</creator>
    public abstract class AppHostBase : IAppHost
    {
        private readonly ILogger _logger;
        private readonly ILoggerManager _loggerManager;
        /// <inheritdoc cref="ILogger"/>
        protected ILogger Logger => _logger;

        /// <summary>
        /// 服务列表
        /// </summary>
        internal IServiceCollection Services;

        /// <inheritdoc cref="HostConfiguration"/>
        public HostConfiguration HostConfig { get; private set; }
        /// <inheritdoc/>
        public IConfiguration Configuration { get; private set; }
        /// <inheritdoc/>
        public IServiceProvider ServiceProvider { get; private set; }
        /// <inheritdoc/>
        public ILoggerManager LoggerManager => _loggerManager;

        /// <inheritdoc/>
        public event EventHandler<IConfigurationBuilder> HostConfiguring;
        /// <inheritdoc/>
        public event EventHandler<IConfiguration> HostConfigurationInitialized;
        /// <inheritdoc/>
        public event EventHandler<IServiceCollection> HostServicesInitializing;
        /// <inheritdoc/>
        public event EventHandler HostReady;

        /// <inheritdoc cref="AppHostBase"/>
        /// <param name="args">启动参数</param>
        /// <param name="loggerManager">采用的日志方式</param>
        protected AppHostBase(string[] args, ILoggerManager loggerManager) : this(loggerManager)
        {
            HostConfig = HostConfiguration.Load(args);

            Initialize();
        }

        /// <summary>
        /// 将 ILoggerManager 添加到构造函数中
        /// </summary>
        /// <param name="loggerManager"><inheritdoc cref="ILoggerManager" path="/summary"/></param>
        private AppHostBase(ILoggerManager loggerManager)
        {
            Check.NotNull(loggerManager, nameof(loggerManager));

            _loggerManager = loggerManager;
            _logger = _loggerManager.CreateLogger(typeof(AppHostBase));
        }

        /// <inheritdoc cref="AppHostBase"/>
        /// <param name="configuration"><inheritdoc cref="HostConfiguration" path="/summary"/></param>
        /// <param name="loggerManager"><inheritdoc cref="ILoggerManager" path="/summary"/></param>
        protected AppHostBase(HostConfiguration configuration, ILoggerManager loggerManager) : this(loggerManager)
        {
            Check.NotNull(configuration, nameof(configuration));

            HostConfig = configuration;

            Initialize();
        }

        private void Initialize()
        {
            _logger.LogInformation($"应用程始启动,开始初始化");

            InitializeConfiguration(HostConfig.ConfigDirectory);
            InitializeServices();
        }

        /// <inheritdoc/>
        public IAppHost Build()
        {
            ConfigureServices(Services);

            ServiceProvider = Services.BuildServiceProvider();

            _logger.LogInformation($"服务初始化完毕,共 {Services.Count} 项服务");

            _logger.LogInformation($"程序初始化完毕");

            OnHostReady(this);

            return this;
        }

        /// <inheritdoc/>
        public virtual void Run() { }

        private void InitializeConfiguration(string configDirectory)
        {
            _logger.LogInformation($"准备加载配置文件");
            if (string.IsNullOrEmpty(configDirectory))
            {
                configDirectory = AppDomain.CurrentDomain.BaseDirectory;
            }

            string appsettingFileName = HostConfig.DefaultSettingsFileName;
            if (!File.Exists(Path.Combine(configDirectory, appsettingFileName)))
            {
                throw new FileNotFoundException($"未找到 {appsettingFileName} 文件,查找目录在:\r\n{configDirectory}");
            }

            var builder = new ConfigurationBuilder()
                .SetBasePath(configDirectory)
                .AddJsonFile(appsettingFileName, optional: true, reloadOnChange: true);

            ConfigureJsonFiles(builder);

            OnHostConfiguring(this, builder);

            Configuration = builder.Build();

            OnHostConfigurationInitialized(this, Configuration);

            _logger.LogInformation($"配置文件加载完成,工作目录: {configDirectory}");
        }

        /// <summary>
        /// 配置json文件,基类已自动完成<see langword="appsettings.json"/>文件的加载,您只需加载您其它的配置文件即可
        /// </summary>
        /// <param name="configuration">传入过来的<see cref="IConfigurationBuilder"/>接口对象</param>
        protected virtual void ConfigureJsonFiles(IConfigurationBuilder configuration)
        {

        }

        /// <summary>
        /// 初始化基础服务
        /// </summary>
        private void InitializeServices()
        {
            _logger.LogInformation($"准备初始化服务");
            var services = new ServiceCollection();
            Services = services;

            // 添加服务
            // services.AddTransient<IService, ServiceImplementation>();
            // 添加主机服务:public class AppHostedService : IHostedService,该类要实现StartAsync以及StopAsync方法
            // services.AddHostedService<AppHostedService>();

            services.AddSingleton(_loggerManager);
            services.AddSingleton(Configuration);
            services.AddSingleton(HostConfig);

            OnHostServicesInitializing(this, services);
        }

        /// <summary>
        /// 配置服务,各子项目可以重写此方法来注册各项服务
        /// </summary>
        /// <param name="services">服务集合</param>
        protected virtual void ConfigureServices(IServiceCollection services)
        {

        }

        /// <inheritdoc cref="HostConfiguring" path="/summary"/>
        /// <param name="sender"><inheritdoc cref="IAppHost" path="/summary"/></param>
        /// <param name="builder">用于构建应用程序配置的构建器</param>
        protected virtual void OnHostConfiguring(IAppHost sender, IConfigurationBuilder builder)
        {
            HostConfiguring?.Invoke(sender, builder);
        }

        /// <inheritdoc cref="HostConfigurationInitialized" path="/summary"/>
        /// <param name="sender"><inheritdoc cref="IAppHost" path="/summary"/></param>
        /// <param name="builder">应用程序配置</param>
        protected virtual void OnHostConfigurationInitialized(IAppHost sender, IConfiguration builder)
        {
            HostConfigurationInitialized?.Invoke(sender, builder);
        }

        /// <inheritdoc cref="HostServicesInitializing" path="/summary"/>
        /// <param name="sender"><inheritdoc cref="IAppHost" path="/summary"/></param>
        /// <param name="builder">服务列表</param>
        protected virtual void OnHostServicesInitializing(IAppHost sender, IServiceCollection builder)
        {
            HostServicesInitializing?.Invoke(sender, builder);
        }

        /// <inheritdoc cref="HostReady" path="/summary"/>
        /// <param name="sender"><inheritdoc cref="IAppHost" path="/summary"/></param>
        protected virtual void OnHostReady(IAppHost sender)
        {
            HostReady?.Invoke(sender, EventArgs.Empty);
        }
    }
}

祝您用餐愉快,下一篇我们将编写如何使用主机框架的实际应用,敬请期待

1-3-5 $ 3-5-5-4 带着田螺回四堡 3-5-2-4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1356634.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

改善 GitHub Pages 阅读体验:Quick Docs

一个不到 10MB 的小工具&#xff0c;来提供本地、快速的文档访问&#xff0c;来改善开发过程中&#xff0c;阅读在线文档体验糟糕的问题。 以及&#xff0c;介绍如何快速制作一个利于分发使用的&#xff0c;离线文档工具包。 写在前面 即使现在 AI 辅助编码和 Chat Bot 类的…

docker容器添加新的端口映射

通常在运行容器时&#xff0c;我们都会通过参数 -p来指定宿主机和容器端口的映射&#xff0c;例如 docker run -it -d --restart always --name [指定容器名] -p 8899:8080 [指定镜像名]上述命令将容器内的8080端口映射到宿主机的8899端口。 参数说明 -d 表示后台运行容器 -t…

基于注解的IOC配置

基于注解的IOC配置 学习基于注解的IOC配置&#xff0c;大家脑海里首先得有一个认知&#xff0c;即注解配置和xml配置要实现的功能都是一样的&#xff0c;都是要降低程序间的耦合。只是配置的形式不一样。 1.创建工程 pom.xml <?xml version"1.0" encoding&qu…

Apache Doris (六十): Doris - 物化视图

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录

【创建VirtualBox虚拟机并安装openEuler20.03 TLS SP1操作系统】

创建VirtualBox虚拟机并安装openEuler20.03 TLS SP1操作系统 一、环境说明二、安装过程 一、环境说明 虚拟机软件&#xff1a;Virtualbox操作系统&#xff1a;openEuler 20.03 TLS SP1&#xff08;x86&#xff09; 二、安装过程 创新虚拟机 修改虚拟机配置&#xff1a; …

软件测试题常见版

1、python深浅拷贝 浅拷贝&#xff0c;指的是重新分配一块内存&#xff0c;创建一个新的对象&#xff0c;但里面的元素是原对象中各个子对象的引用。深拷贝&#xff0c;是指重新分配一块内存&#xff0c;创建一个新的对象&#xff0c;并且将原对象中的元素&#xff0c;以递归的…

Python编程基础:顺序结构、循环结构、程序跳转语句、pass空语句

Python是一种简单而强大的编程语言&#xff0c;它提供了多种结构和语句&#xff0c;使得程序编写变得更加灵活和高效。在本文中&#xff0c;将介绍Python中的顺序结构、循环结构、程序跳转语句以及pass空语句&#xff0c;并解释如何正确使用它们。 目录 程序的描述方式自然语言…

vuepress2 打包后刷新页面侧边栏丢失问题

问题&#xff1a;打包后刷新页面时侧边栏丢失问题 原因&#xff1a;node版本问题 文档中写着依赖环境 Node.js v18.16.0 我当时的版本是 16.19.0 我应该算是遇到了两个问题 【刷新后侧边栏消失】【刷新后页面内容加载错误】 我看了控制台&#xff0c;侧边栏不出现的原因&a…

自定义事件

自定义事件 自定义事件 AAA"fn1"&#xff1a;向子组件的事件池中注入AAA事件&#xff0c;方法是父组件的fn1 发布订阅&#xff1a;子组件某个操作把父组件中的某个方法执行了 参数可以传多个 $listeners* $listeners&#xff1a;事件池中的方法 { aaa:fn1, bbb:fn2 }…

2023年山东省高职组区块链技术竞赛任务书

2023年山东省高职组区块链技术任务书 目录 模块一&#xff1a;区块链产品方案设计及系统运维 任务1-1&#xff1a;区块链产品需求分析与方案设计 任务1-2&#xff1a;区块链系统部署与运维 任务1-3&#xff1a;区块链系统测试 模块二&#xff1a;智能合约开发与测试 任务2-1&am…

加密算法和身份认证

前瞻概念 在了解加密和解密的过程前&#xff0c;我们先了解一些基础概念 明文&#xff1a;加密前的消息叫 “明文” &#xff08;plain text&#xff09;密文: 加密后的文本叫 “密文” (cipher text)密钥: 只有掌握特殊“钥匙”的人&#xff0c;才能对加密的文本进行解密,这里…

【三维分割】SAGA:Segment Any 3D Gaussians

系列文章目录 代码&#xff1a;https://jumpat.github.io/SAGA. 论文&#xff1a;https://jumpat.github.io/SAGA/SAGA_paper.pdf 来源&#xff1a;上海交大和华为研究院 文章目录 系列文章目录摘要一、前言二、相关工作1.基于提示的二维分割2.将2D视觉基础模型提升到3D3.辐射…

【网络安全】【密码学】常见数据加(解)密算法及Python实现(一)

一、Base64编码 1、算法简介 Base64是一种常见的编&#xff08;解&#xff09;码方法&#xff0c;用于传输少量二进制数据。该编码方式较为简短&#xff0c;并不具有可读性&#xff0c;对敏感数据可以起到较好的保护作用。 2、Python实现&#xff08;调库&#xff09; &…

LRU的设计与实现(算法村第五关黄金挑战)

146. LRU 缓存 - 力扣&#xff08;LeetCode&#xff09; 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存…

SSL/TLS 握手过程详解

SSL握手过程详解 1、SSL/TLS 历史发展2、SSL/TLS握手过程概览2.1、协商交换密码套件和参数2.2、验证一方或双方的身份2.3、创建/交换对称会话密钥 3、TLS 1.2 握手过程详解4、TLS 1.3 握手过程详解5、The TLS 1.2 handshake – Diffie-Hellman Edition 1、SSL/TLS 历史发展 可…

显示所有中国城市需要多少个汉字?

显示所有中国城市需要多少个汉字呢&#xff1f; 需要3678个汉字&#xff0c;看看我怎么知道的。 第一步&#xff1a;先找到中国的所有城市的名称 去哪里找到中国的所有城市的名称呢&#xff1f; 进入中国天气网&#xff1a;http://www.weather.com.cn/ 使用 F12 打开浏览器的调…

[Mac软件]Boxy SVG 4.20.0 矢量图形编辑器

Boxy SVG 是一款入门级矢量图形编辑器&#xff0c;具有全套基本功能、易于学习的选项卡式界面和可自定义的键盘快捷键。有了它&#xff0c;您可以轻松创建横幅、图标、按钮、图形、界面草图&#xff0c;甚至有趣的表情包。 编辑器支持使用多种工具创建和编辑矢量对象&#xff…

深入探讨关于Redis的底层

1.1为什么Redis存储比关系型数据库快&#xff1a; 数据存储在内存中&#xff08;比如企业项目中用户表中有一个亿的用户&#xff0c;如果再来注册一个用户&#xff0c;或者登录&#xff0c;必须先判断是否有这个数据&#xff0c;这个时候如果直接查询数据库的话&#xff0c;对服…

Java+springboot+vue智慧校园源码,数据云平台Web端+小程序教师端+小程序家长端

技术架构&#xff1a; Javaspringbootvue element-ui小程序电子班牌&#xff1a;Java Android演示自主版权。 智慧校园电子班牌人脸识别系统全套源码&#xff0c;包含&#xff1a;数据云平台Web端小程序教师端小程序家长端电子班牌 学生端。 电子班牌系统又称之为智慧班牌&am…

分布式之任务调度学习一

1 任务调度 1.1 什么时候需要任务调度&#xff1f; 1.1.1 任务调度的背景 在业务系统中有很多这样的场景&#xff1a; 1、账单日或者还款日上午 10 点&#xff0c;给每个信用卡客户发送账单通知&#xff0c;还款通知。如何判断客户的账单日、还款日&#xff0c;完成通知的发…