Java --- springboot2请求参数处理

news2024/10/6 5:59:47

目录

一、请求参数处理

1.1、请求映射

1.2、自定义请求规则

1.3、请求处理

1.4、普通参数与基本注解

1.4.1、注解

1.5、参数处理原则

1.6、复杂参数 

1.7、自定义参数对象

1.8、自定义Converter


一、请求参数处理

1.1、请求映射

//    @RequestMapping(value = "/user",method = RequestMethod.GET)
    @GetMapping("/user")
    public String getUser(){
        return "GET-张三";
    }

   // @RequestMapping(value = "/user",method = RequestMethod.POST)
    @PostMapping("/user")
    public String saveUser(){
        return "POST-张三";
    }

    //@RequestMapping(value = "/user",method = RequestMethod.PUT)
    @PutMapping("/user")
    public String putUser(){
        return "PUT-张三";
    }
    //@RequestMapping(value = "/user",method = RequestMethod.DELETE)
    @DeleteMapping("/user")
    public String deleteUser(){
        return "DELETE-张三";
    }
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>你好,老莫</h1>
<form action="/user" method="get">
    <input value="get提交" type="submit">
</form>
<form action="/user" method="post">
    <input value="post提交" type="submit">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="DELETE">
    <input value="delete提交" type="submit">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT">
    <input value="put提交" type="submit">
</form>
</body>
</html>

需要手动开启rest风格

 原因是框架底层默认没有开启

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
   return new OrderedHiddenHttpMethodFilter();
}

 rest原理(表单提交时):

1、表单提交会带上_method=put

2、请求过来会被HiddenHttpMethodFilter拦截:①、请求是否正常,并且是post。②、获取到_method的值。③、兼容PUT、DELETE、PATCH。④、原生request(post),包装requestWrapper重写了getMethod方法,返回的是传入的值。⑤、过滤器链放行的时候用wrapper,后面的方法调用getMethod是调用requestWrapper的。

3、使用客户端工具如:postman就不会过filter 

1.2、自定义请求规则

@Configuration(proxyBeanMethods = false)
public class WebConfig {

    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_k");
        return hiddenHttpMethodFilter;
    }
}

1.3、请求处理

 springmvc功能分析都从org\springframework\web\servlet\DispatcherServlet.java -----》doDispatch()

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
         //找到当前请求使用哪个Handler(Controller的方法)处理
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
    
handlerMapping:处理器映射

RequestMappingHandlerMapping:保存了所有@RequestMapping和handler的映射规则。

 所有的请求映射在HandlerMapping中,springboot自动配置欢迎页的WelcomePageHandlerMapping,访问/能访问到index.html。请求进来,会按个尝试所有HandlerMapping看是否有请求信息,如果有就找到这个请求对应的handler,如果没有就到下一个HandlerMapping。

1.4、普通参数与基本注解

1.4.1、注解

@RestController
public class ParameterController {
    /**
     * @PathVariable 路径变量
     * @RequestHeader 获取请求头
     * @RequestParam 获取请求参数
     * @CookieValue 获取cookie的值
     * @param id
     * @param name
     * @param pv
     * @return
     */
    @GetMapping("/map/{id}/owner/{name}")
    public Map<String,Object> map(@PathVariable("id") Integer id,
                                  @PathVariable("name") String name,
                                  @PathVariable Map<String ,String > pv,
                                  @RequestHeader("User-Agent") String userAgent,
                                  @RequestHeader Map<String ,String > header,
                                  @RequestParam("age") Integer  age,
                                  @RequestParam("inters")List<String> inters,
                                  @RequestParam Map<String,String> params){
        HashMap<String, Object> map = new HashMap<>();
//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("header",header);
        map.put("age",age);
        map.put("inters",inters);
        map.put("params",params);
        return map;
    }

