当前位置:网站首页>How to write controller layer code gracefully?
How to write controller layer code gracefully?
2022-06-29 23:55:00 【JAVA Chinese community】

This article mainly introduces controller The treatment of layer , A complete back-end request is made by 4 Part of it is made up of :1. Address of the interface ( That is to say URL Address )、2. Request mode ( It is commonly get、set, Of course, put、delete)、3. Request data (request, Yes head Follow body)、4. The response data (response)
This article will address the following 3 A question :
When a request is received , How to check parameters gracefully
How to uniformly process the return response data
Received request , How to handle exceptions thrown when processing business logic
One 、Controller Layer parameter receiving ( Basics , Skippable )
Common requests are divided into get Follow post2 Kind of
@RestController
@RequestMapping("/product/product-info")
public class ProductInfoController {
@Autowired
ProductInfoService productInfoService;
@GetMapping("/findById")
public ProductInfoQueryVo findById(Integer id) {
...
}
@PostMapping("/page")
public IPage findPage(Page page, ProductInfoQueryVo vo) {
...
}
}@RestController: I've explained before ,`@RestController` = `@Controller` + `ResponseBody`. Add this note ,springboot I will treat this class as `controller` To deal with , Then put all the returned parameters into `ResponseBody` in@RequestMapping: Prefix requested , That is all that should be `Controller` All requests under the need to add `/product/product-info` The prefix of@GetMapping("/findById"): Mark this is a `get` request , And need to pass `/findById` Address to access@PostMapping("/page"): Empathy , It means it's a `post` request` Parameters `: As for the parameters , Just write `ProductInfoQueryVo`, From the front `json` The request will be assigned to the corresponding object by mapping , For example, ask to write ,`productId` Will be automatically mapped to `vo` Corresponding attributes
size : 1
current : 1
productId : 1
productName : Soak the foot Two 、 Unified status code
1. Returns the format
To follow Front end sister Make a good relationship , We usually need to wrap the data returned from the back end , Add it up Status code , State information , In this way, the front-end sister can receive data according to different Status code , Judge Response data status , Whether it is successful or abnormal is displayed differently . Of course, this gives you more opportunities to communicate with your front-end sister , Suppose we make an agreement 1000 It means success
If you don't package , Then the returned data looks like this
{
"productId": 1,
"productName": " Soak the foot ",
"productPrice": 100.00,
"productDescription": " Chinese medicine feet soaking and massage ",
"productStatus": 0,
}It looks like this after packaging
{
"code": 1000,
"msg": " The request is successful ",
"data": {
"productId": 1,
"productName": " Soak the foot ",
"productPrice": 100.00,
"productDescription": " Chinese medicine feet soaking and massage ",
"productStatus": 0,
}
}2. encapsulation ResultVo
These status codes must be prepared in advance , How to make it up ? Write a constant 1000? Or just write it dead 1000? If you want to write in this way, you will really read the book for nothing , Write Status code Of course, enumeration is used to pull
First of all, define a ` Status code ` The interface of , all ` Status code ` All need to implement it , Only with standards can we do things
public interface StatusCode {
public int getCode();
public String getMsg();
}Then go to the front sister , Make an appointment with him for the status code ( This may be your only Agreement ) Enumerating classes , Of course not `setter` The method , So we can't use `@Data` Note the , We need to use it. `@Getter`
@Getter
public enum ResultCode implements StatusCode{
SUCCESS(1000, " The request is successful "),
FAILED(1001, " request was aborted "),
VALIDATE_ERROR(1002, " Parameter verification failed "),
RESPONSE_PACK_ERROR(1003, "response Return packaging failure ");
private int code;
private String msg;
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
}Write enumeration classes , Start writing `ResultVo` Packaging class , We preset several default methods , For example, if it succeeds, it will be passed in by default `object` That's all right. , We automatically pack it into `success`
@Data
public class ResultVo {
// Status code
private int code;
// State information
private String msg;
// Returns the object
private Object data;
// Manually set the return vo
public ResultVo(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
// The success status code is returned by default , Data objects
public ResultVo(Object data) {
this.code = ResultCode.SUCCESS.getCode();
this.msg = ResultCode.SUCCESS.getMsg();
this.data = data;
}
// Returns the specified status code , Data objects
public ResultVo(StatusCode statusCode, Object data) {
this.code = statusCode.getCode();
this.msg = statusCode.getMsg();
this.data = data;
}
// Only the status code is returned
public ResultVo(StatusCode statusCode) {
this.code = statusCode.getCode();
this.msg = statusCode.getMsg();
this.data = null;
}
}Use , Now the return is definitely not `return data;` It's so simple , But needs `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)));
}Finally, the data with the status code will be returned
3、 ... and 、 Unified verification
1. The original way
Suppose you have an add ProductInfo The interface of , When there is no unified verification , We need to do this
@Data
public class ProductInfoVo {
// Name of commodity
private String productName;
// commodity price
private BigDecimal productPrice;
// On the shelf status
private Integer productStatus;
}@PostMapping("/findByVo")
public ProductInfo findByVo(ProductInfoVo vo) {
if (StringUtils.isNotBlank(vo.getProductName())) {
throw new APIException(" Commodity name cannot be empty ");
}
if (null != vo.getProductPrice() && vo.getProductPrice().compareTo(new BigDecimal(0)) < 0) {
throw new APIException(" Commodity prices cannot be negative ");
}
...
ProductInfo productInfo = new ProductInfo();
BeanUtils.copyProperties(vo, productInfo);
return new ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));
}this if People who write are stupid , Can you bear it ? I can't bear it
2. @Validated Parameter checking
Fortunately, there are @Validated, It is also a necessary medicine for checking parameters . With @Validated We just need to vo Add a little note on it , The verification function can be completed
@Data
public class ProductInfoVo {
@NotNull(message = " The product name cannot be empty ")
private String productName;
@Min(value = 0, message = " The commodity price cannot be negative ")
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)));
} Run it , What happens if the parameters are wrong ?
We deliberately sent a price for -1 The parameters of the past
productName : Soak the foot
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": " The commodity price cannot be negative ",
"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 [ The commodity price cannot be negative ]\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"
} Is it a success ? Although the parameters were successfully verified , An exception is also returned , And bring them up. " The commodity price cannot be negative " Information about . But if you return to the front end like this , The front sister came over with a knife , It was agreed that year Status code , You A heartless person Say forget and forget ? user Experience less than or equal to 0 ah ! So we need to optimize , Every time something goes wrong , Automatic handle Status code Write well , Live up to your sister's promise !
3. Optimize exception handling
First, let's see what exceptions are thrown by the validation parameters
Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors We see that the code throws org.springframework.validation.BindException Binding exception for , So our idea is AOP Intercept all controller, Then the exceptions are intercepted uniformly , encapsulate ! perfect !

