我们的系统使用的java语言开发,基于Spring框架搭建的业务中台,在讨论业务系统异常处理策略之前,先把java的异常机制进行简单说明。

一、Java的异常机制

1.Java异常分类

【Error】是系统错误或者编译错误,常见的虚拟机运行错误、栈溢出错误、内存溢出错误都是属于error,这种程序无法处理,发生后会导致jvm终止线程

【Exception】是程序中产生的错误,程序本身可以捕获并且处理。通常会分为运行时异常(非受检异常)和非运行时异常(受检异常),受检异常程序必须要处理(try-catch 或者继续抛出),非受检异常程序可以不处理,会自动向上抛出,直至main方法或者Thread.run方法,终止该线程。

2. Java的异常处理方式

(1)调用方通过try - catch - finally处理,示例代码如下:

try
{可能会发生的异常
}catch(异常类型 异常名(变量)){针对异常进行处理的代码
}catch(异常类型 异常名(变量)){针对异常进行处理的代码
}...
[finally{释放资源代码;
}]

(2)throws 调用方补处理,直接将异常在方法声明中抛出,交由上层处理,示例代码如下:

public void testExceptionThrow throws NullPointerException{throw new NullPointerException();
}

二、Spring的异常机制

Spring有一套自带的视图错误处理机制,借助SpringMVC的视图控制能力,通过一些异常的处理Resolver来进行错误页面的跳转。而在中台系统建设中,则需要用到Spring提供的自定义异常的处理能力,后端使用的异常处理机制主要以下两种

1. @ControllerAdvice+@ExceptionHandler处理全局异常

实现方式是自定义一个异常处理类,只需要在该类上标记@ControllerAdvice即可。
同时要在执行异常处理的方法上标记@ExceptionHandler。这样在发生了指定异常时可以找到响应的异常处理方法进行处理。此种模式的底层是 ExceptionHandlerExceptionResolver 支持的,代码示例如下:

