一个项目,
有一些配置,
不想写死,想用一个配置文件管理,需要读写这个配置文件。
写完了之后,看着一大堆代码,进入了反思,“我是不是自我矛盾了,说了不想写死,还是写了一个死的配置文件读写类,能不能通用一点呢,能不能搞个单例模式控制一下全局访问点呢,……“
肯定能,通用的单例实现,我见过,懒得写了,直接网上搜索了一下 :) 。
通用呢,我也不想太发散,做了如下约定:
配置文件以json文件存放在App_Data文件夹下(我现在做的是ASP.NET MVC项目,其它项目,后续再适当调整吧,大同小异),
然后,每个配置文件名,就用配置类的类名。
基本上整体代码都没什么问题,只有一个问题,让我搞了关天,那就是:
之前,我把初始化的代码,即对json配置文件反序列化的代码写在配置类实例化后就会执行的地方(比如默认构造函数内,或者是会由默认构造函数调用的其它函数内),会导到JSON反序列化时,发生异常,
进入一个死循环”实例化,初始化,反序列化,……“。
后来我把这个初始化的代码单独拿出来,在单例实例化之后调用,而不是具体配置类实例化的地方调用,从而解决了这个问题。
看着这些文字,有点晕,算了,直接上代码吧。
//用于实现通用单例和隔离类初始化方法的代码
/// <summary>
/// 不支持非公共的无参构造函数的
/// </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>
/// 支持非公共的无参构造函数的
/// </summary>
/// <typeparam name="T"></typeparam>
public class BaseInstance2<T> where T : class, IInitable //new(),new不支持非公共的无参构造函数
{
/*
* 单线程测试通过!
* 多线程测试通过!
* 根据需要在调用的时候才实例化单例类!
*/
private static T _instance;
private static readonly object SyncObject = new object();
public static T Instance
{
get
{
if (_instance == null)//没有第一重 singleton == null 的话,每一次有线程进入 GetInstance()时,均会执行锁定操作来实现线程同步,
//非常耗费性能 增加第一重singleton ==null 成立时的情况下执行一次锁定以实现线程同步
{
lock (SyncObject)
{
if (_instance == null)//Double-Check Locking 双重检查锁定
{
//_instance = new T();
//需要非公共的无参构造函数,不能使用new T() ,new不支持非公共的无参构造函数
_instance = (T)Activator.CreateInstance(typeof(T), true); //第二个参数防止异常:“没有为该对象定义无参数的构造函数。”
_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>
/// 系统配置
/// </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();
}