Play with you perfect , Such a stupid operation springboot Don't you know ?spring mvc Of course I know , So it provides us with a @RestControllerAdvice To enhance all @RestController, And then use @ExceptionHandler annotation , You can intercept the corresponding exception .
Here we intercept BindException.class Just fine . Finally, before returning , Let's wrap the exception information , Package as ResultVo, Of course, keep up ResultCode.VALIDATE_ERROR Exception status code of . So the front-end sister sees VALIDATE_ERROR The status code , The pop-up window of data verification exception will be called to prompt the user where the data has not been filled
@RestControllerAdvice
public class ControllerExceptionAdvice {
@ExceptionHandler({BindException.class})
public ResultVo MethodArgumentNotValidExceptionHandler(BindException e) {
// Get... From the exception object ObjectError object
ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
return new ResultVo(ResultCode.VALIDATE_ERROR, objectError.getDefaultMessage());
}
} Laikangkang effect , perfect .1002 The status code agreed with the front-end sister
{
"code": 1002,
"msg": " Parameter verification failed ",
"data": " The commodity price cannot be negative "
}Four 、 Unified response
1. Unified packaging response
Look back controller Layer return
return new ResultVo(productInfoService.getOne(new QueryWrapper(productInfo))); I'm not happy about it , Who has time to write every day new ResultVo(data) ah , I just want to return an entity ! I don't care how to achieve it ! All right. , That's it AOP Intercept all Controller, Again @After I will help you to package it when you are ready

