优雅的写Controller 层代码这样写才可以

前 言

本篇主要要介绍的就是controller层的处理,一个完整的后端请求由4部分组成:

  • 接口地址(也就是URL地址)
  • 请求方式(一般就是get、set,当然还有put、delete)
  • 请求数据(request,有head跟body)
  • 响应数据(response)
    本篇将解决以下3个问题:
  • 当接收到请求时,如何优雅的校验参数
  • 返回响应数据该如何统一的进行处理
  • 接收到请求,处理业务逻辑时抛出了异常又该如何处理

一、Controller层参数接收

常见的请求就分为 get 跟 post 两种

@RestController
@RequestMapping("/product/product-info")
public class ProductInfoController {@AutowiredProductInfoService productInfoService;@GetMapping("/findById")public ProductInfoQueryVo findById(Integer id) {...}@PostMapping("/page")public IPage findPage(Page page, ProductInfoQueryVo vo) {...}
}
  • @RestController: 之前解释过,@RestController = @Controller +
    ResponseBody。加上这个注解,springboot就会吧这个类当成controller进行处理,然后把所有返回的参数放到ResponseBody中
  • @RequestMapping:
    请求的前缀,也就是所有该Controller下的请求都需要加上/product/product-info的前缀
  • @GetMapping(“/findById”): 标志这是一个get请求,并且需要通过/findById地址才可以访问到
  • @PostMapping(“/page”): 同理,表示是个post请求
  • 参数:
    至于参数部分,只需要写上ProductInfoQueryVo,前端过来的json请求便会通过映射赋值到对应的对象中,例如请求这么写,productId就会自动被映射到vo对应的属性当中。
size : 1
current : 1productId : 1
productName : 泡脚

二、统一状态码

1. 返回格式
为了跟前端妹妹打好关系,我们通常需要对后端返回的数据进行包装一下,增加一下状态码,状态信息,这样前端妹妹接收到数据就可以根据不同的状态码,判断响应数据状态,是否成功是否异常进行不同的显示。当然这让你拥有了更多跟前端妹妹的交流机会,假设我们约定了1000就是成功的意思
如果你不封装,那么返回的数据是这样子的

{"productId": 1,"productName": "泡脚","productPrice": 100.00,"productDescription": "中药泡脚加按摩","productStatus": 0,
}

经过封装以后时这样子的

{"code": 1000,"msg": "请求成功","data": {"productId": 1,"productName": "泡脚","productPrice": 100.00,"productDescription": "中药泡脚加按摩","productStatus": 0,}
}

2. 封装ResultVo
这些状态码肯定都是要预先编好的,怎么编呢?写个常量1000?还是直接写死1000?要这么写就真的书白读的了,写状态码当然是用枚举拉

首先先定义一个状态码的接口,所有状态码都需要实现它,有了标准才好做事

public interface StatusCode {public int getCode();public String getMsg();
}

然后去找前端妹妹,跟他约定好状态码(这可能是你们唯一的约定了)枚举类嘛,当然不能有setter方法了,因此我们不能在用@Data注解了,我们要用@Getter

@Getter
public enum ResultCode implements StatusCode{SUCCESS(1000, "请求成功"),FAILED(1001, "请求失败"),VALIDATE_ERROR(1002, "参数校验失败"),RESPONSE_PACK_ERROR(1003, "response返回包装失败");private int code;private String msg;ResultCode(int code, String msg) {this.code = code;this.msg = msg;}
}

写好枚举类,就开始写ResultVo包装类了,我们预设了几种默认的方法,比如成功的话就默认传入object就可以了,我们自动包装成success

@Data
public class ResultVo {// 状态码private int code;// 状态信息private String msg;// 返回对象private Object data;// 手动设置返回vopublic ResultVo(int code, String msg, Object data) {this.code = code;this.msg = msg;this.data = data;}// 默认返回成功状态码,数据对象public ResultVo(Object data) {this.code = ResultCode.SUCCESS.getCode();this.msg = ResultCode.SUCCESS.getMsg();this.data = data;}// 返回指定状态码,数据对象public ResultVo(StatusCode statusCode, Object data) {this.code = statusCode.getCode();this.msg = statusCode.getMsg();this.data = data;}// 只返回状态码public ResultVo(StatusCode statusCode) {this.code = statusCode.getCode();this.msg = statusCode.getMsg();this.data = null;}
}

使用,现在的返回肯定就不是return data;这么简单了,而是需要new ResultVo(data);

