记近三天开发遇到的问题

很长时间没写代码了,今天周三,从周一开始,写了三天,攒下很多遇到的错误和问题。这里记录一下。
后端使用spring boot,架构用了阿里的COLA。此为背景

1、request不能序列化错误

# 报错信息如下
It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)] 

原因:joinPoint.getArgs()返回的数组中携带有Request(HttpServletRequest)或者Response(HttpServletResponse)对象,导致序列化异常。
代码类似这样,LogAnnotation是自定义的用来记录日志的,切面会报错

    @LogAnnotation(logType = LogType.CREATE, subType = "协议", content = "新增协议", page = "新增协议页面")
    @PostMapping("/add")
    public SingleResponse<AddAgreementDTO> addAgreement(
    								HttpServletRequest request,
                                    @Validated @RequestBody AddAgreementCmd cmd)

很神奇的是,早上遇到这个问题时(当时因为idea缓存的问题快崩溃了),代码执行不了。现在虽然还是报错,但代码可以执行了。百度一下,都说要修改切面的代码。
但我其实只是想拿到header里面的token。所以我用

@RequestHeader(TOKEN_HEADER_NAME) String token

的方式解决了。
其实也可以把request以组件的方式放在controller,使用@Resource注解注入。

2、spring boot get请求传参下划线和驼峰转换问题

get请求的参数如果放在url中,那么这个参数的名字必须一样。
一般前端的参数是下划线形式,后端的实体名字是驼峰,为了自动转换,一般会配置下

jackson:
	# 设置属性命名策略,对应jackson下PropertyNamingStrategy中的常量值,SNAKE_CASE-返回的json驼峰式转下划线,json body下划线传到后端自动转驼峰式
	property-naming-strategy: SNAKE_CASE

但是如果get请求参数放在url中,不放在body就不会自动转换了。
一般的解决方法是:

    @GetMapping("/replace-list")
    public PageResponse<InfoDTO> getInfo(
            @RequestParam(value = "start_time", required = false) String startTime,
            @RequestParam(value = "end_time", required = false) String endTime,
            @RequestParam(value = "page_size", required = false, defaultValue = "10") Integer pageSize,
            @RequestParam(value = "page_index", required = false, defaultValue = "1") Integer pageIndex,
            @RequestParam(value = "need_total_count", required = false, defaultValue = "true") Boolean needTotalCount)

我实在无法忍受,有两点:一、这样写太丑。二、required = true 并不能校验字符串的长度。
如果想要校验还要在前面加上@NotBlank(message = “id不能为空”),那这个方法得多长。所以肯定要用DTO来接收参数,但是命名的问题要解决。
也是试了很多种方法。例如:

  • @ModelAttribute
  • 直接传参
  • @JSONField(name=“app_id”)

都没有用。后来想了个歪点子,其实我只需要修改get请求参数的setter就行了,因为spring就是根据参数名来调用DTO的setter方法。所以我改一下setter方法就行了。例如:

// Controller 中直接写DTO
@GetMapping("/list")
public PageResponse<ListDTO> getListByPageQry(Query qry) 

// Query 中 修改idea模板,生成setter 方法
    public void setStart_time(Date startTime) {
        this.startTime = startTime;
    }
    public void setEnd_time(Date endTime) {
        this.endTime = endTime;
    }
// 有些分页的参数是继承的,无法生成,那就只能自己写,好在每个分页的参数都一样,可以直接copy

这里留下修改后idea的setter模板,无非是把驼峰转为下划线。

#set($paramName = $helper.getParamName($field, $project))
#if($field.modifierStatic)
static ##
#end
void set$StringUtil.capitalizeWithJavaBeanConvention($StringUtil.sanitizeJavaIdentifier($helper.getPropertyName($field, $project).replaceAll("[A-Z]", "_$0").toLowerCase()))($field.type $paramName) {
#if ($field.name == $paramName)
    #if (!$field.modifierStatic)
    this.##
    #else
        $classname.##
    #end
#end
$field.name = $paramName;
}

3、gitlab CI分析

项目的CI 流程大概是

1、在.gitlab-ci.yml 中定义
mvn clean package -Dmaven.test.skip=true -U
./deploy.sh
2、在deploy中
docker build
docker push
3、在Dockerfile 中
java -cp
4、CD的话在jenkins 里面
ansible-playbook 
# deploy.yml
- name: stop older {{ service }} docker container
  shell: cd /data/app/{{service}} && docker-compose down
  register: the_output
- debug:
    var: the_output
# docker compose
version: "3"
services:
  baota:
   image: demo
   container_name: demo
   user: root
   restart: always
   network_mode: host
   environment:
    - PROFILE=dev
    - AGENT=localhost
   volumes:
    - /data/logs/demo/:/data/logs/demo/

这里说一下java -cp

用法: java [-options] class [args...]
           (执行类)
   或  java [-options] -jar jarfile [args...]
           (执行 jar 文件)
其中选项包括:
    -d32          使用 32 位数据模型 (如果可用)
    -d64          使用 64 位数据模型 (如果可用)
    -server       选择 "server" VM
                  默认 VM 是 server.

    -cp <目录和 zip/jar 文件的类搜索路径>
    -classpath <目录和 zip/jar 文件的类搜索路径>; 分隔的目录, JAR 档案
                  和 ZIP 档案列表, 用于搜索类文件。
    -D<名称>=<>
                  设置系统属性
    -verbose:[class|gc|jni]
                  启用详细输出
    -version      输出产品版本并退出

cp 就是classpath的意思,乍一看还以为是cp。
启动一个spring jar 包时:

  • 可以直接 java -jar xxx.jar 指定jar包
  • 也可以 java -cp /data:/data/config com.demo.Application ,指定类名

cp的好处是 不用打成一个大jar包,把每个jar包放入/data,然后指定类。像COLA 有6个子项目,如果只是修改了哪个子项目,就可以少替换一点。更节省也更安全。
虽然一般上线都是一起打包放入docker的····,但这是一种用法不是?

4、idea和maven的缓存问题

起因是,同事修改了依赖的包,上传了私服,我更新了jar包,本地仓库里的jar包日期也是最新的,idea就是编译不过,要不就是编译过了,但运行不过。还有我自己引入了新的jar包,但是idea能编译,却run 不起来,报错信息:

Java:程序包xxxx不存在

信息很好理解,但实在不知道原因,一番操作猛如虎,clean package install deploy,甚至Invalidate Caches ,定睛一看原地杵。
百度一下,打勾了 Delegate IDE build/run actions to Maven 。
意思是将IDE构建/运行操作委托给Maven,确实能运行了,但是每次run或debug之前都要build。
还有别的解决办法:在idea的终端 mvn idea:idea。
两种办法,都能解决,但我不能每次改了代码,都运行一遍吧,这个缓存的问题真的头疼!!!
因为这破问题,耽误了我多半天时间,有空真的要了解一下了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值