I'm afraid it didn't hurt enough last time ,springboot Don't you know such an operation ?
@RestControllerAdvice(basePackages = {"com.bugpool.leilema"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
// response yes ResultVo type , Or annotated NotControllerResponseAdvice No packaging
return !methodParameter.getParameterType().isAssignableFrom(ResultVo.class);
}
@Override
public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
// String Type cannot be packaged directly
if (returnType.getGenericParameterType().equals(String.class)) {
ObjectMapper objectMapper = new ObjectMapper();
try {
// Package data in ResultVo Convert from inside to back json String to return
return objectMapper.writeValueAsString(new ResultVo(data));
} catch (JsonProcessingException e) {
throw new APIException(ResultCode.RESPONSE_PACK_ERROR, e.getMessage());
}
}
// Otherwise, it is directly packaged into ResultVo return
return new ResultVo(data);
}
}@RestControllerAdvice(basePackages = {"com.bugpool.leilema"})Automatically scan all the specified packagescontroller, stayResponseUnified processing shall be carried out whenrewrite
supportsMethod , in other words , When the return type is alreadyResultVo了 , Then there is no need to encapsulate , When not equal toResultVoOnly whenbeforeBodyWriteMethod , The effect is the same as that of the filterFinally, rewrite our encapsulation method
beforeBodyWrite, Note that except forStringThe return value of is a bit special , Cannot be encapsulated directly into json, We need to do something special , Other directnew ResultVo(data);Just ok 了
Finish off work , Kangkang effect
@PostMapping("/findByVo")
public ProductInfo findByVo(@Validated ProductInfoVo vo) {
ProductInfo productInfo = new ProductInfo();
BeanUtils.copyProperties(vo, productInfo);
return productInfoService.getOne(new QueryWrapper(productInfo));
} At this point, even if we return to po, The received return is in the standard format , Brother Kaifa showed a happy smile
{
"code": 1000,
"msg": " The request is successful ",
"data": {
"productId": 1,
"productName": " Soak the foot ",
"productPrice": 100.00,
"productDescription": " Chinese medicine feet soaking and massage ",
"productStatus": 0,
...
}
}2. NOT Unified response
Reason for not opening unified response
Brother Kaifa is happy , But other systems are not happy . for instance : We have integrated a Health detection The function of , That is, the goods
@RestController
public class HealthController {
@GetMapping("/health")
public String health() {
return "success";
}
} The company has deployed a set of tools to verify the survival status of all systems , This tool sends messages regularly get Request to our system
“ brother , Are you dead ?”
“ I'm not dead , roll ”
“ brother , Are you dead ?”
“ I'm not dead , roll ”
Yes ,web The essence of the project is the repeater . Once the request is sent No response , Will send a message to the person in charge ( Enterprise wechat or SMS ), Yours The system is dead ! Hurry back screening bug Well ! Just to give you a sense . Every time you see me Shoot shiver , morning 6 spot ! I tm!!!!!
ok , Can't , He's the boss , What people want to return is not
{
"code": 1000,
"msg": " The request is successful ",
"data": "success"
} Only one return is required success, The standards set by others cannot be changed because of your system . As the saying goes , If you can't change the environment , Then you can only let me ****
Add no encapsulation annotation
Because percent 99 Your request still needs packaging , Only a few don't need , Write the filter on the package ? It is not very easy to maintain , Then add a note . Add this note to all that do not need packaging .
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {
}Then filter the method containing this annotation on our enhanced filtering method
@RestControllerAdvice(basePackages = {"com.bugpool.leilema"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
// response yes ResultVo type , Or annotated NotControllerResponseAdvice No packaging
return !(methodParameter.getParameterType().isAssignableFrom(ResultVo.class)
|| methodParameter.hasMethodAnnotation(NotControllerResponseAdvice.class));
}
...Finally, add notes on the methods that do not need packaging
@RestController
public class HealthController {
@GetMapping("/health")
@NotControllerResponseAdvice
public String health() {
return "success";
}
} At this time, it will not be automatically encapsulated , Others without comments are still automatically packaged 
5、 ... and 、 Unification exception
Each system has its own Business exceptions , such as Inventory cannot be less than 0 A subclass , This exception is not a program exception , It is an exception caused by a business operation , We also need to conduct standardized choreography business Abnormal status code , And write a specialized Exception class , Finally, through the just learned Abnormal interception Unified treatment , And play journal
Exception status code enumeration , Since it is a status code , Then we must implement our standard interface `StatusCode`
@Getter
public enum AppCode implements StatusCode {
APP_ERROR(2000, " Business exceptions "),
PRICE_ERROR(2001, " The price is abnormal ");
private int code;
private String msg;
AppCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
}Exception class , It needs to be emphasized here ,`code` representative `AppCode` Exception status code of , That is to say 2000;`msg` representative ` Business exceptions `, This is just a big category , Generally, the front end will be placed in the pop-up window `title` On ; Last `super(message);` This is the detail of the throw , Display on the front end ` Popup form ` in , stay `ResultVo` Keep it in `data` in
@Getter
public class APIException extends RuntimeException {
private int code;
private String msg;
// Manual setting exception
public APIException(StatusCode statusCode, String message) {
// message Used to set the details of the error thrown by the user , for example : Current price -5, Less than 0
super(message);
// Status code
this.code = statusCode.getCode();
// Status code matching msg
this.msg = statusCode.getMsg();
}
// Default exception uses APP_ERROR Status code
public APIException(String message) {
super(message);
this.code = AppCode.APP_ERROR.getCode();
this.msg = AppCode.APP_ERROR.getMsg();
}
}Finally, intercept the unified exception , So no matter in `service` Layer or `controller` layer , Developers just throw `API abnormal `, There is no need to return the relationship to the front end , There is no need to care ` journal ` Printing of
@RestControllerAdvice
public class ControllerExceptionAdvice {
@ExceptionHandler({BindException.class})
public ResultVo MethodArgumentNotValidExceptionHandler(BindException e) {
// Get... From the exception object ObjectError object
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); Since the logging framework has not been integrated yet , For the time being , write TODO
return new ResultVo(e.getCode(), e.getMsg(), e.getMessage());
}
}Finally using , Our code just needs to be written like this
if (null == orderMaster) {
throw new APIException(AppCode.ORDER_NOT_EXIST, " The order number does not exist :" + orderId);
}{
"code": 2003,
"msg": " The order does not exist ",
"data": " The order number does not exist :1998"
} It will automatically throw out AppCode.ORDER_NOT_EXIST State code response , With exception details The order number does not exist :xxxx. The development efficiency of the back-end brother , Front end sister gets 2003 Status code , Call the corresponding warning pop-up ,title write The order does not exist ,body Detailed information records " The order number does not exist :1998". meanwhile journal And it was automatically typed !666! My old friend likes it three times in a row !
Source of the article :https://bugpool.blog.csdn.net/article/details/105610962

