JAVA-总结

本文总结了Java开发中的各项规范,包括使用Integer代替int,代码格式化,异常处理,减少重复调用,合理参数传递,枚举使用,事务处理,Mapper批量操作,XML配置,YML值引用,接口参数校验,排序与分页,equals方法使用,避免循环中交互,以及Jackson和Fastjson注解等。此外,还讨论了Redis的增量操作,MySQL的for_update用法,AOP参数匹配,MDC,MyBatis主键返回,动态表头生成,序列化,分布式锁,微服务概念,以及HTTP请求处理等问题。


(1)总结

1.所有的int都用Integer类型,包括出参和入参

2.写完代码git提交记得格式化

3.对于所有的校验,如果不通过,一定要返回错误的Result

4.代码不要重复调用,应该加一个变量接收,命名需规范-驼峰命名

5.只有一个参数的情况下,就不要用对象传递,一般参数 >3个才用对象,直接用参数Date或String即可

对象传递需进行转换
传入:req->dto
传出:bo->resp

6.所有的类型字段,状态字段,如果值超过2个,需要加枚举对象

枚举遍历

StateEnum[] stateEnums = StateEnum.values();
for (StateEnum stateEnum : stateEnums) {
	if(stateEnum.getCode()) {...}
}

7.多个写操作的语句,需要加事务

@Transactional 事务注解
throw new BusinessException(“msg”);
手动抛出自定义异常;
异常拦截器;

@AdminOptLogTitle(“预约操作接口”)
查询类的不需要加上这个注解,只有写操作,或者有敏感操作才加,加了这个操作就代表要存入数据库记录;

8.Mapper批量操作规范