@PostMapping("/findByVo")
public ResultVo findByVo(@Validated ProductInfoVo vo) {ProductInfo productInfo = new ProductInfo();BeanUtils.copyProperties(vo, productInfo);return new ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));
}

最后返回就会是上面带了状态码的数据了

三、统一校验

1. 原始做法
假设有一个添加ProductInfo的接口,在没有统一校验时,我们需要这么做

@Data
public class ProductInfoVo {// 商品名称private String productName;// 商品价格private BigDecimal productPrice;// 上架状态private Integer productStatus;
}
@PostMapping("/findByVo")
public ProductInfo findByVo(ProductInfoVo vo) {if (StringUtils.isNotBlank(vo.getProductName())) {throw new APIException("商品名称不能为空");}if (null != vo.getProductPrice() && vo.getProductPrice().compareTo(new BigDecimal(0)) < 0) {throw new APIException("商品价格不能为负数");}...ProductInfo productInfo = new ProductInfo();BeanUtils.copyProperties(vo, productInfo);return new ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));
}

这if写的人都傻了,能忍吗?肯定不能忍啊
2. @Validated参数校验
好在有@Validated,又是一个校验参数必备良药了。有了@Validated我们只需要再vo上面加一点小小的注解,便可以完成校验功能

@Data
public class ProductInfoVo {@NotNull(message = "商品名称不允许为空")private String productName;@Min(value = 0, message = "商品价格不允许为负数")private BigDecimal productPrice;private Integer productStatus;
}
@PostMapping("/findByVo")
public ProductInfo findByVo(@Validated ProductInfoVo vo) {ProductInfo productInfo = new ProductInfo();BeanUtils.copyProperties(vo, productInfo);return new ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));
}

运行看看,如果参数不对会发生什么?

我们故意传一个价格为-1的参数过去。

productName : 泡脚
productPrice : -1
productStatus : 1
{"timestamp": "2020-04-19T03:06:37.268+0000","status": 400,"error": "Bad Request","errors": [{"codes": ["Min.productInfoVo.productPrice","Min.productPrice","Min.java.math.BigDecimal","Min"],"arguments": [{"codes": ["productInfoVo.productPrice","productPrice"],"defaultMessage": "productPrice","code": "productPrice"},0],"defaultMessage": "商品价格不允许为负数","objectName": "productInfoVo","field": "productPrice","rejectedValue": -1,"bindingFailure": false,"code": "Min"}],"message": "Validation failed for object\u003d\u0027productInfoVo\u0027. Error count: 1","trace": "org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors\nField error in object \u0027productInfoVo\u0027 on field \u0027productPrice\u0027: rejected value [-1]; codes [Min.productInfoVo.productPrice,Min.productPrice,Min.java.math.BigDecimal,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [productInfoVo.productPrice,productPrice]; arguments []; default message [productPrice],0]; default message [商品价格不允许为负数]\n\tat org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:164)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:124)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:830)\n","path": "/leilema/product/product-info/findByVo"
}

大功告成了吗?虽然成功校验了参数,也返回了异常,并且带上"商品价格不允许为负数"的信息。但是你要是这样返回给前端,前端妹妹就提刀过来了,当年约定好的状态码,你个负心人说忘就忘?用户体验小于等于0啊!所以我们要进行优化一下,每次出现异常的时候,自动把状态码写好,不负妹妹之约!
3. 优化异常处理
首先我们先看看校验参数抛出了什么异常

Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors

我们看到代码抛出了org.springframework.validation.BindException的绑定异常,因此我们的思路就是AOP拦截所有controller,然后异常的时候统一拦截起来,进行封装!完美!

玩你个头啊完美,这么呆瓜的操作springboot不知道吗?spring mvc当然知道拉,所以给我们提供了一个@RestControllerAdvice来增强所有@RestController,然后使用@ExceptionHandler注解,就可以拦截到对应的异常。

这里我们就拦截BindException.class就好了。最后在返回之前,我们对异常信息进行包装一下,包装成ResultVo,当然要跟上ResultCode.VALIDATE_ERROR的异常状态码。这样前端妹妹看到VALIDATE_ERROR的状态码,就会调用数据校验异常的弹窗提示用户哪里没填好

@RestControllerAdvice
public class ControllerExceptionAdvice {@ExceptionHandler({BindException.class})public ResultVo MethodArgumentNotValidExceptionHandler(BindException e) {// 从异常对象中拿到ObjectError对象ObjectError objectError = e.getBindingResult().getAllErrors().get(0);return new ResultVo(ResultCode.VALIDATE_ERROR, objectError.getDefaultMessage());}
}

来看看效果,完美。1002与前端妹妹约定好的状态码

{"code": 1002,"msg": "参数校验失败","data": "商品价格不允许为负数"
}

四、统一响应

1. 统一包装响应
再回头看一下controller层的返回

return new ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));