Previous recommendation A dependency to fix Spring Boot Profile desensitization
Interview shock 58:truncate、delete and drop Of 6 Big difference !
Interview shock 55:delete、drop、truncate What's the difference? ?

边栏推荐
- After crossing, she said that the multiverse really exists
- Top ten securities companies: "bulldozer market" reappearance
- AI赋能新零售,「智」胜之道在于生态思维|数智夜话直播精选摘录
- MetaQ集群安装测试
- Solr基础操作5
- Sword finger offer 14- I. cut rope
- What is online account opening? In addition, is it safe to open a mobile account?
- I wonder if I can open an account today? In addition, is it safe to open an account online now?
- 【微信小程序】认识小程序项目的基本组成结构
- 6.29日刷题题解
猜你喜欢

西门子低代码 9.14版本: 满足不同需求

Implementation of aut, a self-developed transport layer protocol for sound network -- dev for dev column

Jetpack之Room的使用,结合Flow

Leetcode(680)——验证回文字符串 Ⅱ

AI empowers new retail, the way to win "wisdom" lies in ecological thinking | selected excerpts from digital intelligence night talk live broadcast

Leetcode 1385. Distance value between two arrays

333333333333333333333333333333

Matlab exercises -- program control process exercise

matplotlib matplotlib中plt.hist()参数解释

Matplotlib plt Hist() parameter explanation
随机推荐
Solr basic operation 4
RRDtool 画MRTG Log数据
Virtual machine online migration based on openstack
简单理解B树和B+树
Ingenious application of golang generics to prevent null pointer errors of variables and structural fields
Fund information disclosure
请指教什么是在线开户?另外,手机开户安全么?
【一起上水硕系列】Day 8
Solr基础操作4
新钛云服荣膺“2022爱分析 · IT运维厂商全景报告”云管理平台CMP 代表厂商!...
Metaq cluster installation test
一步步教你在Edge浏览器上安装网风笔记
@Scheduled注解的坑,我替你踩了
Effective self summary of remote communication | community essay solicitation
深度学习的历史
Zhongang Mining: Fluorite helps the construction and development of lithium battery in fluorine industry
Bee common configuration
solo 博客皮肤导入 skins 文件夹后出现 500 错误
[LeetCode] 只出现一次的数字【136】
Gradle serialization 7- configuration signature