批量增加,删除,更新

	<insert id="insertList" parameterType="java.util.List">
	  insert into reserve_rule_config (rule_date, rule_start_tm, rule_end_tm, rule_number) values
	  <foreach collection="list" separator="," item="item">
		  (#{item.ruleDate,jdbcType=DATE}, #{item.ruleStartTm,jdbcType=VARCHAR},
		  #{item.ruleEndTm,jdbcType=VARCHAR},#{item.ruleNumber,jdbcType=INTEGER})
	  </foreach>
	</insert>
	
	<delete id="deleteList" parameterType="java.util.List">
		delete from reserve_rule_config
		where reserve_rule_config_id in (
		<foreach collection="list" separator="," item="item">
			#{item}
		</foreach>
		)
	</delete>
	
	<update id="updateRecords" parameterType="java.util.List">
		<foreach collection="list" item="item" separator=";">
			update reserve_rule_config
			<set>
				<if test="item.ruleStartTm != null">
					rule_start_tm = #{item.ruleStartTm,jdbcType=VARCHAR},
				</if>
				<if test="item.ruleEndTm != null">
					rule_end_tm = #{item.ruleEndTm,jdbcType=VARCHAR},
				</if>
			</set>
			where reserve_rule_config_id = #{item.reserveRuleConfigId}
		</foreach>
	</update>

9.XML中Date类型一定要加jdbcType=DATE

	rule_date = #{ruleDate,jdbcType=DATE}

10.引用yml中配置的值

	@Value("${spring.profiles.active}")
	private String profiles;

11.什么时候定义code?

1.前端需要这个code做一些交互,或者提示,或者跳转,前端需要通过code写逻辑代码,比如code=401,前端要跳转到登陆页面
2.这个code很特殊,通过code方便直观的定位问题,比如通过code直接查询文档,知道报错原因,或者通过code的规则知道是哪个模块的报错,去找对应模块开发人员等
3.通用系统类code(如系统异常,服务器异常请重试),通用业务类code(用户不存在,状态已更新请刷新)

12.Pattern(正则表达式)

指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配。
典型的调用顺序

  Pattern p = Pattern.compile("a*b"); 
  Matcher m = p.matcher("aaaaab"); 
  boolean b = m.matches();

在仅使用一次正则表达式时,可以方便地通过此类定义 matches方法。此方法编译表达式并在单个调用中将输入序列与其匹配。

boolean b = Pattern.matches("a*b", "aaaaab");

等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。

13.对于接口传入的参数都要进行校验

为空校验、匹配校验、重合校验等,避免恶意调接口。

14.分页排序时,排序不要有二义性,二义性情况下可能会导致分页结果乱序,可以在后面追加一个主键排序

order by reserve_date desc,reserve_record_id

15.判断相等或不等时尽量使用equals

如果使用==或!=判断interger类型,当interger 类型的数据超过128的时候,此时保存的是引用地址,这时候相同的值比较也会报false,用equals就不会有问题。

16.循环里面不要涉及到与其他控件(数据库,mq(消息队列),外部应用之类的)交互,尽量一次性批量查询

网络通信是很耗时的,单个查询可能会忽略不计,在循环里面查询就会很慢

17.@JsonProperty和@JSONField

2个注解都是为了解决json字符串的某些属性名和JavaBean中的属性名匹配不上的问题。

  • @JsonProperty
    该注解为jackson包下的,在starter-web启动器下已经存在。
    使用方法,在bean属性或方法上加上该注解, 出现重复字段解决办法,@JsonProperty改为加在get方法上。
  • @JSONField
    该注解为fastjson包下的,启动类配置fastjson转换(如果仅仅是内部使用转换json字符串则不需要配置)
  • 需要注意的是在使用这2个注解进行转换时必须使用相应的方法否则不起作用(fastjson、jackson)
    fastjson忽略属性注解为@JSONField(serialize = false)
    jackson忽略属性注解为@JsonIgnore

18.Redis - increment 递增方法 | 处理防重复和并发问题

什么是increment?
Redis 的 INCR 命令将key中存储的数字值递增。如果key不存在,那么key的值会先被初始化为0,然后在执行 INCR 操作。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。本操作的值限制在 64 位(bit)有符号数字表示之内。
在这里插入图片描述
问题:redis是单线程操作,上图获取和插入是两个原子操作,并发可能会获取到相同的值。
解决:使用redis自带的自增函数increment()。

19. MySQL for update使用详解

  • 排他锁(S):select * from table_name where … for update;

for update是在数据库中上锁用的,可以为数据库中的行上一个排它锁。当一个事务的操作未完成时候,其他事务可以读取但是不能写入或更新。

for update 和 for update nowait区别(前者阻塞其他事务,后者拒绝其他事务)
for update锁住表或者锁住行,只允许当前事务进行操作(读写),其他事务被阻塞,直到当前事务提交或者回滚,被阻塞的事务自动执行
for update nowait 锁住表或者锁住行,只允许当前事务进行操作(读写),其他事务被拒绝,事务占据的statement连接也会被断开

20. args用来匹配方法参数。

SpringBoot使用AOP,PointCut详解
@Around(“paramCheckPointCut() && args(…, param)”)

  • args() : 匹配不带参数的方法。
  • args(java.lang.String) : 匹配方法参数是String类型的。
  • args(…) :带任意参数的方法。
  • args(java.lang.String,…) :匹配第一个参数是String类型的,其他参数任意。

21. MDC(Mapped Diagnostic Context,映射调试上下文)

MDC 是 log4j 、logback及log4j2 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。

当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。

22. mybatis新增后返回主键id

在xml文件,insert标签属性中,添加useGeneratedKeys和keyProperty,如图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

23. 根据对象动态生成表头

根据对象动态生成表头map,key为字段名,value为注解参数值

	public static Map<String, String> setHeaderField(Class<?> entityClass) {
		Map<String, String> tableHeader = new LinkedHashMap();
		Arrays.stream(entityClass.getDeclaredFields()).forEach(field -> {
			ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
			if (annotation != null) {
				tableHeader.put(field.getName(), annotation.value());
			}
		});
		return tableHeader;
	}

24. 使用try-with-resources自动关闭OutputStream

在Java中使用try-with-resources语句可以自动关闭资源,包括OutputStream,从而简化代码并减少资源泄露的风险。

public class OutputStreamExample {
    public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("example.txt")) {
            outputStream.write("Hello, World!".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,outputStream在try代码块执行完毕后会自动关闭,无需手动调用close()方法‌。

(2)问题

  1. 所以的实体类都必须实现序列化吗?
    序列化
  2. 系统部署到linux服务器(非分布式)定时任务会重复执行两次?
    分布式锁
  3. 分布式是否属于微服务?
    答案是属于。微服务的意思也就是将模块拆分成一个独立的服务单元通过接口来实现数据的交互。但是微服务不一定是分布式,因为微服务的应用不一定是分散在多个服务器上,他也可以是同一个服务器。这也是分布式和微服务的一个细微差别。
  4. group_concat函数详解?
SELECT parking_order_id,GROUP_CONCAT(DISTINCT `job_number`) as `job_number`
FROM `order_peer` GROUP BY parking_order_id;

在这里插入图片描述

  1. mysql锁表原因及解决方法?
    锁表的原理:数据库使用独占式封锁机制,当执行insert,update,delete语句时,对表进行锁住,直到发生commite 或者 回滚 或者退出数据库用户;
    解决方法:
show full processlist;

查看进程id,然后用kill id杀掉进程

  1. 聚簇索引的概念,回表操作?

  2. 惰性删除方案?

  3. 接口返回数据,为什么有一个或多个?
    带宽限制,高并发导致服务器崩;

  4. 面向过程编程—>面向对象编程?
    校验类的封装一个方法,通用类的封装public方法,独立逻辑的封装private方法。

  5. SpringBoot配置LocalDateTime自动返回前端时间戳
    直接在yml配置,前后端统一使用13位毫秒级时间戳进行交互。

spring:
  jackson:
    serialization:
      # Date返回前端转时间戳
      write-dates-as-timestamps: true
  1. feign远程调用失败
    okhttp 和 HttpURLConnection 不支持 @RequestBody + RequestMethod.GET,只有 httpclient 支持,默认使用 HttpURLConnection。
    解决方法:更改为Post请求,使用 httpclient。=
feign:
  hystrix:
    enabled: true
  okhttp:
    enabled: false
  httpclient:
    enabled: true
  1. 为什么使用RequestBody只能读取一遍请求数据流?
    那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()。
    解决办法:重写HttpServletRequestWrapper方法
    这种方法就是通过重写HttpServletRequestWrapper把request的保存下来,然后通过过滤器保存下来的request在填充进去,这样就可以多次读取request了。

  2. 为什么表单Content-Type为multipart/form-data时上传文件,后台数据的接收为空?
    request 特性
    1、在form表单提交是默认类型的时候可以通过request.getParameter()获取请求参数。但是当请求类型不是默认类型(例如enctype="multipart/form-data"类型,上传一个文件)的时候,需调用request.getInputStream()或request.getReader()方法来获取请求内容值。
    2、 request.getParameter(),request.getInputStream(),request.getReader()三个方法是不能共存的。调用其中一个就会影响剩下两个方法的使用,会导致request中的字节流为空。导致后面再次调用的时候读取不到。

public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        // 读取输入流里的请求参数,并保存到bytes里
        bytes = IOUtils.toByteArray(request.getInputStream());
    }
  1. Copy ‘xxx‘ to effectively final temp variable 问题解决
    在这里插入图片描述
    便用if函数对变量进行了校验,编译后发现报错了。筛查后发现,在这块代码下有一处匿名内部类调用了该变量。
    出错的原因是:内部类对象的生命周期会超过局部变量的生命周期。
    所以在匿名内部类中,只能调用被final修饰的局部变量,而在java 8中,如果局部变量被匿名内部类访问,那么该变量会自动被final隐性修饰。Lambda大致同理。

  2. SpringCloud Feign 的 @RequestParam 长度过长,导致服务降级
    @RequestParam会经过Query String的方式,即便设置了提交方式是post也会将参数拼接在url的后面,然而url传参是有长度限制的(类似于Get请求),会截断后面的参数,致使请求失败 。
    解决方法:把@RequestParam 的参数全部封装成一个对象,用@RequestBody 方式对该对象进行请求,这样就能解决长度过长的问题。

  3. maven依赖死活都下载不了
    在这里插入图片描述
    解决方法:项目想下载一个依赖,在idea中死都下不了,查看网上各种解决方案都没有效果,使用命令下载导入到maven仓库。先去中央仓库,下好包,使用cmd打开命令行输入。

mvn install:install-file -Dfile=d:\env\logback-kafka-appender-0.2.0-RC2.jar -DgroupId=com.github.danielwegener -DartifactId=logback-kafka-appender -Dversion=0.2.0-RC2 -Dpackaging=jar
  1. 调用Arrays.asList()产生的List中add、remove等方法时报异常
    这是由于Arrays.asList()返回的是Arrays的内部类ArrayList, 而不是java.util.ArrayList。Arrays的内部类ArrayList和java.util.ArrayList都是继承AbstractList,remove、add等方法在AbstractList中是默认throw UnsupportedOperationException而且不作任何操作。java.util.ArrayList重写这些方法而Arrays的内部类ArrayList没有重写,所以会抛出异常。

  2. MySQL 8.0 默认的排序规则为 utf8mb4_0900_ai_ci,使用脚本还原的表的排序规则可能是 utf8mb4_general_ci,之后又自己在库中建的表是 utf8mb4_0900_ai_ci,于是库中存在这两种排序规则,在做关联查询时就会报错。

解决方案
将库中所有表的排序规则改为一致,此处将 utf8mb4_general_ci 批量改为 utf8mb4_0900_ai_ci
生成修改脚本

SELECT 
	CONCAT('ALTER TABLE `', table_name, '` MODIFY `', column_name, '` ', DATA_TYPE, 
        '(', CHARACTER_MAXIMUM_LENGTH, ') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', 
        (CASE WHEN IS_NULLABLE = 'NO' THEN ' NOT NULL' ELSE '' END),
        (case when IFNULL(column_comment,'')='' then '' else concat(' COMMENT \'' , column_comment ,'\'') end),
        ';') as `sql`
FROM information_schema.COLUMNS
WHERE 1=1
	and TABLE_SCHEMA = 'LT_PMP_Dev' #要修改的数据库名称
	and DATA_TYPE = 'varchar'
	and COLLATION_NAME='utf8mb4_general_ci'

执行生成的sql语句

19.@Transactional事务注解和切换数据源的自定义注解冲突
Spring的@Transactional注解默认是基于单一数据源的配置。当你试图在同一个事务中切换数据源,Spring会抛出异常,因为它无法保证事务的一致性和隔离性。

核心原理详解

  1. 动态数据源路由机制
    AbstractRoutingDataSource 是 Spring 提供的抽象类
    通过重写 determineCurrentLookupKey() 返回数据源标识
    内部维护 Map 结构存储所有数据源(targetDataSources)

  2. 线程级数据源隔离
    ThreadLocal 特性:
    每个线程独立存储数据副本
    线程结束后自动释放资源
    完美支持 web 请求的线程隔离

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里 Jess

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值