/*** 处理整个web controller的异常*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler({ArithmeticException.class}) // 可以处理多种异常public String handleArithmeticException() {log.info("处理异常");// 也可以返回ModelAndView类型对象,因为在处理异常相关源码中最后也会渲染视图转化为ModelAndViewreturn "error";}@ExceptionHandler({NullPointerException.class})public String handleNullPointerException(Exception e) {log.info("处理异常");return "error";}
}

@ControllerAdvice 注解的原理是SpringAOP提供的,是将Controller层的方法作为切面,从而对Controller层方法进行拦截处理,如果是前后端分离的项目也可以使用 @RestControllerAdvice 注解。

2. 自定义实现 HandlerExceptionResolver 处理异常

可以作为默认的全局异常处理规则(注意:设置为最高优先级会顶替掉SpringBoot原生的异常处理规则)

// SpringBoot底层会优先调用SpringBoot定义的异常处理器(ExceptionResolver)
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Component
public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {try {response.sendError(511, "xxx错误");} catch (IOException e) {e.printStackTrace();}ModelAndView modelAndView = new ModelAndView();return modelAndView;}
}

三、业务系统中的异常处理

异常是可以继承的,业务系统中都会通过继承异常实现自己的自定义异常,而为了简化开发,减少程序员的工作量,自定义异常都会实现RuntimeException ,这样程序中就不需要显性处理,如有需要自己捕获处理该异常,但这种潜规则会导致很多程序员不知道异常该如何使用如何设计。下面我就分享下寿险中台中的异常处理机制,可以给大家提供一种异常处理的参考方案。

1.设计异常机制前的知识准备

【为什么要自定义异常】

Java是面向对象的语言,区别于面向过程的语言,很多处理结果和信息不仅可以像面向过程的语言的返回值传递,还可以通过参数对象、上下文变量传递,而java提供的异常机制就是在不影响方法定义的出参、入参的情况下定义错误信息的传递机制。

【系统中的错误码和异常的关系】

异常是一种快捷方便的错误传递方式,而外部服务接口交互不能通过异常的方式传递。

(1)错误码 : 接口交互的错误传递方式,一般都是由一个Result的DTO 通过errorCode和errorMessage来承载。
(2)异常 : 应用内部的错误传递方式,可以由框架统一处理,调用方也可以根据自己的需要去差异化catch进行业务处理。

【为什么会选择集成RuntimeException】

RuntimeException不需要程序处理,可以交由框架自动处理,而且Spring的异常机制就是针对RuntimeException进行的,更加方便了程序员的开发工作。由于RuntimeException的无感也会带来一些问题,如果不在方法中声明或者注释中说明异常情况,调用方很容易忽略了异常情况的逻辑和应对,致使系统的健壮性降低。

【系统异常还有没有必要自定义,有没有必要继承RuntimeException】

先说结论系统异常自定义还是有必要的,因为很多系统异常都是受检异常,尤其使用一些组件或者功能时,数组越界、ClassNotFound等,针对这类异常程序必须要catch或者throw 处理,会给开发带来很大的处理工作。而系统异常的处理往往逻辑比较统一,完全可以交由框架在拦截层统一处理,所以在业务系统中将系统异常统一定义转换,可以极大降低对开发人员的要求,同时也可以满足资深程序员对各类异常单独处理的需求。

【异常中需要注意的事项】

(1)主动throw异常,异常中需要带有上下文信息;
(2)程序中尽量不要产生error,error是无法处理的,会导致线程终止,exp. 方法参数中使用原生类型int,而请求方传的参数是Integer,当参数为null的时候就会触发error,导致线程终止;

2.寿险中台的异常规范

【寿险中台异常的分类】

常见的业务系统中都会定义自己的业务异常、系统异常,寿险中台也是使用了众安的common包中的异常定义的BizException和ServiceException作为寿险中台异常的父类,这样做的好处时即使寿险中台自定义了异常,但整体异常框架仍然是在众安技术部的规范内,实现自己的自定义需求的同时,不会影响技术框架的能力。

业务系统通常是自定义业务异常,但针对特殊的系统异常有特殊处理的话也可以扩展ServiceException(极少数,比如需要识别一些中间件的特殊异常进行中间件调用方式的替换等)。所以业务系统自定义的异常归类如下:

【系统异常规范】

每个系统必须有一个统一的Error枚举类,以理赔为例会在common包中定义ClaimErrorEnum;自定义异常构造方法参数中必须要有此枚举类。
在说明业务系统错误枚举类之前,在提下业务错误枚举的父类接口BaseResultCode。由于枚举无法继承,所以一些通用的内容通过接口的方式让业务枚举实现,赋能给枚举类。业务错误枚举类通过接口实现继承了错误码的组装方法,业务系统只需要关注自己的3位错误码即可,同时也规范了业务枚举类的方法行为,可以统一拦截标准化处理。

接口BaseResultCode代码如下:

/*** 错误码枚举的实现基础接口 <br/>* 各业务系统的自定义枚举类需要实现此接口 <br/>** @author guosenlin* @date 2022/2/21 11:12*/
public interface BaseResultCode {/*** 部门编码:技术服务中心编码*/String DEPARTMENT_CODE = "10";/*** 应用编码:未知的应用编码*/String UNKNOWN_APP_CODE = "00";/*** 返回错误编码,定义为三位,自定义 2 是业务错误码 9 是系统错误码*** @return*/String getCode();/*** 返回错误描述* * @return*/String getMsg();/*** 应用编码* * @return*/String getAppCode();/*** 返回错误编码,由部门编码+应用编码+自定义编码拼接完成 <br/>* 1、部门编码:取默认值DEPARTMENT_CODE <br/>* 2、应用编码:子类实现的getAppCode()方法返回,一个应用是固定值 <br/>* 3、自定义编码:子类枚举实现getCode() <br/>** @return*/default String errorCode() {String appCode = getAppCode();String code = getCode();StringBuilder sb = new StringBuilder();sb.append(DEPARTMENT_CODE);// 00代表未知sb.append(appCode != null ? appCode : UNKNOWN_APP_CODE);sb.append(code);return sb.toString();}/*** 获得错误描述* * @return*/default String errorDesc() {return getMsg();}}

理赔服务业务错误枚举ClaimErrorEnum代码如下:

/*** 理赔错误码枚举类 <br/>* code:错误代码 <br/>* msg:错误描述 <br/>* * @author nidazhang* @date 2022-11-14*/
@Getter
@AllArgsConstructor
public enum ClaimErrorCodeEnum implements BaseResultCode {/**  201:理赔报案失败 */REPORT_FAILED_ERROR("201", "理赔报案失败"),/** 202:未查询到该报案号对应的案件信息 */NOT_QUERY_REPORT_INFO_ERROR("202", "未查询到该报案号对应的案件信息"),/**  203:案件状态非法  */ILLEGAL_REPORT_STATUS_ERROR("203", "案件状态非法"),/** 204:案件存在分支业务  */REPORT_HAS_BRANCH_BUSINESS_ERROR("204", "案件存在分支业务"),/** 205:调用保单锁单并抄单返回为空 */COPYING_POLICY_RETURN_NULL_ERROR("205", "调用保单锁单并抄单返回为空"),/**  206:批量解锁失败 */BATCH_UNLOCK_FAILED_ERROR("206", "批量解锁失败"),/** 207:案件已注销或者已结案,不允许对案件进行操作  */REPORT_STATUS_NOT_ALLOW_OPERATE_ERROR("207", "案件已注销或者已结案,不允许对案件进行操作"),/** 208:未查找到该分案或者材料已齐全,禁止补传  */PROHIBIT_MATERIAL_UPLOAD_ERROR("208", "未查找到该分案或者材料已齐全,禁止补传"),/** 901:调用保单锁单并抄单异常 */CALL_LOCK_AND_QUERY_POLICY_ERROR("901", "调用保单锁单并抄单异常"),/** 902:调用批量解锁系统异常 */CALL_BATCH_UNLOCK_ERROR("902", "调用批量解锁系统异常"),;private String code;private String msg;/*** 获得理赔系统的项目编码,为固定常量值* * @return*/@Overridepublic String getAppCode() {return ClaimReportConstants.CLAIM_APP_CODE;}
}

自定义异常代码如下:

异常基础类:

/*** 异常基础类** @author guosenlin* @data 2021/7/30 14:36*/
public abstract class BasicException extends RuntimeException {/*** 异常错误码枚举*/protected BaseResultCode errorCodeEnum;/*** 异常错误信息描述*/protected String errorMsg;/*** 异常上下文信息*/protected Map<String,Object> exceptionContext;/*** 获得错误码枚举信息* * @return*/public BaseResultCode getErrorCodeEnum() {return errorCodeEnum;}/*** 获得错误信息* * @return*/public String getErrorMsg() {if (errorMsg != null) {return errorMsg;}if (errorCodeEnum != null) {return errorCodeEnum.errorDesc();}return "";}/*** 获得错误码信息* * @return*/public String getErrorCode() {if (errorCodeEnum != null) {return errorCodeEnum.errorCode();}return "";}/*** 获得异常上下文信息* * @return*/public Map<String,Object> getExceptionContext() {return exceptionContext;}public BasicException() {super();}public BasicException(String message) {super(message);}public BasicException(Throwable cause) {super(cause);}public BasicException(String message, Throwable cause) {super(message, cause);}@Overridepublic String getMessage() {StringBuilder sb = new StringBuilder();sb.append(getErrorCode());sb.append(":");sb.append(getErrorMsg());sb.append(",");String message = super.getMessage();sb.append(message != null ? message : "");return sb.toString();}
}

理赔服务自定义异常示例:

/*** 理赔抄单异常** @author guosenlin* @data 2021/7/30 14:37*/
public class ClaimCopyPolicyException extends BasicException {private static final long serialVersionUID = 2237743543787228870L;public ClaimCopyPolicyException(String errMsg, Object... contextInfo) {this(errMsg, null, contextInfo);}public ClaimCopyPolicyException(Throwable e, Object... contextInfo) {this(null, e, contextInfo);}public ClaimCopyPolicyException(String errMsg, Throwable e, Object... contextInfo) {super(e);this.errorMsg = errMsg;this.contextInfo = contextInfo;}
}

【业务异常处理机制】

(1)系统框架异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseBean<?> methodArgumentNotValidErrorHandler(HttpServletRequest req,MethodArgumentNotValidException e) {BindingResult result = e.getBindingResult();StringBuilder errorMsg = new StringBuilder();if (result.hasErrors()) {List<ObjectError> allErrors = result.getAllErrors();allErrors.forEach(error -> errorMsg.append(error.getDefaultMessage()).append("!"));}log.info("参数校验失败,reqMethod:{},URI:{},错误信息:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(), errorMsg,ExceptionUtils.getStackTrace(e));return ResponseBean.fail(ResultCodeEnum.PARAM_EXCEPTION, errorMsg.toString());}@ExceptionHandler(IllegalArgumentException.class)public ResponseBean<?> illegalArgumentExceptionHandler(HttpServletRequest req, IllegalArgumentException e) {String message = e.getMessage();log.info("参数校验失败,reqMethod:{},URI:{},错误信息:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(), message,ExceptionUtils.getStackTrace(e));return ResponseBean.fail(ResultCodeEnum.PARAM_EXCEPTION, message);}@ExceptionHandler(BindException.class)public ResponseBean<?> bindErrorHandler(HttpServletRequest req, BindException e) {BindingResult result = e.getBindingResult();StringBuilder errorMsg = new StringBuilder();if (result.hasErrors()) {List<ObjectError> allErrors = result.getAllErrors();allErrors.forEach(error -> errorMsg.append(error.getDefaultMessage()).append("!"));}log.info("参数校验失败,reqMethod:{},URI:{},错误信息:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(), errorMsg,ExceptionUtils.getStackTrace(e));return ResponseBean.fail(ResultCodeEnum.PARAM_EXCEPTION, errorMsg.toString());}@ExceptionHandler(value = BizException.class)public ResponseBean<?> bizErrorHandler(HttpServletRequest req, BizException e) {log.info("业务异常,reqMethod:{},URI:{},上下文信息:{},异常信息:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(),LogUtils.toJsonOrElseToString(e.getContextInfo()), e.getErrorMsg(), ExceptionUtils.getStackTrace(e));return ResponseBean.fail(e.getErrorCodeEnum(), e.getErrorMsg());}@ExceptionHandler(value = ServiceException.class)public ResponseBean<?> serviceErrorHandler(HttpServletRequest req, ServiceException e) {log.warn("服务异常,reqMethod:{},URI:{},上下文信息:{},异常信息:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(),LogUtils.toJsonOrElseToString(e.getContextInfo()), e.getErrorMsg(), ExceptionUtils.getStackTrace(e));return ResponseBean.fail(e.getErrorCodeEnum(), e.getErrorMsg());}@ExceptionHandler(value = BasicException.class)public ResponseBean<?> basicExceptionHandler(HttpServletRequest req, BasicException e) {log.warn("服务异常,reqMethod:{},URI:{},上下文信息:{},异常信息:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(),LogUtils.toJsonOrElseToString(e.getContextInfo()), e.getErrorMsg(), ExceptionUtils.getStackTrace(e));return ResponseBean.fail(e.getErrorCodeEnum(), e.getErrorMsg());}@ExceptionHandler(value = Throwable.class)public ResponseBean<?> defaultErrorHandler(HttpServletRequest req, Throwable e) {log.error("系统异常,reqMethod:{},URI:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(),ExceptionUtils.getStackTrace(e));return ResponseBean.fail(ResultCodeEnum.UNKNOWN_EXCEPTION, e.getMessage());}
}
(2)主动捕获特殊处理
//抄单并锁单
List<ClaimPolicyBO> claimPolicyList = multiplePolicyLockIntegration.queryPolicySnapAndLock(claimReportBO);
try {//生成立案号
String registerNo = bizNoGenarateManager.generateRegistNo();
claimReportBO.setRegisterNo(registerNo);
claimReportBO.setRegisterTime(LocalDateTime.now());
claimReportBO.setStatus(CaseStatusEnum.FINISHED_REGIST);
//抄单填充报案号以及分案号以及锁单标记
for (ClaimCaseBO claimCaseBO : claimReportBO.getSubCaseList()) {for (ClaimReportPolicyBO casePolicyRelaBO : claimCaseBO.getCasePolicyRelationList()) {setPolicyLevelReportNo(claimReportBO, claimPolicyList, claimCaseBO, casePolicyRelaBO);
}
}
//入库保存
reportRepositoryService.saveClaimRegist(claimReportBO);
} catch (Exception exception) {//入库或者生成号码等异常时候,保单解锁掉
multiplePolicyLockIntegration.batchUnlockPolicy(claimReportBO);
throw new ServiceException(ClaimErrorCodeEnum.CLAIM_REGISTER_SYS_002_ERROR, exception, claimReportBO.getReportNo());
}
(3)异常信息在接口层的转换处理
1)针对系统异常处理:通过异常切面,将异常转换为错误码
@ExceptionHandler(BindException.class)
public ResponseBean<?> bindErrorHandler(HttpServletRequest req, BindException e) {BindingResult result = e.getBindingResult();
StringBuilder errorMsg = new StringBuilder();
if (result.hasErrors()) {List<ObjectError> allErrors = result.getAllErrors();
allErrors.forEach(error -> errorMsg.append(error.getDefaultMessage()).append("!"));
}
log.info("参数校验失败,reqMethod:{},URI:{},错误信息:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(), errorMsg,
ExceptionUtils.getStackTrace(e));
return ResponseBean.fail(ResultCodeEnum.PARAM_EXCEPTION, errorMsg.toString());
}
2)针对业务异常处理:获取异常的错误码,通过统一的包装类进行返回
@ExceptionHandler(value = BasicException.class)
public ResponseBean<?> basicExceptionHandler(HttpServletRequest req, BasicException e) {log.warn("服务异常,reqMethod:{},URI:{},上下文信息:{},异常信息:{},异常堆栈:{}", req.getMethod(), req.getRequestURI(),
LogUtils.toJsonOrElseToString(e.getContextInfo()), e.getErrorMsg(), ExceptionUtils.getStackTrace(e));
return ResponseBean.fail(e.getErrorCodeEnum(), e.getErrorMsg());
}

【寿险中台异常处理方案】

【寿险中台自定义异常使用规范】

(1)主动封装抛出自定义异常, 必须带有环境上下文信息,上下文信息要包含发生异常点的具体信息;比如在遍历保单险种的时候发生数据不合法系统异常,上下文中应该包含遍历的保单险种号,而不只是保单号信息;

(2)主动抛出异常无需在打印相关异常日志,因为异常中已包含堆栈信息和上下文信息,最终会在捕获处或者系统拦截器处打印;此时打印属于重复打印,会无谓增加日志量;

(3)只允许主动抛出自定义业务异常,不允许主动抛出java原生异常;可以将其他系统异常转化为自定义系统异常,原则上不允许主动抛出自定义的系统异常,比如以下代码使用的是java原生的IllegalStateException来封装业务枚举不存在异常,会导致异常捕获处理的复杂度和困难增加。
反例错误代码

(4)捕获异常必须进行有效业务处理,否则不允许catch自定义异常。有效业务处理包括打印error日志触发告警,保存异常记录数据,调用其他业务方法或者将受检异常转为自定义系统异常等;不允许捕获异常后只是做runtime类的异常转换或者打印日志等无用行为;

(5)程序员主动抛出异常必须清晰区分系统异常、业务异常和正确的错误枚举,业务异常和系统异常会导致处理逻辑的差异,影响业务处理结果;

(6)定义方法时如果会抛出自定义异常,必须在方法声明中声明异常信息,并在注释中说明不同错误枚举的产生业务场景,以便调用方根据情况自行决定异常处理策略;方法中调用其他服务产生的自定义异常也应在方法中声明;

(7)寿险中台的默认事务处理是在Facade层,当没有明确的事务代码或注解,事务会在facade层统一提交,异常传递至平台默认拦截器层时会导致事务回滚;

(8)非必要不允许自定义异常,必要场景为需要针对某类业务错误进行特殊业务处理,这种情况下通过自定义异常方便系统通过catch异常的方式实现。禁止只是为了某一通用异常业务概念进行自定义封装,比如核保不通过属于行业内比较通用的业务规则错误,但如果没有针对核保不通过的异常进行捕获处理需求,只需要使用通用的BizException+核保不通过错误枚举承载即可。

(9)寿险中台通用错误码规范
寿险中台错误码由 三部分组成 系统编码 + 应用编码 + 具体错误码,具体错误码包含通用错误码和自定义错误码,通用错误码包含常见的业务参数不合法、权限不足、不符合业务、系统未知错误等通用错误码。以寿险中台新契约核保不通过为例,寿险中台系统编码为SX,新契约编码为NCS,核保不通过的错误码排位201 ,则此最终接口错误码为 SX-NCS-001。

(10)应用自定义错误码需要遵守通用的号段规则
2XX代表业务错误码,应用依次编排自定义业务错误码;
9XX代表系统错误码,应用依次编排自定义系统错误码。

参考文章:
https://blog.csdn.net/qq_51628741/article/details/125873733

寿险业务系统异常处理方案相关推荐