    /**
     * @RequestBody 获取请求体[post]
     * @param content
     * @return
     */
    @PostMapping("/save")
    public Map postMap(@RequestBody String content){
        HashMap<String, Object> map = new HashMap<>();
        map.put("content",content);
        return map;
    }
    /**
     *@MatrixVariable:矩阵变量
     * @param low
     * @param brand
     * @return
     * springboot默认是禁用了矩阵变量的功能
     * 手动开启:
     * 1、对于路径的处理:UrlPathHelper进行解析
     * 2、removeSemicolonContent:支持矩阵变量
     */
    @GetMapping("/car/{path}")
    public Map  sell(@MatrixVariable("low") Integer low,
                     @MatrixVariable("brand") List<String> brand,
                     @PathVariable String path){
        HashMap<String , Object> map = new HashMap<>();
        map.put("low",low);
        map.put("brand",brand);
        map.put("path",path);
        return map;
    }
    @GetMapping("/boss/{bossId}/{empId}")
    public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossId,
                    @MatrixVariable(value = "age",pathVar = "empId") Integer empId){
        HashMap<Object, Object> map = new HashMap<>();
        map.put("bossId",bossId);
        map.put("empId",empId);
        return map;
    }
}
@Controller
public class RequestController {
    @GetMapping("/goto")
    public String gotoPage(HttpServletRequest request){
        request.setAttribute("msg","转发成功");
        return "forward:/success";//转发到/success请求
    }

    /**
     * RequestAttribute :获取request域属性
     * @param msg
     * @param request
     * @return
     */
    @ResponseBody
    @GetMapping("/success")
    public Map success(@RequestAttribute("msg") String msg,
                       HttpServletRequest request){
        HashMap<String , Object> map = new HashMap<>();
        map.put("request-msg",request.getAttribute("msg"));
        map.put("requestAtt_msg",msg);
        return map;
    }
}
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                //不移除分号内容,矩阵变量功能生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }

//    @Override
//    public void configurePathMatch(PathMatchConfigurer configurer) {
//        UrlPathHelper urlPathHelper = new UrlPathHelper();
//        //不移除分号内容,矩阵变量功能生效
//        urlPathHelper.setRemoveSemicolonContent(false);
//        configurer.setUrlPathHelper(urlPathHelper);
//    }
}

1.5、参数处理原则

1、HandlerMapping中找到能处理请求的Handler(Controller中的方法)。 

2、在当前Handler中找到一个HandlerAdapter

HandlerAdapter:

 RequestMappingHandlerAdapter:支持方法上标注@RequestMapping

HandlerFunctionAdapter:支持函数式编程。

执行目标方法

// Actually invoke the handler.
//DispatcherServlet ---》doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 //执行目标方法

mav = invokeHandlerMethod(request, response, handlerMethod);

 //真正执行目标方法

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

//获取方法的参数值

Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

参数解析器

确定将要执行的目标方法的每一个参数的值

SpringMVC目标方法能写多少种参数类型,取决于参数解析器。

 

 当前解析器是否支持解析这种参数

支持就调用resolveArgument

返回值处理器

 确定目标方法每一个参数的值

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {

   MethodParameter[] parameters = getMethodParameters();
   if (ObjectUtils.isEmpty(parameters)) {
      return EMPTY_ARGS;
   }

   Object[] args = new Object[parameters.length];
   for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      args[i] = findProvidedArgument(parameter, providedArgs);
      if (args[i] != null) {
         continue;
      }
      if (!this.resolvers.supportsParameter(parameter)) {
         throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
      }
      try {
         args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
      }
      catch (Exception ex) {
         // Leave stack trace for later, exception may actually be resolved and handled...
         if (logger.isDebugEnabled()) {
            String exMsg = ex.getMessage();
            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
               logger.debug(formatArgumentError(parameter, exMsg));
            }
         }
         throw ex;
      }
   }
   return args;
}

 在所有参数解析器中判断哪个支持当前参数

@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
   HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
   if (result == null) {
      for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
         if (resolver.supportsParameter(parameter)) {
            result = resolver;
            this.argumentResolverCache.put(parameter, result);
            break;
         }
      }
   }
   return result;
}

解析这个参数的值 

调用HandlerMethodArgumentResolver的resolveArgument方法

