技术总结---项目开端基础设施制造--造轮子 自动生成sql +导入封装
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了技术总结---项目开端基础设施制造--造轮子 自动生成sql +导入封装相关的知识,希望对你有一定的参考价值。
由于这个项目没有使用框架,使用了sqlite数据库。后面的业务可想而知会使用多少拼接sql,这就会相当麻烦 一旦数据库改变 就要改很多东西。
所以基础的就是封装生成sql。需要使用反射和自定义的特性。 自定义特性在于“绑定数据库列名”和“绑定业务格式”
一、业务一---自动生成sql
1.首先是实体对应的表:[特性--》TableNameAttr]
using System;
using System.Collections.Generic;
using System.Text;
namespace PublicLibrary.attr
//数据库表名
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class TableNameAttr: Attribute
string value;
public TableNameAttr(string value)
this.Value = value;
public string Value get => value; set => this.value = value;
2.其次是实体字段对应的列名:[特性--》TableFieldAttr]
using System;
namespace PublicLibrary.attr
//查询 新增 修改的注解
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class TableFieldAttr:Attribute
//bool是boolean 的类型
//是否存在的字段==> 针对查询
private bool isExist =true;
//数据库列名
private string columName;
//字符串类型
private string jdbcType;
//默认值
private string value;
/* //新增 修改 选着性操作的字段
private bool selective;*/
//需要自定义的数据库函数
private bool definedFunc;
//是否主键
private bool primaryKey =false;
//函数的表达式
private string patternStr;
//是否更新 默认为true
private bool isUpdate =true;
public TableFieldAttr(string columName)
this.ColumName = columName;
//字段数据库不存在
public TableFieldAttr(bool isExist)
this.IsExist = false;
//行数据列名 默认值
public TableFieldAttr(string columName, string value)
this.ColumName = columName;
this.Value = value;
public TableFieldAttr(string columName, string value, string jdbcType,bool definedFunc, bool primaryKey, string patternStr)
this.ColumName = columName;
this.DefinedFunc = definedFunc;
this.JdbcType = jdbcType;
this.DefinedFunc = definedFunc;
this.PrimaryKey = primaryKey;
this.PatternStr = patternStr;
this.Value = value;
public TableFieldAttr(string columName, string value, string jdbcType,bool definedFunc, bool primaryKey, string patternStr,bool isUpdate)
this.ColumName = columName;
this.DefinedFunc = definedFunc;
this.JdbcType = jdbcType;
this.DefinedFunc = definedFunc;
this.PrimaryKey = primaryKey;
this.PatternStr = patternStr;
this.Value = value;
this.IsUpdate = isUpdate;
public bool IsExist get => isExist; set => isExist = value;
public string ColumName get => columName; set => columName = value;
public string JdbcType get => jdbcType; set => jdbcType = value;
public string Value get => value; set => this.value = value;
public bool DefinedFunc get => definedFunc; set => definedFunc = value;
public bool PrimaryKey get => primaryKey; set => primaryKey = value;
public string PatternStr get => patternStr; set => patternStr = value;
public bool IsUpdate get => isUpdate; set => isUpdate = value;
3.再就是创建sql的公共方法:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using PublicLibrary.attr;
using PublicLibrary.serivce;
using PublicLibrary.serivce.impl;
namespace PublicLibrary
public static class SQLiteSqlUtils
/// <summary>
/// 创建 查询语句
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t">对象</param>
/// <param name="whereColums">查询出的字段,如[id,name]</param>
/// <returns></returns>
public static string CreateSelectSql<T>(T t,string[] whereColums)
Type type = typeof(T);
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var startTime = DateTime.Now;
TableNameAttr tableAttr = type.GetCustomAttribute<TableNameAttr>();
string tableName = tableAttr != null ? tableAttr.Value : "";
string sqlTemp = "SELECT #colums FROM " + tableName + " WHERE 1=1 #whereColums";
Dictionary<string, TableFieldAttr> fieldAttrDic = new Dictionary<string, TableFieldAttr>();
StringBuilder colums = new StringBuilder();
StringBuilder whereColumsTem = new StringBuilder("");
FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
PropertyInfo propertyInfo = null;
int countFlag = 0;
IEnumerable<TableFieldAttr> enumerable = null;
for (int i = 0; i < fieldInfos.Length; i++)
enumerable = fieldInfos[i].GetCustomAttributes<TableFieldAttr>();
foreach (TableFieldAttr attr in enumerable)
//不是主键 和是数据库字段
if (attr.IsExist)
fieldAttrDic.Add(fieldInfos[i].Name, attr);
colums.Append((countFlag == 0 ? "" : ",") + attr.ColumName);
countFlag++;
countFlag = 0;
TableFieldAttr fieldAttr = null;
if (whereColums != null)
for (int k = 0; k < whereColums.Length; k++)
propertyInfo = type.GetProperty(UpperCaseFirst(whereColums[k]), bindingFlags);
if (propertyInfo != null)
fieldAttr = fieldAttrDic[whereColums[k]];
whereColumsTem.Append(" and " + (fieldAttr == null ? "NoField" : fieldAttr.ColumName) + "=" + propertyInfo.GetValue(t) + "");
/*whereColumsTem.Append((countFlag == 0 ? " " : " and ") + tableName + "." + (fieldAttr == null ? "NoField" : fieldAttr.ColumName) + "=" + propertyInfo.GetValue(t) + "");
countFlag++;*/
string selectSql = sqlTemp.Replace("#colums", colums.ToString()).Replace("#whereColums", whereColumsTem.ToString());
return selectSql;
/// <summary>
/// 创建 删除语句
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t">对象</param>
/// <param name="whereColums">查询出的字段,如[id,name]</param>
/// <returns></returns>
public static string CreateDeleteSql<T>(T t, string[] whereColums)
Dictionary<string, TableFieldAttr> fieldAttrDic = new Dictionary<string, TableFieldAttr>();
Type type = typeof(T);
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var startTime = DateTime.Now;
TableNameAttr tableAttr = type.GetCustomAttribute<TableNameAttr>();
string tableName = tableAttr != null ? tableAttr.Value : "";
string sqlTemp = "DELETE FROM " + tableName + " WHERE 1=1 #whereColums";
StringBuilder whereColumsTem = new StringBuilder("");
FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
PropertyInfo propertyInfo = null;
IEnumerable<TableFieldAttr> enumerable = null;
int countFlag = 0;
for (int i = 0; i < fieldInfos.Length; i++)
enumerable = fieldInfos[i].GetCustomAttributes<TableFieldAttr>();
foreach (TableFieldAttr attr in enumerable)
//不是主键 和是数据库字段
if (attr.IsExist)
fieldAttrDic.Add(fieldInfos[i].Name, attr);
TableFieldAttr fieldAttr = null;
if (whereColums != null)
for (int k = 0; k < whereColums.Length; k++)
propertyInfo = type.GetProperty(UpperCaseFirst(whereColums[k]), bindingFlags);
if (propertyInfo != null)
fieldAttr = fieldAttrDic[whereColums[k]];
whereColumsTem.Append(" and " + (fieldAttr == null ? "NoField" : fieldAttr.ColumName) + "=" + propertyInfo.GetValue(t) + "");
string deleteSql = sqlTemp.Replace("#whereColums", whereColumsTem.ToString());
return deleteSql;
public static string CreateInsertSql<T>(T t)
List<T> list = new List<T>();
list.Add(t);
return CreateInsertSql<T>( list, new ColumServiceImpl());
public static string CreateInsertSql<T>(T t, ColumService columService)
List<T> list = new List<T>();
list.Add(t);
return CreateInsertSql<T>(list, columService);
public static string CreateInsertSql<T>( List<T> list)
return CreateInsertSql<T>(list, new ColumServiceImpl());
//新增 批量新增的sql
public static string CreateInsertSql<T>(List<T> list,ColumService columService)
Type type = typeof(T);
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var startTime = DateTime.Now;
TableNameAttr tableAttr = type.GetCustomAttribute<TableNameAttr>();
string tableName = tableAttr != null ? tableAttr.Value : "";
string insertHead = "INSERT INTO " + tableName + "(#colums) VALUES\\n";
string valuesTemp = "(#values)";
FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
Dictionary<string, TableFieldAttr> table = new Dictionary<string, TableFieldAttr>();
Dictionary<string, PropertyInfo> tablePropertys = new Dictionary<string, PropertyInfo>();
//string iteminfo = "";
PropertyInfo propertyinfo = null;
TableFieldAttr tableFieldAttr = null;
IEnumerable<TableFieldAttr> enumerable = null;
string columsTemplet = "";
string valuesTemplet = "";
for (int i = 0; i < fieldInfos.Length; i++)
tablePropertys.Add(fieldInfos[i].Name, type.GetProperty(UpperCaseFirst(fieldInfos[i].Name), bindingFlags));
enumerable = fieldInfos[i].GetCustomAttributes<TableFieldAttr>();
foreach (TableFieldAttr attr in enumerable)
table.Add(fieldInfos[i].Name, attr);
attr.JdbcType = fieldInfos[i].FieldType.ToString();
//不是主键 和是数据库字段
if (attr.IsExist)
columsTemplet += " " + attr.ColumName + " ,";
valuesTemplet += " #" + fieldInfos[i].Name + " ,";
insertHead = insertHead.Replace("#colums", columsTemplet.Substring(0, columsTemplet.Length - 1));
valuesTemp = valuesTemp.Replace("#values", valuesTemplet.Substring(0, valuesTemplet.Length - 1));
string valuesInfo = "";
string setValue = "";
for (int k = 0; k < list.Count; k++)
object value = "";
string currentValue = "";
string rowInfo = valuesTemp;
//rowInfo = ;
foreach (string key in table.Keys)
propertyinfo = tablePropertys[key];
tableFieldAttr = table[key];
//默认值 不进行函数转换
if (tableFieldAttr.Value != null)
value = tableFieldAttr.Value;
currentValue = value.ToString();
setValue = currentValue;
else if (propertyinfo != null)
value = tablePropertys[key].GetValue(list[k], null);
currentValue = (value == null) ? "" : "" + value.ToString() + "";
setValue = tableFieldAttr.DefinedFunc ? columService.DoFormat(key, currentValue, tableFieldAttr.PatternStr) : currentValue;
rowInfo = rowInfo.Replace("#" + key + "", setValue);
valuesInfo += rowInfo + ",";
var endTime = DateTime.Now;
TimeSpan ts = endTime.Subtract(startTime);
Console.WriteLine("insert语句生成耗时0ms.", ts.TotalMilliseconds);
return insertHead + valuesInfo.Substring(0, valuesInfo.Length - 1);
/// <summary>
/// 单条数据更新
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="whereColums"></param>
/// <returns></returns>
public static string CreateUpdateSql<T>(T t, string[] whereColums)
List<T> list = new List<T>();
list.Add(t);
return CreateUpdateSql<T>(list, whereColums, new ColumServiceImpl());
/// <summary>
/// 更新的语句
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <param name="whereColums">whereColums 必填 参考 新增和修改【id,name】</param>
/// <param name="columService"></param>
/// <returns></returns>
public static string CreateUpdateSql<T>(T t, string[] whereColums, ColumService columService)
List<T> list = new List<T>();
list.Add(t);
return CreateUpdateSql<T>( list, whereColums, columService);
/// <summary>
/// 多条数据更新
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="whereColums"></param>
/// <returns></returns>
public static string CreateUpdateSql<T>(List<T> list, string[] whereColums)
return CreateUpdateSql<T>(list, whereColums, new ColumServiceImpl());
/// <summary>
/// 修改 批量修改的sql 主语法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="whereColums"></param>
/// <param name="columService"></param>
/// <returns></returns>
public static string CreateUpdateSql<T>(List<T> list, string[] whereColums, ColumService columService)
StringBuilder whereColumsTem = new StringBuilder("");
List<string> whereColumTs = new List<string>();
if (whereColums != null)
whereColumTs = new List<string>(whereColums);
var startTime = DateTime.Now;
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
Type type = typeof(T);
TableNameAttr tableAttr = type.GetCustomAttribute<TableNameAttr>();
string tableName = tableAttr != null ? tableAttr.Value : "";
string updateSql = "";
string updateHead = "UPDATE " + tableName + " #colums ";
FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
Dictionary<string, TableFieldAttr> table = new Dictionary<string, TableFieldAttr>();
Dictionary<string, PropertyInfo> tablePropertys = new Dictionary<string, PropertyInfo>();
PropertyInfo propertyinfo = null;
TableFieldAttr tableFieldAttr = null;
IEnumerable<TableFieldAttr> enumerable = null;
string setTemplet = "";
for (int i = 0; i < fieldInfos.Length; i++)
tablePropertys.Add(fieldInfos[i].Name, type.GetProperty(UpperCaseFirst(fieldInfos[i].Name), bindingFlags));
enumerable = fieldInfos[i].GetCustomAttributes<TableFieldAttr>();
foreach (TableFieldAttr attr in enumerable)
table.Add(fieldInfos[i].Name, attr);
attr.JdbcType = fieldInfos[i].FieldType.ToString();
//不是主键 和是数据库字段
if (attr.IsExist && !attr.PrimaryKey && attr.IsUpdate)
setTemplet += " " + attr.ColumName + "= #" + fieldInfos[i].Name + " ,";
//匹配
if (whereColumTs.Contains(fieldInfos[i].Name))
whereColumsTem.Append(" AND " + attr.ColumName + " = #"+ fieldInfos[i].Name + "");
setTemplet = "set " + setTemplet.Substring(0, setTemplet.Length - 1);
if (whereColumTs.Count>0)
setTemplet += " WHERE 1=1 " + whereColumsTem.ToString();
//updateHead += setTemplet;
string colum = "";
object value = "";
string currentValue = "";
for (int k = 0; k < list.Count; k++)
colum = setTemplet;
foreach (string key in table.Keys)
propertyinfo = tablePropertys[key];
tableFieldAttr = table[key];
if (tableFieldAttr.Value != null)
value = tableFieldAttr.Value;
//默认值不做函数处理
currentValue = value.ToString();
else if (propertyinfo != null)
value = tablePropertys[key].GetValue(list[k], null);
currentValue = (value == null) ? "" : "" + value.ToString() + "";
//实际值才会做函数处理
currentValue = tableFieldAttr.DefinedFunc ? columService.DoFormat(key, currentValue, tableFieldAttr.PatternStr) : currentValue;
else
currentValue = "";
colum = colum.Replace("#" + key + "", currentValue);
updateSql += updateHead.Replace("#colums", colum) + "\\n;";
updateSql = updateSql.Substring(0, updateSql.Length - 1);
var endTime = DateTime.Now;
TimeSpan ts = endTime.Subtract(startTime);
Console.WriteLine("update语句生成耗时0ms.", ts.TotalMilliseconds);
return updateSql;
/// <summary>
/// 首字母大写
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string UpperCaseFirst(this string str)
if (string.IsNullOrWhiteSpace(str))
return string.Empty;
char[] s = str.ToCharArray();
char c = s[0];
if (a <= c && c <= z)
c = (char)(c & ~0x20);
s[0] = c;
return new string(s);
实际使用
针对实体:
方法调用示例:
List<OsZbSupplierProductInfo> record =new List<OsZbSupplierProductInfo>();
SQLiteSqlUtils.CreateInsertSql(record)
业务2.针对导入excel
如法炮制 需要什么?需要列的下标 列内容非空验证 重复性验证 格式验证 长度验证
1.自定义特效
using System;
namespace PublicLibrary.attr
//导入的注解【特征值/注解】
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class ColumnInfoAttr:Attribute
//位置
int index = -1;
// 长度
int length = 32;
// 是否为空
bool nullable = false;
// 时间格式
string dateFormat = "";
//正则表单式 验证数据格式
string pattern = "";
public ColumnInfoAttr(int index, int length)
this.index = index;
this.length = length;
public ColumnInfoAttr(int index, int length, bool nullable)
this.index = index;
this.length = length;
this.nullable = nullable;
public ColumnInfoAttr(int index, int length, bool nullable,string dateFormat)
this.index = index;
this.length = length;
this.nullable = nullable;
this.dateFormat = dateFormat;
public ColumnInfoAttr(int index, int length, bool nullable, string dateFormat, string pattern)
this.index = index;
this.length = length;
this.nullable = nullable;
this.dateFormat = dateFormat;
this.Pattern = pattern;
public int Index get => index; set => index = value;
public int Length get => length; set => length = value;
public bool Nullable get => nullable; set => nullable = value;
public string DateFormat get => dateFormat; set => dateFormat = value;
public string Pattern get => pattern; set => pattern = value;
2.公共方法【注此部分没有校验重复性】
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using ess_zbfz_main.dto;
using ExcelDataReader;
using PublicLibrary;
using PublicLibrary.attr;
namespace ess_zbfz_main.util
//导入工具类
public class ExcelUtil
//, Dictionary<string, string> dbListDict, string[] keys
public static MessageInfo<T> ReadExcel<T>(int firstIndex)
MessageInfo<T> messageInfo = new MessageInfo<T>();
FileStream fileStream =null;
IExcelDataReader reader = null;
DataSet result = null;
try
//小于0 默认是第三行开始读取
firstIndex = firstIndex < 0 ? 2 : firstIndex;
OpenFileDialog fileImportDialog = new OpenFileDialog();
fileImportDialog.Filter = "导入文件包(*.xlsx)|*.xlsx";//扩展名
fileImportDialog.FileName = "";
if (fileImportDialog.ShowDialog() == DialogResult.OK)
string saveTempPath = @System.AppDomain.CurrentDomain.BaseDirectory + "data\\\\receiveData\\\\";//临时存放的路径
/* if (!File.Exists(saveTempPath))
File.Create(saveTempPath);
*/
string saveName = fileImportDialog.FileName.Substring(fileImportDialog.FileName.LastIndexOf("\\\\") + 1);
string dataPath = saveTempPath + saveName;//文件地址
File.Copy(fileImportDialog.FileNames[0], dataPath, true);
//解析处理 start
//stream =
fileStream = File.Open(fileImportDialog.FileName, FileMode.Open, FileAccess.Read);
reader = ExcelReaderFactory.CreateReader(fileStream);
object[] curObject = new object[10];
result = reader.AsDataSet(new ExcelDataSetConfiguration()
ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
UseHeaderRow = true,
ReadHeaderRow = (rowReader) =>
// 从第几行之后开始读取
int index = firstIndex;
if (index != rowReader.Depth)
rowReader.Read();
);
DataTableCollection tableCollection = result.Tables;
//初步处理的数据
messageInfo = GetmessageInfo<T>(tableCollection[0], firstIndex);
//reader.Close();
return messageInfo;
catch(Exception ex)
Console.WriteLine(ex.Message);
MessageBox.Show("导入文件错误信息:" + ex.Message);
finally
//需要释放资源
if (reader != null)
reader.Close();
if (fileStream != null)
fileStream.Close();
if (result != null)
result.Clear();
return messageInfo;
public static MessageInfo<T> GetmessageInfo<T>(DataTable dt, int firstIndex)
MessageInfo<T> messageInfo = new MessageInfo<T>();
bool existError = false;
int totalCount = dt.Rows.Count;
int successCount = 0;
//错误信息
StringBuilder errorSb = new StringBuilder();
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var list = new List<T>();
Type type = typeof(T);
ColumnInfoAttr columnInfoAttr = null;
FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
Dictionary<int, PropertyInfo> dictionAry = new Dictionary<int, PropertyInfo>();
Dictionary<int, ColumnInfoAttr> columnDic = new Dictionary<int, ColumnInfoAttr>();
//行属性
//PropertyInfo propertyInfo = type.GetProperty("CurExcelIndex");
for (int p = 0; p < fieldInfos.Length; p++)
columnInfoAttr = fieldInfos[p].GetCustomAttribute<ColumnInfoAttr>();
if (columnInfoAttr != null)
dictionAry.Add(columnInfoAttr.Index, type.GetProperty(SQLiteSqlUtils.UpperCaseFirst(fieldInfos[p].Name)));
columnDic.Add(columnInfoAttr.Index, columnInfoAttr);
PropertyInfo currentProp = null;
//实体
T s;
bool flag = false;
int currentRow = firstIndex;
foreach (DataRow item in dt.Rows)
currentRow++;
s = Activator.CreateInstance<T>();
for (int i = 0; i < dt.Columns.Count; i++)
if (dictionAry.ContainsKey(i))
currentProp = dictionAry[i];
columnInfoAttr = columnDic[i];
object v = null;
if (currentProp.PropertyType.ToString().Contains("System.Nullable"))
v = Convert.ChangeType(item[i], Nullable.GetUnderlyingType(currentProp.PropertyType));
else
v = Convert.ChangeType(item[i], currentProp.PropertyType);
//不可以为空==> 非空验证
if (!columnInfoAttr.Nullable && (v == null || v.ToString().Trim().Length<=0))
//successCount++;
existError = true;
errorSb.Append("第" + currentRow + "行," + dt.Columns[i].ColumnName + " 数据不得为空\\n");
flag = true;
//break;
//不为空 超过了最大长度==> 长度验证
if (v != null && columnInfoAttr.Length < v.ToString().Length)
//successCount++;
existError = true;
errorSb.Append("第" + currentRow + "行," + dt.Columns[i].ColumnName + " 数据长度不得超过" + columnInfoAttr.Length + "个字符\\n");
flag = true;
//break;
//正则验证部分==> 数据格式验证
if (v != null && columnInfoAttr.Pattern!=null && columnInfoAttr.Pattern != "" && v.ToString().Trim().Length > 0)
//不匹配正则
if(!Regex.IsMatch(v.ToString().Trim(), columnInfoAttr.Pattern))
existError = true;
errorSb.Append("第" + currentRow + "行," + dt.Columns[i].ColumnName + " 数据格式不正确");
flag = true;
//是否校验不合格
if (flag)
flag = false;
successCount++;
break;
currentProp.SetValue(s, v, null);
currentProp = dictionAry[-10];
currentProp.SetValue(s, currentRow, null);//设置excel的行列
currentProp = null;
list.Add(s);
//返回的信息
messageInfo.Record = list;
messageInfo.ErrorInfo = errorSb.ToString();
messageInfo.TotalCount = totalCount;
messageInfo.ErrorCount = totalCount - successCount;
messageInfo.SuccessCount = successCount;
messageInfo.ExistError = existError;
return messageInfo;
3.具体的使用
实体:
方法调用:
int firstIndex = 2;
MessageInfo<OsZbSupplierProductInfo> messageInfo = ExcelUtil.ReadExcel<OsZbSupplierProductInfo>(firstIndex);
宋文超super
以上是关于技术总结---项目开端基础设施制造--造轮子 自动生成sql +导入封装的主要内容,如果未能解决你的问题,请参考以下文章