  1. 人大金仓与中科软携手护航“寿险业务系统信创升级”

    近日,人大金仓与中科软科技股份有限公司的寿险业务系统完成兼容性认证,双方将携手共建金融行业信创生态. 近年来,金融机构与新一代信息技术紧密融合,不断加快产品服务创新和流程优化,提升金融服务质量和效率. ...

  2. 金融科技之:小额贷款业务系统建设方案分享

    小额贷款业务的金融科技发展是促进行业健康合规发展的首要前提.今天小编与大家分享关于小额贷款业务信息化系统建设的方案,供小额贷款行业的朋友参考,希望为行业的发展贡献一份力量. 根据小额贷款公司业务管理需 ...

  3. APS软件有哪些排程规则?有何异常处理方案?

    一.什么是APS系统 APS系统是什么意思?起什么作用?随着企业规模不断扩大,在经营管理方面会面临各种各样的问题,为了帮助解决此类问题,很多公司都会引入APS高级排程系统帮助进行生产管理的优化. AP ...

  4. 接口上线平滑处理_业务系统割接上线关键点和割接方案内容说明

    今天谈下业务系统割接上线方案的内容,对于大项目来说,业务系统在建设完成并UAT测试通过后,最终的割接上线往往是一个系统工程,不能有丝毫的马虎. 系统割接上线概述 临近项目上线,最近周末都在加班处理上线 ...