1.6、复杂参数 

    @ResponseBody
    @GetMapping("/success")
    public Map success(HttpServletRequest request){
        HashMap<String , Object> map = new HashMap<>();
        Object mapkey = request.getAttribute("mapkey");
        Object modelkey = request.getAttribute("modelkey");
        Object requestkey = request.getAttribute("requestkey");
        map.put("mapkey",mapkey);
        map.put("modelkey",modelkey);
        map.put("requestkey",requestkey);
        return map;
    }

    /**
     * 这三个都可以在request域中放数据
     * @param map
     * @param model
     * @param request
     *
     * @param response
     * @return
     */
    @GetMapping("/param")
    public String param(Map<String,Object> map,
                        Model model,
                        HttpServletRequest request,
                        HttpServletResponse response){
        map.put("mapkey","mapvalue");
        model.addAttribute("modelkey","modelvalue");
        request.setAttribute("requestkey","requestvalue");
        Cookie cookie = new Cookie("key1","value1");
        response.addCookie(cookie);
        return "forward:/success";
    }

1.7、自定义参数对象

@Data
public class Persons {
    private String userName;
    private Date birth;
    private Integer age;
    private Pets pets;
}
@Data
public class Pets {
    private String name;
    private Integer age;
}
  /**
     * 数据绑定:页面提交的请求数据(GET,POST)都可以和对象属性进行绑定
     * @param persons
     * @return
     */
    @PostMapping("/saveuser")
    public Persons saveuser(Persons persons){
        return persons;
    }
<form action="/saveuser" method="post">
    姓名:<input name="userName" value="老莫"/></br>
    年龄:<input name="age" value="12"/></br>
    生日:<input name="birth" value="2011/10/1"/></br>
    宠物名:<input name="pets.name" value="金龙鱼"/></br>
    宠物年龄:<input name="pets.age" value="2"/></br>
    <input value="保存" type="submit">
</form>
ServletModelAttributeMethodProcessor 这个参数处理器支持

是否是简单类型

public static boolean isSimpleValueType(Class<?> type) {
   return (Void.class != type && void.class != type &&
         (ClassUtils.isPrimitiveOrWrapper(type) ||
         Enum.class.isAssignableFrom(type) ||
         CharSequence.class.isAssignableFrom(type) ||
         Number.class.isAssignableFrom(type) ||
         Date.class.isAssignableFrom(type) ||
         Temporal.class.isAssignableFrom(type) ||
         URI.class == type ||
         URL.class == type ||
         Locale.class == type ||
         Class.class == type));
}

@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

   Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
   Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

   String name = ModelFactory.getNameForParameter(parameter);
   ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
   if (ann != null) {
      mavContainer.setBinding(name, ann.binding());
   }

   Object attribute = null;
   BindingResult bindingResult = null;

   if (mavContainer.containsAttribute(name)) {
      attribute = mavContainer.getModel().get(name);
   }
   else {
      // Create attribute instance
      try {
         attribute = createAttribute(name, parameter, binderFactory, webRequest);
      }
      catch (BindException ex) {
         if (isBindExceptionRequired(parameter)) {
            // No BindingResult parameter -> fail with BindException
            throw ex;
         }
         // Otherwise, expose null/empty value and associated BindingResult
         if (parameter.getParameterType() == Optional.class) {
            attribute = Optional.empty();
         }
         bindingResult = ex.getBindingResult();
      }
   }

   if (bindingResult == null) {
      // Bean property binding and validation;
      // skipped in case of binding failure on construction.
      WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
      if (binder.getTarget() != null) {
         if (!mavContainer.isBindingDisabled(name)) {
            bindRequestParameters(binder, webRequest);
         }
         validateIfApplicable(binder, parameter);
         if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            throw new BindException(binder.getBindingResult());
         }
      }
      // Value type adaptation, also covering java.util.Optional
      if (!parameter.getParameterType().isInstance(attribute)) {
         attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
      }
      bindingResult = binder.getBindingResult();
   }

   // Add resolved attribute and BindingResult at the end of the model
   Map<String, Object> bindingResultModel = bindingResult.getModel();
   mavContainer.removeAttributes(bindingResultModel);
   mavContainer.addAllAttributes(bindingResultModel);

   return attribute;
}

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);

WebDataBinder:web数据绑定器,将请求参数的值绑定到指定的JavaBean里面

WebDataBinder利用它里面的Converters将请求数据转成指定的数据类型,再次封装到JavaBean中。

GenericConversionService:在设置每一个值的时候,找到它里面所有的converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean ----》Integer).

1.8、自定义Converter

<form action="/saveuser" method="post">
    姓名:<input name="userName" value="老莫"/></br>
    年龄:<input name="age" value="12"/></br>
    生日:<input name="birth" value="2011/10/1"/></br>