开发小哥肯定不乐意了,谁有空天天写new ResultVo(data)啊,我就想返回一个实体!怎么实现我不管!好把,那就是AOP拦截所有Controller,再@After的时候统一帮你封装一下咯。
怕是上一次脸打的不够疼,springboot能不知道这么个操作吗?

@RestControllerAdvice(basePackages = {"com.bugpool.leilema"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {// response是ResultVo类型,或者注释了NotControllerResponseAdvice都不进行包装return !methodParameter.getParameterType().isAssignableFrom(ResultVo.class);}@Overridepublic Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {// String类型不能直接包装if (returnType.getGenericParameterType().equals(String.class)) {ObjectMapper objectMapper = new ObjectMapper();try {// 将数据包装在ResultVo里后转换为json串进行返回return objectMapper.writeValueAsString(new ResultVo(data));} catch (JsonProcessingException e) {throw new APIException(ResultCode.RESPONSE_PACK_ERROR, e.getMessage());}}// 否则直接包装成ResultVo返回return new ResultVo(data);}
}
  • @RestControllerAdvice(basePackages =
    {“com.bugpool.leilema”})自动扫描了所有指定包下的controller,在Response时进行统一处理
  • 重写supports方法,也就是说,当返回类型已经是ResultVo了,那就不需要封装了,当不等与ResultVo时才进行调用beforeBodyWrite方法,跟过滤器的效果是一样的
  • 最后重写我们的封装方法beforeBodyWrite,注意除了String的返回值有点特殊,无法直接封装成json,我们需要进行特殊处理,其他的直接new
    ResultVo(data);就ok了
@PostMapping("/findByVo")
public ProductInfo findByVo(@Validated ProductInfoVo vo) {ProductInfo productInfo = new ProductInfo();BeanUtils.copyProperties(vo, productInfo);return productInfoService.getOne(new QueryWrapper(productInfo));
}

此时就算我们返回的是po,接收到的返回就是标准格式了,开发小哥露出了欣慰的笑容

{"code": 1000,"msg": "请求成功","data": {"productId": 1,"productName": "泡脚","productPrice": 100.00,"productDescription": "中药泡脚加按摩","productStatus": 0,...}
}

2. NOT统一响应

开发小哥是开心了,可是其他系统就不开心了。举个例子:我们项目中集成了一个健康检测的功能,也就是这货

@RestController
public class HealthController {@GetMapping("/health")public String health() {return "success";}
}

公司部署了一套校验所有系统存活状态的工具,这工具就定时发送get请求给我们系统
是的,web项目的本质就是复读机。一旦发送的请求没响应,就会给负责人发信息(企业微信或者短信之类的),你的系统死啦!赶紧回来排查bug吧!让大家感受一下。每次看到我都射射发抖,早上6点!我tm!!!!!
在这里插入图片描述
好吧,没办法,人家是老大,人家要的返回不是

{"code": 1000,"msg": "请求成功","data": "success"
}

人家要的返回只要一个success,人家定的标准不可能因为你一个系统改。俗话说的好,如果你改变不了环境,那你就只能我****
「新增不进行封装注解」
因为百分之99的请求还是需要包装的,只有个别不需要,写在包装的过滤器吧?又不是很好维护,那就加个注解好了。所有不需要包装的就加上这个注解。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {
}

然后在我们的增强过滤方法上过滤包含这个注解的方法

@RestControllerAdvice(basePackages = {"com.bugpool.leilema"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {// response是ResultVo类型,或者注释了NotControllerResponseAdvice都不进行包装return !(methodParameter.getParameterType().isAssignableFrom(ResultVo.class)|| methodParameter.hasMethodAnnotation(NotControllerResponseAdvice.class));}

最后就在不需要包装的方法上加上注解

@RestController
public class HealthController {@GetMapping("/health")@NotControllerResponseAdvicepublic String health() {return "success";}
}

这时候就不会自动封装了,而其他没加注解的则依旧自动包装
在这里插入图片描述
每个系统都会有自己的业务异常,比如库存不能小于0子类的,这种异常并非程序异常,而是业务操作引发的异常,我们也需要进行规范的编排业务异常状态码,并且写一个专门处理的异常类,最后通过刚刚学习过的异常拦截统一进行处理,以及打日志

五、统一异常

1.异常状态码枚举, 既然是状态码,那就肯定要实现我们的标准接口StatusCode

@Getter
public enum  AppCode implements StatusCode {APP_ERROR(2000, "业务异常"),PRICE_ERROR(2001, "价格异常");private int code;private String msg;AppCode(int code, String msg) {this.code = code;this.msg = msg;}
}

2.异常类, 这里需要强调一下,code代表AppCode的异常状态码,也就是2000;msg代表业务异常,这只是一个大类,一般前端会放到弹窗title上;最后super(message);这才是抛出的详细信息,在前端显示在弹窗体中,在ResultVo则保存在data中

@Getter
public class APIException extends RuntimeException {private int code;private String msg;// 手动设置异常public APIException(StatusCode statusCode, String message) {// message用于用户设置抛出错误详情,例如:当前价格-5,小于0super(message);// 状态码this.code = statusCode.getCode();// 状态码配套的msgthis.msg = statusCode.getMsg();}// 默认异常使用APP_ERROR状态码public APIException(String message) {super(message);this.code = AppCode.APP_ERROR.getCode();this.msg = AppCode.APP_ERROR.getMsg();}}

3.最后进行统一异常的拦截,这样无论在service层还是controller层,开发人员只管抛出API异常,不需要关系怎么返回给前端,更不需要关心日志的打印

@RestControllerAdvice
public class ControllerExceptionAdvice {@ExceptionHandler({BindException.class})public ResultVo MethodArgumentNotValidExceptionHandler(BindException e) {// 从异常对象中拿到ObjectError对象ObjectError objectError = e.getBindingResult().getAllErrors().get(0);return new ResultVo(ResultCode.VALIDATE_ERROR, objectError.getDefaultMessage());}@ExceptionHandler(APIException.class)public ResultVo APIExceptionHandler(APIException e) {// log.error(e.getMessage(), e); 由于还没集成日志框架,暂且放着,写上TODOreturn new ResultVo(e.getCode(), e.getMsg(), e.getMessage());}
}

4.最后使用,我们的代码只需要这么写

if (null == orderMaster) {throw new APIException(AppCode.ORDER_NOT_EXIST, "订单号不存在:" + orderId);
}
{"code": 2003,"msg": "订单不存在","data": "订单号不存在:1998"
}

就会自动抛出AppCode.ORDER_NOT_EXIST状态码的响应,并且带上异常详细信息订单号不存在:xxxx。

后端小哥开发有效率,前端妹妹获取到2003状态码,调用对应警告弹窗,title写上订单不存在,body详细信息记载"订单号不存在:1998"。同时日志还自动打上去了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/92939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue+element开发Simple Admin后端管理系统页面

最近看到各种admin&#xff0c;头大&#xff0c;内容太多&#xff0c;根本不知道怎么改。所以制作了这个项目&#xff0c;只包含框架、和开发中最常用的表格和表单&#xff0c;不用自己从头搭建架构&#xff0c;同时也容易上手二次开发。可以轻松从其他开源项目整合到本项目。项…

基于Qt Creator开发的坦克大战小游戏

目录 介绍开发环境技术介绍安装说明项目目录设计思想项目介绍运行演示知识点记录Gitee源码链接 介绍 &#xff01;&#xff01;&#xff01;资源图片是从网上免费下载&#xff0c;源码都是原创&#xff0c;供个人学习使用&#xff0c;非盈利&#xff01;&#xff01;&#xff…

UE5报错及解决办法

1、编译报错&#xff0c;内容如下&#xff1a; Unable to build while Live Coding is active. Exit the editor and game, or press CtrlAltF11 if iterating on code in the editor or game 解决办法 取消Enable Live Coding勾选

Java编码

Java编码问题 Unicode与码点 所谓Unicode就是全世界的字符字典&#xff0c;也就是把字符给一个编号&#xff0c;这个编码就是码点。比如 2. 编码 由于这种分配的编码无论从占用空间角度&#xff0c;还是读取速度&#xff0c;以及逻辑划分角度&#xff0c;都不是完善。所以出…

了解”变分下界“

“变分下界”&#xff1a;在变分推断中&#xff0c;我们试图找到一个近似概率分布q(x)来逼近真实的概率分布p(x)。变分下界是一种用于评估近似概率分布质量的指标&#xff0c;通常用来求解最优的近似分布。它的计算涉及到对概率分布的积分或期望的估计

分布式搜索引擎es-3

文章目录 数据聚合聚合的种类RestAPI实现聚合 自动补全自定义拼音分词器自动补全查询案例&#xff1a;实现酒店搜索框自动补全自动补全的javaAPI实现搜索框自动补全 口述自动补全数据同步集群集群的分布式存储集群分布式查询集群故障转移 数据聚合 什么是聚合&#xff1f; 聚合…

【面试经典150 | 矩阵】旋转图像

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;原地旋转方法二&#xff1a;翻转代替旋转 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带…

【Cesium创造属于你的地球】相机系统

相机系统里面有setView&#xff0c;flyTo&#xff0c;lookAt&#xff0c;viewBoundingsphere这几种方法&#xff0c;以下是相关的使用方法&#xff0c;学起来&#xff01;&#xff01;&#xff01; setView 该方法可以直接切换相机视口&#xff0c;从而不需要通过一个飞入的效…

深度学习(1)---卷积神经网络(CNN)

文章目录 一、发展历史1.1 CNN简要说明1.2 猫的视觉实验1.3 新认知机1.4 LeNet-51.5 AlexNet 二、卷积层2.1 图像识别特点2.2 卷积运算2.3 卷积核2.4 填充和步长2.5 卷积计算公式2.6 多通道卷积 三、池化层 一、发展历史 1.1 CNN简要说明 1. 卷积神经网络&#xff08;Convolut…

基于SSM的公司项目管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

java复习回顾

文章目录 0 开发工具1. 对象和类、三大特性2. 成员/实例变量和实例变量&#xff08;this关键字&#xff09;3. 方法重载overload4. 构造方法和this关键字5. 继承6. 访问修饰符7. 方法重写8. 继承下的构造执行9. 多态9.1 向上转型9.2 向下转型9.3 多态的应用 0 开发工具 Maven是…

Acwing 907. 区间覆盖

Acwing 907. 区间覆盖 知识点题目描述思路讲解代码展示 知识点 贪心 题目描述 思路讲解 代码展示 #include <iostream> #include <algorithm>using namespace std;const int N 100010;int n;struct Range {int l, r;bool operator < (const Range &W) …

分页前后端完整代码

一、后端代码 User实体类要继承PageVo package com.like.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.like.common.PageVo; import…

借助ChatGPT的神奇力量,解锁AI无限可能!

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前学习C/C、算法、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&…

计算机毕业设计 基于SSM的宿舍管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

时间序列-AR模型与MA模型的原理与实现

文章目录 1 自回归模型AR Model1.1 自回归模型 vs 多元线性回归模型1.1.1 线性回归1.1.2 AR(1)模型1.1.3 AR(p)模型 1.2 AR建模问题 2 移动平均模型 MA Model2.1 MA模型的数学表示2.1.1 MA(1)模型2.2.2 MA(q)模型 2.2 MA建模问题 ARIMA模型是AR模型&#xff08;自回归模型&…

使用 Python 给 PDF 添加目录书签

0、库的选择——pypdf 原因&#xff1a;Python Version Support Python 3.11 3.10 3.9 3.8 3.7 3.6 2.7 pypdf>3.0 YES YES YES YES YES YES PyPDF2>2.0 YES YES YES YES YES YES PyPDF2 1.20.0 - 1.28.4 YES YES YES YES YES YES P…

91、Redis - 事务 与 订阅-发布 相关的命令 及 演示

★ 事务相关的命令 Redis事务保证事务内的多条命令会按顺序作为整体执行&#xff0c;其他客户端发出的请求绝不可能被插入到事务处理的中间&#xff0c; 这样可以保证事务内所有命令作为一个隔离操作被执行。 Redis事务同样具有原子性&#xff0c;事务内所有命令要么全部被执…

年度顶级赛事来袭:2023 CCF大数据与计算智能大赛首批赛题上线!

久等了&#xff01; 大数据与人工智能领域年度顶级盛事——2023 CCF大数据与计算智能大赛——首批赛题已上线&#xff0c;大赛火力全开&#xff0c;只等你来挑战&#xff01; 大赛介绍 CCF大数据与计算智能大赛&#xff08;CCF Big Data & Computing Intelligence Contes…

深入学习git

1、git原理及整体架构图 一些常用的命令 git add . 或 git add src/com/ygl/hello/hello.java 指定文件 git commit . 或 git commit src/com/ygl/hello/hello.java 指定文件 git push origin 分支名称 2、git stash的应用场景 场景一&#xff1a;你正在当前分支A开发&…