  5. centos6.9负载均衡方案完整配置(lvs+keepalived+pxc+nfs+业务系统)

    前期准备: NFS服务器:计算机名nfsserver,IP地址192.168.1.103,用于存放业务系统的数据. node1:计算机名PXC01,IP地址192.168.1.105,安装pxc系统和 ...

  6. 把虚拟机迁移到云服务器,VMware业务系统迁移上云方案

    背景 客户要将业务从自建的虚拟化数据中心迁移至UCloud,希望能够将多年前的VMware体系换到公有云体系.其中:客户希望上云过程不影响到现有业务: 去除机房托管的过保设备,减少不必要的支出: 减少 ...

  7. 服务器迁移虚拟化实施方案,VMVARE业务系统迁移上云方案

    背景 客户要将业务从自建的虚拟化数据中心迁移至UCloud,希望能够将多年前的VMware体系换到公有云体系.其中:客户希望上云过程不影响到现有业务: 去除机房托管的过保设备,减少不必要的支出: 减少 ...

  8. 统一门户与业务系统的sso整合技术方案(单点登录)

    一.单点登录(SSO,Single Sign On)整合 目前计划接入统一门户的所有业务系统均为基于JavaEE技术的B/S架构系统.由于统一门户的单点登录技术选用的是JA-SIG组织开发的Cas S ...

