一個項目,
有一些配置,
不想寫死,想用一個配置文件管理,需要讀寫這個配置文件。
寫完了之后,看著一大堆代碼,進入了反思,“我是不是自我矛盾了,說了不想寫死,還是寫了一個死的配置文件讀寫類,能不能通用一點呢,能不能搞個單例模式控制一下全局訪問點呢,……“
肯定能,通用的單例實現(xiàn),我見過,懶得寫了,直接網(wǎng)上搜索了一下 :) 。
通用呢,我也不想太發(fā)散,做了如下約定:
配置文件以json文件存放在App_Data文件夾下(我現(xiàn)在做的是ASP.NET MVC項目,其它項目,后續(xù)再適當調(diào)整吧,大同小異),
然后,每個配置文件名,就用配置類的類名。
基本上整體代碼都沒什么問題,只有一個問題,讓我搞了關天,那就是:
之前,我把初始化的代碼,即對json配置文件反序列化的代碼寫在配置類實例化后就會執(zhí)行的地方(比如默認構(gòu)造函數(shù)內(nèi),或者是會由默認構(gòu)造函數(shù)調(diào)用的其它函數(shù)內(nèi)),會導到JSON反序列化時,發(fā)生異常,
進入一個死循環(huán)”實例化,初始化,反序列化,……“。
后來我把這個初始化的代碼單獨拿出來,在單例實例化之后調(diào)用,而不是具體配置類實例化的地方調(diào)用,從而解決了這個問題。
看著這些文字,有點暈,算了,直接上代碼吧。
//用于實現(xiàn)通用單例和隔離類初始化方法的代碼
/// <summary>
/// 不支持非公共的無參構(gòu)造函數(shù)的
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class BaseInstance1<T> where T : class, new()
{
private readonly static object lockObj = new object();
private static T instance = null;
public static T Instance
{
get
{
if (instance == null)
{
lock (lockObj)
{
if (instance == null)
{
instance = new T();
}
}
}
return instance;
}
}
}
/// <summary>
/// 支持非公共的無參構(gòu)造函數(shù)的
/// </summary>
/// <typeparam name="T"></typeparam>
public class BaseInstance2<T> where T : class, IInitable //new(),new不支持非公共的無參構(gòu)造函數(shù)
{
/*
* 單線程測試通過!
* 多線程測試通過!
* 根據(jù)需要在調(diào)用的時候才實例化單例類!
*/
private static T _instance;
private static readonly object SyncObject = new object();
public static T Instance
{
get
{
if (_instance == null)//沒有第一重 singleton == null 的話,每一次有線程進入 GetInstance()時,均會執(zhí)行鎖定操作來實現(xiàn)線程同步,
//非常耗費性能 增加第一重singleton ==null 成立時的情況下執(zhí)行一次鎖定以實現(xiàn)線程同步
{
lock (SyncObject)
{
if (_instance == null)//Double-Check Locking 雙重檢查鎖定
{
//_instance = new T();
//需要非公共的無參構(gòu)造函數(shù),不能使用new T() ,new不支持非公共的無參構(gòu)造函數(shù)
_instance = (T)Activator.CreateInstance(typeof(T), true); //第二個參數(shù)防止異常:“沒有為該對象定義無參數(shù)的構(gòu)造函數(shù)。”
_instance.Init();
}
}
}
return _instance;
}
}
public static void SetInstance(T value)
{
_instance = value;
}
}
public interface IInitable
{
/// <summary>
/// 帶有初始化方法
/// </summary>
void Init();
}
//配置基類
/// <summary>
/// 基礎配置類,定義了配置讀取的通用操作。
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class BaseConfig<T> : BaseInstance2<T> where T : class , IInitable //, new()
{
private string filepath = null;
private ILog logger = null;
/// <summary>
///
/// </summary>
public BaseConfig()
{
filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", $"{typeof(T).Name}.json");
}
/// <summary>
/// 初始化
/// </summary>
public virtual void Init()
{
try
{
logger = LoggerConfig.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
//read config file
if (File.Exists(filepath))
{
var josnstr = File.ReadAllText(filepath, System.Text.Encoding.UTF8);
var cfg = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(josnstr);
Clone(cfg);
}
}
catch (Exception ex)
{
logger.Error("讀取SysConfig配置文件時報錯", ex);
}
}
private void Clone(T config)
{
var props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var p in props)
{
p.SetValue(this, p.GetValue(config));
}
}
/// <summary>
/// 保存修改
/// </summary>
public void Save()
{
try
{
//if (File.Exists(filepath))
{
string jsonstr = Newtonsoft.Json.JsonConvert.SerializeObject(this);
File.WriteAllText(filepath, jsonstr);
}
}
catch (Exception ex)
{
logger.Error("保存配置文件時報錯", ex);
}
}
}
/// <summary>
/// 系統(tǒng)配置
/// </summary>
public class SysConfig : BaseConfig<SysConfig>, IInitable
{
private SysConfig() { }
public override void Init()
{
base.Init();
}
/// <summary>
/// 配置字段1
/// </summary>
public int XXXCount { get; set; } = 5;
/// <summary>
/// 配置字段2
/// </summary>
public int YYYCount { get; set; } = 6;
}
//測試代碼
public ActionResult ShowSysConfig()
{
//var cfg1 = new SysConfig();
var cfg = SysConfig.Instance;
var jsonstr = Newtonsoft.Json.JsonConvert.SerializeObject(cfg);
return Content(jsonstr, "application/json");
}
public ActionResult ChangeSysConfig()
{
var cfg = SysConfig.Instance;
cfg.XXXCount += 1;
cfg.YYYCount += 1;
SysConfig.Instance.Save();
return ShowSysConfig();
}
浙公網(wǎng)安備 33010602011771號