asp.net 自定义的模板方法接口通用类型
本来想写这个帖子已经很久了,但是公司事情多,做着做着就忘记了。公司因为需要做接口,而且用的还是asp.net的老框架,使用Handler来做,没得办法,自己照着MVC写了一个通过的接口操作模板。
上送json数据,返回的也是json数据。可以像MVC一样自动绑定并可以进行DataAnnotations验证。尽量达到在业务逻辑处理区域不用对上送参数做过多的获取和判断,能一次搞定就一次搞定。
话不多说,上代码!!!
BaseClass:用作接口参数的基类。接口参数类型可以继承该类,也可以不继承,或自己定义其他基类。
public abstract class BaseClass { }
EmptyClass:一个空类。当做一个参数类型传入到接口中,占用地方而已。
public class EmptyClass : BaseClass { }
ModelError:错误消息载体类型。
[Serializable]
public class ModelError {
public ModelError(Exception exception) : this(exception, null) { } public ModelError(string errorMessage) {
this.ErrorMessage = errorMessage ?? string.Empty;
} public ModelError(Exception exception, string errorMessage) : this(errorMessage) {
if (exception == null)
throw new ArgumentNullException("exception");
this.Exception = exception;
} public Exception Exception { get; private set; }
public string ErrorMessage { get; private set; }
}
ModelErrorCollection:错误消息载体集合。
[Serializable]
public class ModelErrorCollection : Collection<ModelError> {
public void Add(string errorMessage) {
base.Add(new ModelError(errorMessage));
} public void Add(Exception exception) {
base.Add(new ModelError(exception));
}
}
ModelState:模型绑定状态。
/// <summary>
/// 模型绑定状态
/// </summary>
[Serializable]
public class ModelState {
private ModelErrorCollection _errors = new ModelErrorCollection();
public bool IsValid {
get {
return _errors.Count == ;
}
}
public ModelErrorCollection Errors {
get {
return _errors;
}
}
}
ModelBinder:模型绑定抽象类,需要继承此抽象类。用来绑定上送参数并验证的基类。
/// <summary>
/// 模型绑定抽象类,需要继承此抽象类。
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class ModelBinder<T> where T : class {
protected ModelState _modelState; /// <summary>
/// 模型绑定状态
/// </summary>
public ModelState ModelState {
get {
if (_modelState == null)
_modelState = new ModelState();
return _modelState;
}
} /// <summary>
/// 绑定操作
/// </summary>
/// <returns></returns>
public abstract T Binder(); /// <summary>
/// 验证实体数据合法性。如果有错误,请在ModelState参数中获取。
/// </summary>
/// <param name="entity"></param>
protected void Valide(object entity) {
if (entity == null)
return;
//获取T类型的所有公共属性
Type type = entity.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); if (properties != null && properties.Count() > ) {
//针对每一个公共属性,获取其特性
foreach (var property in properties) {
//如果当前属性为一个自定义类型
if (property.PropertyType != typeof(object) && Type.GetTypeCode(property.PropertyType) == TypeCode.Object) {
this.Valide(property.GetValue(entity, null));
}
else
ValideProperty(entity, property); if (!_modelState.IsValid)
break;
}
}
} /// <summary>
/// 验证属性的每一个特性约束
/// </summary>
/// <param name="entity"></param>
/// <param name="property"></param>
private void ValideProperty(object entity, PropertyInfo property) {
if (entity != null && property != null) {
var attributes = property.GetCustomAttributes(typeof(ValidationAttribute), false);
foreach (ValidationAttribute attribute in attributes)
ValidatePropertyAttribute(entity, property, attribute);
}
} /// <summary>
/// 使用特性对属性进行验证
/// </summary>
/// <param name="entity"></param>
/// <param name="property"></param>
/// <param name="attribute"></param>
private void ValidatePropertyAttribute(object entity, PropertyInfo property, ValidationAttribute attribute) {
if (entity != null && property != null && attribute != null) {
//找到该属性
//注明:每一个函数都应当具有独立性.
PropertyInfo currentProperty = entity.GetType().GetProperties().Where(p => p.Name == property.Name).FirstOrDefault(); //判断当前特性是否有IsRequiredInstance字段,这是自定义的特性,用于验证同一个实例中两个不同共有属性的值
PropertyInfo[] pros = attribute.GetType().GetProperties();
if (pros.Where(it => it.Name == "IsRequiredInstance" && it.PropertyType == typeof(bool)).FirstOrDefault() != null)
attribute.GetType().GetProperty("Instance").SetValue(attribute, entity, null); if (currentProperty != null) {
var value = currentProperty.GetValue(entity, null);
if (!attribute.IsValid(value))
_modelState.Errors.Add(attribute.ErrorMessage);
}
}
} }
BaseHandler:一般处理逻辑的基类。
/// <summary>
/// 一般处理逻辑的基类。
/// </summary>
/// <typeparam name="T">请求参数模型</typeparam>
/// <typeparam name="R">返回参数模型</typeparam>
public abstract class BaseHandler<T, R> : ModelBinder<T> where T : class where R : class {
protected readonly ILog log;
public BaseHandler() {
log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}
public BaseHandler(string typeName) {
if ((typeName ?? "").Trim() != "")
log = LogManager.GetLogger(typeName);
else
log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
} /// <summary>
/// 处理接口API消息
/// </summary>
/// <returns></returns>
public abstract R Process(); /// <summary>
/// 真正需要处理的接口逻辑
/// </summary>
/// <param name="param">客户端传过来的请求参数</param>
/// <returns></returns>
protected abstract R DoWork(T param);
}
IndexHandler:一般业务接口的模板方法处理器。该接口是基础性接口,如果有其他业务需求,请自行另外自定义或继承该接口。
/// <summary>
/// 一般业务接口的模板方法处理器。
/// 该接口是基础性接口,如果有其他业务需求,请自行另外自定义或继承该接口。
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class IndexHandler<T> : BaseHandler<T, BaseResponseResult> where T : class {
public IndexHandler() : base() { }
public IndexHandler(string typeName) : base(typeName) { } /// <summary>
/// 对实体模型进行绑定和参数的特性验证
/// </summary>
/// <returns></returns>
public override T Binder() {
//初始化模型
T rc = default(T); try {
//初始化ModelState
if (_modelState == null)
_modelState = new ModelState();
else
_modelState.Errors.Clear(); //获取数据
Stream stream = HttpContext.Current.Request.InputStream;
stream.Seek(, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
int count = stream.Read(buffer, , buffer.Length);
if (count > ) {
string requestParam = Encoding.UTF8.GetString(buffer);
//绑定数据
rc = new JavaScriptSerializer().Deserialize<T>(requestParam);
if (rc != null) {
//验证数据合法性
base.Valide(rc);
}
else
_modelState.Errors.Add("绑定数据失败!");
}
else
_modelState.Errors.Add("请求参数为空!");
}
catch (Exception ex) {
_modelState.Errors.Add("绑定数据出现错误!");
} return rc;
} /// <summary>
/// 处理接口API消息
/// </summary>
/// <returns></returns>
public override BaseResponseResult Process() {
BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError); try {
//绑定请求参数
T requestParam = Binder();
//开启逻辑操作
if (_modelState.IsValid)
rc = DoWork(requestParam);
else {
StringBuilder sbuilder = new StringBuilder();
foreach (var error in _modelState.Errors)
sbuilder.Append(error.ErrorMessage); rc.SetResult(ErrorCode.InvalideParameter, sbuilder.ToString());
}
}
catch (Exception ex) {
rc.SetResult(ErrorCode.SystemError);
rc.returnData = null;
log.Error("Process", ex);
} return rc;
}
}
BaseResponseResult:返回结果。
/// <summary>
/// 返回结果
/// </summary>
public class BaseResponseResult {
public BaseResponseResult() { } public BaseResponseResult(int returnValue, string returnMsg) {
_code = returnValue;
_message = returnMsg;
} public BaseResponseResult(ErrorCode code, string returnMsg="") {
SetResult(code, returnMsg);
} private int _code = ;
private string _message = "";
private string _contentType = "application/json";
/// <summary>
/// 错误码,0表示成功,其他表示失败
/// </summary>
public virtual int returnValue { get { return _code; } }
/// <summary>
/// 错误码,0表示成功,其他表示失败
/// </summary>
public virtual string returnMsg { get { return _message; } }
/// <summary>
/// 返回的数据,json格式
/// </summary>
public virtual object returnData { get; set; } /// <summary>
/// 设置返回状态码
/// </summary>
/// <param name="code"></param>
public virtual void SetCode(int code) {
_code = code;
} /// <summary>
/// 设置返回状态码
/// </summary>
/// <param name="code"></param>
public virtual void SetCode(ErrorCode code) {
SetResult(code);
} /// <summary>
/// 设置返回消息
/// </summary>
/// <param name="message"></param>
public virtual void SetMessage(string message) {
_message = message;
}
/// <summary>
/// 设置返回状态码和消息
/// </summary>
/// <param name="returnValue"></param>
/// <param name="returnMsg"></param>
public virtual void SetResult(int returnValue, string returnMsg) {
_code = returnValue;
_message = returnMsg;
} /// <summary>
///
/// </summary>
/// <param name="code">ErrorCode代码</param>
/// <param name="returnMsg">返回消息。如果此项不输入值,则自动设置默认值!</param>
public virtual void SetResult(ErrorCode code, string returnMsg="") {
this._code = (int)code;
this._message = (returnMsg??"").Trim()!=""?returnMsg:ErrorMsg.ErrorMessage[code];
}
/// <summary>
/// 设置返回消息体
/// </summary>
/// <param name="obj"></param>
public virtual void SetReturnData(params object[] obj) {
if (obj != null && obj.Length > ) {
this.returnData = obj.ToList();
}
} public virtual void SetReturnData(object obj) {
this.returnData = obj;
} public virtual void SetContentType(string contentType) {
this._contentType = contentType;
} /// <summary>
/// 将当前结果转化为JSON字符串
/// </summary>
/// <returns></returns>
public virtual string ToJson() {
return new JavaScriptSerializer().Serialize(this);
} public virtual void Response(bool isEnd = true) {
HttpContext.Current.Response.ContentType = _contentType;
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
HttpContext.Current.Response.Write(this.ToJson());
if (isEnd)
HttpContext.Current.Response.End();
}
}
ErrorCode:返回状态码枚举。
public enum ErrorCode {
/// <summary>
/// 操作错误
/// </summary>
OperationError = -,
/// <summary>
/// 成功
/// </summary>
Success = ,
/// <summary>
/// 失败
/// </summary>
Failed = ,
/// <summary>
/// 无数据
/// </summary>
NoData=,
/// <summary>
/// 不存在此页面
/// </summary>
NotExistPage=,
/// <summary>
/// 无权限
/// </summary>
NoPermission=,
/// <summary>
/// 未登录
/// </summary>
NoLogin=,
/// <summary>
/// 被禁止
/// </summary>
Forbidden=,
/// <summary>
/// 请求参数格式不符合要求
/// </summary>
InvalideParameter = ,
/// <summary>
/// 无此接口
/// </summary>
NoAction = ,
/// <summary>
/// 系统错误
/// </summary>
SystemError = ,
} public class ErrorMsg {
public static Dictionary<ErrorCode, string> ErrorMessage = new Dictionary<ErrorCode, string> {
{ ErrorCode.OperationError,"服务器响应错误!"},
{ ErrorCode.Success,"成功!"},
{ ErrorCode.Failed, "失败!"},
{ ErrorCode.NoData, "查无数据!"},
{ ErrorCode.NotExistPage,"此页码不存在!"},
{ ErrorCode.InvalideParameter,"请求参数非法!"},
{ ErrorCode.NoAction,"无此接口!"},
{ ErrorCode.SystemError,"系统错误!"},
{ ErrorCode.NoPermission,"无权限操作此功能!" },
{ ErrorCode.NoLogin,"未登录!"},
{ ErrorCode.Forbidden,"操作被禁止!"},
};
}
接下来介绍使用方法:
IndexHandler_Base64:定义一个接收base64字符串参数的接口处理器。代码如下:
/// <summary>
/// 一般业务接口的模板方法处理器,适用于参数为base64字符串的请求
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class IndexHandler_Base64<T> : IndexHandler<T> where T:class {
//当前接口是否需要登录后才能操作
protected bool _isLoginRequired = false; /// <summary>
/// 实例化一个只接收base64字符串的接口操作
/// </summary>
/// <param name="isLoginRequired">当前接口是否需要登录。true:需要 false:不需要(默认)</param>
public IndexHandler_Base64(bool isLoginRequired = false) : base() { _isLoginRequired = isLoginRequired; }
/// <summary>
/// 实例化一个只接收base64字符串的接口操作
/// </summary>
/// <param name="typeName">发起日志记录的类名</param>
/// <param name="isLoginRequired">当前接口是否需要登录。true:需要 false:不需要(默认)</param>
public IndexHandler_Base64(string typeName,bool isLoginRequired=false) : base(typeName) { _isLoginRequired = isLoginRequired; } /// <summary>
/// 对实体模型进行绑定和参数的特性验证
/// </summary>
/// <returns></returns>
public override T Binder() {
//初始化模型
T rc = default(T); try {
//初始化ModelState
if (_modelState == null)
_modelState = new ModelState();
else
_modelState.Errors.Clear(); //获取数据
Stream stream = HttpContext.Current.Request.InputStream;
stream.Seek(, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
int count = stream.Read(buffer, , buffer.Length);
if (count > ) {
string requestParam = Encoding.UTF8.GetString(buffer);
requestParam = Encoding.UTF8.GetString(Convert.FromBase64String(requestParam));
//绑定数据
rc = new JavaScriptSerializer().Deserialize<T>(requestParam);
if (rc != null) {
//验证数据合法性
base.Valide(rc);
}
else
_modelState.Errors.Add("绑定数据失败!");
}
else
_modelState.Errors.Add("请求参数为空!");
}
catch (Exception ex) {
_modelState.Errors.Add("绑定数据出现错误!");
} return rc;
} public override BaseResponseResult Process() {
//如果该接口需要登录后才能操作,则检查当前用户是否登录
if (_isLoginRequired) {
int userId = ;
try {
userId = Login.GetSession("UserID");
}
catch { }
if (userId <= )
return new BaseResponseResult(ErrorCode.NoLogin);
} return base.Process();
}
}
接下来定义一个Index.ashx文件,开始使用模板接口。
/// <summary>
/// index 的摘要说明
/// </summary>
public class Index : IHttpHandler {
private readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
//当前登录的用户
public static int _thisUserId {
get {
return Login.GetSession("UserID");
}
}
public void ProcessRequest(HttpContext context) {
BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError, "获取数据失败!"); try {
string action = GetAction(); switch (action) {
//获取 xxx列表
case "":
rc = new ListHandler().Process();
break;
//获取 xxx信息
case "":
rc = new ItemHandler().Process();
break; default:
rc.SetResult(ErrorCode.NoAction);
break;
} }
catch (Exception ex) {
log.Error("ProcessRequest", ex);
rc.SetResult(ErrorCode.SystemError);
} rc.Response();
} public bool IsReusable {
get {
return false;
}
} /// <summary>
/// 获取Action接口名称
/// </summary>
/// <returns></returns>
private string GetAction() {
string rc = ""; try {
rc = HttpContext.Current.Request["action"] ?? "";
if ((rc ?? "").Trim() == "") {
Stream stream = HttpContext.Current.Request.InputStream;
if (stream != null) {
byte[] buffer = new byte[stream.Length];
int count = stream.Read(buffer, , buffer.Length);
if (count > ) {
try {
string requestParam = Encoding.UTF8.GetString(buffer);
requestParam = Encoding.UTF8.GetString(Convert.FromBase64String(requestParam));
JsonData jd = JsonMapper.ToObject(requestParam);
if (jd != null && jd.Keys.Contains("action"))
rc = jd["action"].ToString();
}
catch { }
}
}
}
}
catch (Exception ex) {
log.Error("GetAction", ex);
} return rc;
} } #region 接口处理区 #region 1001 获取 xxx列表
public class ListReqModel { [Required(ErrorMessage = "请上送需要类型!")]
[Range(0,5, ErrorMessage = "type参数值有误!")]
public int type { get; set; }
/// <summary>
/// 页码索引,从0开始
/// </summary>
[Required(ErrorMessage = "请上送页码索引!")]
[Min(, ErrorMessage = "pageIndex参数值有误!")]
public int pageIndex { get; set; } /// <summary>
/// 每页个数
/// </summary>
[Required(ErrorMessage = "请上送每页的个数!")]
[Min(, ErrorMessage = "pageSize参数值有误!")]
public int pageSize { get; set; } }
public class ListHandler : IndexHandler_Base64<ListReqModel> { public ListHandler() : base("ListHandler") { }
protected override BaseResponseResult DoWork(ListReqModel param) {
BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError);
int totalCount = ;
ProjectBLL biz = new ProjectBLL();
DataTable table = biz.GetList(param.type ,Index._thisUserId, out totalCount, param.pageIndex, param.pageSize); if (table != null && table.Rows.Count > ) {
rc.SetReturnData(new
{
//总个数
totalCount = totalCount,
list = table.AsEnumerable().Select(it => new
{
id = it["ID"].ToInt64(),
title = it["Title"].ToNormalString(),
frontCover = it["FrontCover"].ToNormalString()
}).ToList()
});
rc.SetResult(ErrorCode.Success, "成功!");
}
else if (totalCount > )
rc.SetResult(ErrorCode.NotExistPage, "当前页码不存在!");
else
rc.SetResult(ErrorCode.NoData, "没有数据哟!"); return rc;
}
}
#endregion #region 1002 获取xxx信息 public class ItemReqModel {
[Required(ErrorMessage = "请上送项目标识!")]
[Min(, ErrorMessage = "id参数值有误!")]
public long id { get; set; }
} public class ItemHandler : IndexHandler_Base64<ItemReqModel> {
public ItemHandler() : base("ItemHandler") { } protected override BaseResponseResult DoWork(ItemReqModel param) {
BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError);return rc;
}
} #endregion#endregion
ajax的使用方式:
//接入说明:
//1、请使用POST方式上传;
//2、请将参数组装为Json字符串,如:{'action':'xxx','xx':'xx'};
//3、Json字符串组装好以后,将其变为Base64字符串后上传。 //上送数据案例如下所示:
$.ajax({
type: "POST",
dataType: "json",
url: GetHost() + 'Index.ashx',
data: Base.encode('{"action":"1001","type":"' + type + '","pgIndex":' + pageIndex + ',"pgSize":' + pageSize + '}'),
contentType:"application/json",
success: function (data) {
//获取数据
if (data) {
//成功获取数据
if (data.returnValue == ) {
//处理数据
}
}
}
});
至此,完毕!以上的通用接口处理器采用了模板方法设计模式,尽量减少重复工作量。
说明:本篇文章为作者原创,如需转载,请说明来源及出处!
asp.net 自定义的模板方法接口通用类型的更多相关文章
- asp.net core参数保护之自定义要保护的参数类型
asp.net core参数保护之自定义要保护的参数类型 Intro 为了实现 asp.net core 下的参数保护,扩展了asp.net core 中 DataProtection,可以自动化的保 ...
- ASP.NET交互Rest服务接口(Jquery的Get与Post方式)
ASP.NET交互Rest服务接口(Jquery的Get与Post方式) 本文将通过一个简单的实例,介绍如何创建一个Rest服务接口,以及通过JQUERY去对它进行调用;主要采取两种方式分别为Get跟 ...
- 连表查询都用Left Join吧 以Windows服务方式运行.NET Core程序 HTTP和HTTPS的区别 ASP.NET SignalR介绍 asp.net—WebApi跨域 asp.net—自定义轻量级ORM C#之23中设计模式
连表查询都用Left Join吧 最近看同事的代码,SQL连表查询的时候很多时候用的是Inner Join,而我觉得对我们的业务而言,99.9%都应该使用Left Join(还有0.1%我不知道在 ...
- 《连载 | 物联网框架ServerSuperIO教程》- 13.自定义视图显示接口开发,满足不同的显示需求
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
- ABAP基本数据类型、通用类型
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- Android 代码库(自定义一套 Dialog通用提示框 )
做Android开发五年了,期间做做停停(去做后台开发,服务器管理),当回来做Android的时候,发现很生疏,好些控件以前写得很顺手,现在好像忘记些什么了,总要打开这个项目,打开那个项目 ...
- ASP.NET中共有哪几种类型的控件?其中,HTML控件、HTML服务器控件和WEB服务器控件之间有什么区别
ASP.NET的控件包括WEB服务器控件.WEB用户控件.WEB自定义控件.HTML服务器控件和HTML控件.HTML控件.HTML服务器控件和WEB服务器控件之间的区别如下所示.q HTM ...
- ASP.NET WebAPI构建API接口服务实战演练
一.课程介绍 一.王小二和他领导的第一次故事 有一天王小二和往常一下去上早班,刚吃完早餐刚一打开电脑没一会儿.王小二的领导宋大宝走到他的面前,我们现在的系统需要提供服务给其他内部业务系统,我看你平时喜 ...
- 使用ASP.NET Identity 实现WebAPI接口的Oauth身份验证
使用ASP.NET Identity 实现WebAPI接口的Oauth身份验证 目前WEB 前后端分离的开发模式比较流行,之前做过的几个小项目也都是前后分离的模式,后端使用asp.net weba ...
随机推荐
- 调试 ASP.NET Core 2.0 源代码
在Visual Studio 2017中可以通过符号以及源链接,非常方便对 ASP.NET Core 2.0中源代码进行调试.在这篇文章中,我们将重点介绍如何使用源链接对ASP.NET Core源进行 ...
- spring boot系列01--快速构建spring boot项目
最近的项目用spring boot 框架 借此学习了一下 这里做一下总结记录 非常便利的一个框架 它的优缺点我就不在这背书了 想了解的可以自行度娘谷歌 说一下要写什么吧 其实还真不是很清楚,只是想记录 ...
- Ubuntu 16安装GPU版本tensorflow
pre { direction: ltr; color: rgb(0, 0, 0) } pre.western { font-family: "Liberation Mono", ...
- OpenVPN server端配置文件详细说明(转)
本文将介绍如何配置OpenVPN服务器端的配置文件.在Windows系统中,该配置文件一般叫做server.ovpn:在Linux/BSD系统中,该配置文件一般叫做server.conf.虽然配置文件 ...
- CSS图片垂直居中方法整理集合
原帖链接:http://bbs.blueidea.com/thread-2666987-1-1.html 1.因为Opera,FF3,IE8均支持display:talbe;这些特性了,因此改进的办法 ...
- 自动化selenium开发
一.开发环境搭建 1.Firefox浏览器 1.1 下载firefix并安装. 1.2 Firefox中打开"开始菜单“ -> ”开发者“ -> ”获取更多工具“ -> 搜 ...
- spring cloud+dotnet core搭建微服务架构:配置中心(四)
前言 我们项目中有很多需要配置的地方,最常见的就是各种服务URL地址,这些地址针对不同的运行环境还不一样,不管和打包还是部署都麻烦,需要非常的小心.一般配置都是存储到配置文件里面,不管多小的配置变动, ...
- slf4j+log4j在Java中实现日志记录
小Alan今天来跟大家聊聊开发中既简单又常用但必不可少的一样东西,那是什么呢?那就是日志记录,日志输出,日志保存. 后面就统一用日志记录四个字来形容啦. 日志记录是项目的开发中必不可少的一个环节,特别 ...
- Mybatis,Spring,SpringMVC框架面试题
Mybatis测试 1, Mybatis的核心是( sqlsessionfactory ) 2, 使用Mybatis持久化框架进行数据查询需要返回的一个实体类的集合, 在<sel ...
- 【读书笔记】《写给大忙人看的Java SE 8》——Java8新特性总结
虽然看过一些Java 8新特性的资料,但是平时很少用到,时间长了就忘了,正好借着Java 9的发布,来总结下一些Java 8中的新特性. 接口中的默认方法和静态方法 先考虑一个问题,如何向Java中的 ...