<!--    宠物名:<input name="pets.name" value="金龙鱼"/></br>-->
<!--    宠物年龄:<input name="pets.age" value="2"/></br>-->
    宠物:<input name="pets" value="金龙鱼,3">
    <input value="保存" type="submit">
</form>
  @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                //不移除分号内容,矩阵变量功能生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }

            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String , Pets>() {
                    @Override
                    public Pets convert(String source) {
                        if (!StringUtils.isEmpty(source)){
                            Pets pets = new Pets();
                            String[] split = source.split(",");
                            pets.setName(split[0]);
                            pets.setAge(Integer.valueOf(split[1]));
                            return pets;
                        }
                        return null;
                    }
                });
            }
        };

    }

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

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

相关文章

c#笔记-下载编辑器

IDE IDE是指集成开发环境&#xff08;Integrated Development Environment&#xff09;&#xff0c;是一种将软件开发所需的软件组合在一起&#xff0c;可以从同一操作界面以统一的操作方式使用的软件包。通常包括代码编辑器、编译器、链接器、调试器、测试工具、版本管理软件等…

自动化运维工具一Ansible Playbook语法实战

目录 一、Ansible Playbook剧本初识 1.1 Ansible Playbook 基本概述 1.1.1 什么是playbook 1.1.2 Ansible playbook 与AD-Hoc的关系 1.2 Ansible Playbook 书写格式 1.2.1安装NFS 服务 1.3 Playbook变量详解 1.3.1 使用 vars定义变量 1.3.2 使用 vars_flies定义变量 …

Java每日一练(20230501)

目录 1. 路径交叉 &#x1f31f;&#x1f31f; 2. 环形链表 &#x1f31f;&#x1f31f; 3. 被围绕的区域 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏…

17自由度人形机器人实现行走功能

1. 功能说明 本文示例将实现R307样机17自由度人形机器人行走的功能。该项目利用探索者平台制作&#xff0c;其驱动系统采用伺服电机。 2. 仿人形机器人结构设计 人型机器人是一种旨在模仿人类外观和行为的机器人&#xff08;robot&#xff09;&#xff0c;尤其特指具有和人类相…

VS快捷键大全 | 掌握这些快捷键,助你调试快人一步

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

Linux常见指令-1

本期我们开始学习Linux&#xff0c;首先我们来学习Linux的常见指令 目录 操作系统是什么 Linux下基本指令 1.ls指令 2.pwd指令 3.cd指令 4.touch指令 5.mkdir指令 6.rmdir指令 && rm 指令 7.man指令 8.cp指令 9.mv指令 10.cat指令 11.more指令 12.less指…

UE5中实现沿样条线创建网格体

本文是对UE官方教程&#xff1a;https://www.bilibili.com/video/BV1eU4y1c7XL的重现&#xff0c;原教程中通过构造函数实现非运行时执行脚本&#xff0c;并通过UE的样条线组件辅助创建路径网格体。该功能最终实现的效果如下&#xff1a; 1.创建基础蓝图 首先创建一个Actor蓝…

手把手教你 ,带你彻底掌握八大排序算法【数据结构】

文章目录 插入排序直接插入排序希尔排序 选择排序选择排序堆排序升序 交换排序冒泡排序快速排序递归hoare版本挖坑法前后指针版本 三数取中法选key递归到小的子区间时&#xff0c;可以考虑使用插入排序 归并排序递归实现非递归实现 排序算法复杂度以及稳定性 插入排序 直接插入…

计算机操作系统学习-引论

本专栏是对计算机操作系统学习的记录&#xff1a;《现代操作系统 第四版》&#xff0c;电子版的可以在评论区自取。 1 计算机硬件简介 操作系统与运行该操作系统的计算机硬件密切相关。如图1所示&#xff0c;我们可以将自己的计算机抽象为&#xff0c;CUP&#xff0c;内存和I/…

【数学建模】Day01——层次分析法

文章目录 1. 引出层次分析法1.1 思考问题1.2 平台借力1.3 分而治之的思想1.4 一致矩阵1.5 一致性检验1.6 一致矩阵计算权重1.7 判断矩阵求权重 2. 层次分析法2.1 定义2.2 具体步骤2.3 局限性 1. 引出层次分析法 1.1 思考问题 我们评价的目标是什么&#xff1f;我们为了达到这…