  9. 设计模式在业务系统中的应用

    简介: 本文的重点在于说明工作中所使用的设计模式,为了能够更好的理解设计模式,首先简单介绍一下业务场景.使用设计模式,可以简化代码.提高扩展性.可维护性和复用性.有哪些设计模式,这里就不再介绍了,网上 ...

最新文章

  1. php会话的销毁和退出,销毁PHP会话
  2. python while循环语句-Python中的while循环语句怎么用?
  3. 江南百景图自动刷水井,附带拼图教程
  4. excel处置4000行数据卡_【Excel技巧】在Excel中奇偶行提取和奇偶行筛选方法
  5. Java黑皮书课后题第3章:*3.18(运输成本)一个运输公司使用下面的函数,根据运输重量(以磅为单位)来计算运输成本(美元为单位)。编写程序,提示用户输入包裹重量,显示运输成本
  6. 模拟CA机构制作CA机构证书
  7. lintcode:买卖股票的最佳时机 III
  8. datareader对象直接转化为int_Integer、new Integer() 和 int 比较的面试题
  9. 滤波器开发之一:基于算数平均的平滑滤波器
  10. 刷bios工具_微星主板怎么更新bios版本?微星主板更新BIOS版本图文教程
  11. linux平台下C语言按进程名查找进程号pid
  12. 新媒体运营的“钱途”在哪里?
  13. php 实现防盗链,php中如何实现防盗链的示例
  14. day17 10.jdbc的crud操作
  15. 十二届蓝桥杯c++A组答案
  16. moodle平台安装及环境配置(包括安装过程详细截图)
  17. c语言标准库详解(五):stdio.h之直接IO/文件定位/错误处理
  18. IDEA主题SublimeTest3修改
  19. html5 drag移动位置,HTML5拖拽功能drag
  20. 【爬虫1】爬虫和反爬虫介绍

热门文章

  1. 电影院里一个男孩对他女友所做的全过程
  2. 如何使用css实现网站变灰效果
  3. 基于Java及Opencv3.4.1的图像线条识别
  4. 线条的张力怎么体现?构图合理大小合适线条流畅
  5. 服务器计时 - 如何查看 iPlayer 的性能
  6. 【BZOJ4976】宝石镶嵌 DP
  7. 2020 Multi-University Training Contest 6---- HDU--6836、Expectation(矩阵树)
  8. asp.net Excel导入导出
  9. Hive3.1.2 on Tez0.10.1的安装部署
  10. HTML5 SVG彩色斑点缓缓落下背景动画js特效