C语言:指针详解【进阶】后篇

目录 函数指针函数指针数组指向函数指针数组的指针回调函数 前言&#xff1a; 在C语言&#xff1a;指针详解【进阶】前篇中我们深入学习了字符指针&#xff0c;数组指针&#xff0c;指针数组以及数组传参和指针传参。我们对指针的应用有了较为深刻的认识&#xff0c;今天这里我…

BusterNet网络Python模型实现学习笔记之二

文章目录 一、squeeze函数的用法二、nn.CrossEntropyLoss函数三、isinstance函数四、定义冻结层 freeze_layers五、SummaryWriter 基础用法六、Python 基础语法1.变量嵌入到字符串2. enumerate() 函数3. 进度条库tqdm4. 字典&#xff08;dict&#xff09;展开为关键字参数&…

TAPFixer总结

相关工作 Menshen 检测属性用户写 et al检测属性就简单三个 未来工作&#xff1a; liveness; implicit; 数据集&#xff1b; 抽象方式合并&#xff1b;抽象规则配置&#xff1b;缓解谓词爆炸&#xff1b;concurrency的说明; 代码简化工作&#xff1b;给出能修复的漏洞种类 …

《基于光电容积法和机器学习的冠状动脉疾病患者出血风险预测》阅读笔记

目录 一、论文摘要 二、论文十问 三、论文亮点与不足之处 四、与其他研究的比较 五、实际应用与影响 六、个人思考与启示 参考文献 一、论文摘要 在冠状动脉疾病&#xff08;CAD&#xff09;患者的抗血栓治疗过程中&#xff0c;出血事件是关注的主要焦点。本研究旨在探讨…

浅谈一下布隆过滤器的设计之美

1 缓存穿透 2 原理解析 3 Guava实现 4 Redisson实现 5 实战要点 6 总结 布隆过滤器是一个非常有用的数据结构。它可以在大规模数据中高效地判断某个元素是否存在。布隆过滤器的应用非常广泛&#xff0c;不仅在搜索引擎、防垃圾邮件等领域中经常用到&#xff0c;而且在许多…

R语言单因素方差分析

R中的方差分析 介绍用于比较独立组的不同类型的方差分析&#xff0c;包括&#xff1a; 单因素方差分析&#xff1a;独立样本 t 检验的扩展&#xff0c;用于在存在两个以上组的情况下比较均值。这是方差分析检验的最简单情况&#xff0c;其中数据仅根据一个分组变量&#xff0…

【数据结构】七大排序总结

目录 &#x1f33e;前言 &#x1f33e; 内部排序 &#x1f308;1. 直接插入排序 &#x1f308;2. 希尔排序 &#x1f308;3. 直接选择排序 &#x1f308;4. 堆排序 &#x1f308;5. 归并排序 &#x1f308;6. 冒泡排序 &#x1f308;7. 快速排序 &#x1f33e;外部排序 &…

4 月份 火火火火 的开源项目

盘点 4 月份 GitHub 上 Star 攀升最多的开源项目&#xff0c;整个 4 月份最火项目 90% 都是 AI 项目&#xff08;准确的说&#xff0c;最近半年的热榜都是 AI 项目&#xff09; 本期推荐开源项目目录&#xff1a; 1. AI 生成逼真语音 2. 复旦大模型 MOSS&#xff01; 3. 让画中…

万万没想到在生产环境翻车了,之前以为很熟悉 CountDownLatch

前言 需求背景 具体实现 解决方案 总结 前言 之前我们分享了CountDownLatch的使用。这是一个用来控制并发流程的同步工具&#xff0c;主要作用是为了等待多个线程同时完成任务后&#xff0c;在进行主线程任务。然而&#xff0c;在生产环境中&#xff0c;我们万万没想到会…

【LeetCode】583. 两个字符串的删除操作

583. 两个字符串的删除操作&#xff08;中等&#xff09; 思路 这道题的状态定义和 1143. 最长公共子序列 相同&#xff0c;「定义一个 dp 数组&#xff0c;其中 dp[i]表示到位置 i 为止的子序列性质&#xff0c;并不是必须以 i 结尾」&#xff0c;此时 dp 数组的最后一位即为…