01

ThreadLocal,线程会被复用,要清除ThreadLocal

ConcurrentHashMap不能保证整体一致性

CopyOnWriteArrayList不适合写多的场景

02

加锁顺序-防止死锁

购物车批量加锁死锁问题

03

  • newFixedThreadPool是队列无限长
  • newCachedThreadPool是最大线程数无限多

    04

    Jedis正确使用方式

    不要直接new Jedis(),线程不安全
    JedisPool 是线程安全的连接池,Jedis 是非线程安全的单一连接
    image.png
    image.png

    05

    Feign和Ribbon的超时配置

  • 【坑】默认情况Feign的读取超时是1秒(坑)

  • 【坑】readTimeout&connectTimeout同时设置才生效

    1. feign.client.config.default.readTimeout=3000 #读信息时间(服务执行时间)
    2. feign.client.config.default.connectTimeout=3000 #建立连接时间
  • 配置Ribbon的超时时间

    1. ribbon.ReadTimeout=4000
    2. ribbon.ConnectTimeout=4000
  • 同时配置Feign和Ribbon超时,Feign配置生效

    Ribbon自动重试

    自动重试get head请求。Post不重试。
    Ribbon MaxAutoRetriesNextServer 参数默认为 1

    Httpclient最大连接数配置

    连接数要设置两个参数

  1. 总最大连接数:managerParams.setMaxTotalConnections(500); // 最大连接数
  2. 每个host的最大连接数
    • httpclient 3.1:setDefaultMaxConnectionsPerHost

每个host路由的默认最大连接,需要通过setDefaultMaxConnectionsPerHost来设置,
否则默认值是2

HttpClient重试策略

通过本文分析,可以得知HttpClient默认是有重试机制的,其重试策略是:

1.只有发生IOExecetion时才会发生重试
2.InterruptedIOException、UnknownHostException、ConnectException、SSLException,发生这4中异常不重试
3.get方法可以重试3次,post方法在socket对应的输出流没有被write并flush成功时可以重试3次。
4.读/写超时不进行重试
5.socket传输中被重置或关闭会进行重试
6.以及一些其他的IOException,暂时分析不出来。

06

@Transactional坑

  • 不能代理private方法
  • 内部调用事务方法不管用,必须先获取代理对象。

    • AopContext.currentProxy()).b();内部调用

      07

      数据库索引

      08

      hash & equals & compareTo

      09

      BigDecimal

      BigDecimal 初始化

  • 使用 BigDecimal 表示和计算浮点数,且务必使用字符串的构造方法来初始化

image.pngimage.png

scale unscaledValue precision

  1. public static void main(String[] args) {
  2. BigDecimal bigDecimal = new BigDecimal("0.0001100");
  3. System.out.println(bigDecimal.unscaledValue()); //1100
  4. System.out.println(bigDecimal.scale()); //7 10-7次方
  5. System.out.println(bigDecimal.precision()); //4,就表示unscaledValue的位数?
  6. }

BigDecimal比较大小

new BigDecimal(“1.0”).equals(new BigDecimal(“1”))

结果为false
原因:equals 比较的是 BigDecimal 的 value scale,1.0 的 scale 是 1,1 的scale 是 0,所以结果一定是 false。
比较大小使用compareTo()方法。

new BigDecimal(“1.0”).compareTo(new BigDecimal(“1”))==0

  • BigDecimal存入HashSet或map前要使用stripTrailingZeros 方法去掉尾部的零

    计算溢出

  • Math 类的 addExactsubtractExact 等 xxExact 方法进行数值运算,这些方法可以在数值溢出时主动抛出异常

  • 使用BigInteger,使用 BigInteger 的 longValueExact 方法,在转换出现溢出时,同样会抛出 ArithmeticException

    10

    list问题

  • 不能直接使用Arrays.asList来转换基本类型数组

  • Arrays.asList返回的List不支持增删操作
  • 对原始数组修改会影响到Arrays.asList返回的List。
  • List.subList进行切片会导致OOM。(因为sublist强引用了原始的List)
  • subList没有复制元素,和原始list元素修改会互相影响。

    11

  • Optional.ofNullable

    Mysql NULL的坑

  • Mysql中sum函数没有统计到数据返回null,而不是0

  • Mysql中count不统计null值
  • Mysql中=null不是判断,应该用is null

    12

    异常处理

    不要用log.error(“异常”,e.getMessage())
    用log.error(“异常”,e),包含堆栈信息。

    线程池异常处理

  • 线程内部异常处理

  • setUncaughtExceptionHandler
  • 全部默认:Thread.setDefaultUncaughtExceptionHandler

finally执行顺序

finally是将finally中的代码复制到所有逻辑的后面。
先把结果写到了栈返回地址上,再执行的finally

  1. public static int t1 () {
  2. int i = 1;
  3. try {
  4. return i;
  5. }finally {
  6. ++i;
  7. }
  8. }
  9. public static int t2 () {
  10. int i = 1;
  11. try {
  12. return i;
  13. }finally {
  14. return ++i;
  15. }
  16. }
  17. t1 方法返回的是 1 t2 方法返回的是 2
  18. public static User t1 () {
  19. User user = new User();
  20. user.setAge(12);
  21. try {
  22. return user;
  23. }finally {
  24. user.setName("zhangsan");
  25. }
  26. }
  27. 输出:User{age=12, name='zhangsan'}
  28. //返回的就是user指针,set修改堆中数据,int没有指针
  29. public static Integer t13 () {
  30. Integer i = new Integer(10000);
  31. try {
  32. return i;
  33. }finally {
  34. ++i;
  35. }
  36. }

为什么还是返回10000??不是对象修改Integer堆中的值么
Integer中的value字段被final修饰不可更改,所以integer++操作底层实际上会new一个新对象。

你真的了解try{ return }中的return吗? - 知乎

  1. class Test {
  2. public int aaa() {
  3. int x = 1;
  4. try {
  5. return ++x;
  6. } catch (Exception e) {
  7. } finally {
  8. ++x;
  9. }
  10. return x;
  11. }
  12. public static void main(String[] args) {
  13. Test t = new Test();
  14. int y = t.aaa();
  15. System.out.println(y);
  16. }
  17. }

image.png
image.png

13

logback的additivity=”false”

additivity false不继承root的appender

LevelFilter仅仅配置level是不行的,要配置onMatch和onMismatch

image.png

AsyncAppender异步日志问题

AsyncAppender配置参数
image.png
默认队列256,达到80%就丢弃<=info级别的日志
image.png

日志级别判断

image.png

14

文件读取

  • File.readAllLines无法读取超过内存大小的文件。
  • Files.lines可以读大文件
    • 与 readAllLines 方法返回 List 不同,lines 方法返回的是 Stream
  • Files.lines要释放资源

image.png

BufferedInputStream和手动缓冲的区别是什么?

image.png

  • 手动buffer缓冲的比较快。
  • 演示使用了固定大小的缓冲区,如果不是固定的io,那bufferedInputeStream就有了意义。

FileInputStream 与 BufferedInputStream 效率对比 - 戴仓薯 - 博客园

FileChannel

image.png

15

RedisTemplate序列化配置

image.png

  1. /**
  2. * @author Pu Zhiwei {@literal puzhiweipuzhiwei@foxmail.com}
  3. * create 2020-03-17 21:08
  4. * 继承 CachingConfigurerSupport,为了自定义生成 KEY 的策略。可以不继承。
  5. */
  6. @Configuration
  7. @EnableCaching
  8. public class RedisConfig extends CachingConfigurerSupport {
  9. @Value("${spring.cache.redis.time-to-live}")
  10. private Duration timeToLive = Duration.ZERO;
  11. /**
  12. * 配置Jackson2JsonRedisSerializer序列化策略
  13. * */
  14. private Jackson2JsonRedisSerializer<Object> serializer() {
  15. // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
  16. Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
  17. ObjectMapper objectMapper = new ObjectMapper();
  18. // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
  19. objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  20. objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  21. // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
  22. objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
  23. jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  24. return jackson2JsonRedisSerializer;
  25. }
  26. @Bean
  27. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  28. RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
  29. redisTemplate.setConnectionFactory(redisConnectionFactory);
  30. // 用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
  31. redisTemplate.setValueSerializer(serializer());
  32. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  33. // 使用StringRedisSerializer来序列化和反序列化redis的key值
  34. redisTemplate.setKeySerializer(stringRedisSerializer);
  35. // hash的key也采用String的序列化方式
  36. redisTemplate.setHashKeySerializer(stringRedisSerializer);
  37. // hash的value序列化方式采用jackson
  38. redisTemplate.setHashValueSerializer(serializer());
  39. redisTemplate.afterPropertiesSet();
  40. return redisTemplate;
  41. }
  42. @Bean
  43. public CacheManager cacheManager(RedisConnectionFactory factory) {
  44. RedisSerializer<String> redisSerializer = new StringRedisSerializer();
  45. // 配置序列化(解决乱码的问题)
  46. RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
  47. // 缓存有效期
  48. .entryTtl(timeToLive)
  49. // 使用StringRedisSerializer来序列化和反序列化redis的key值
  50. .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
  51. // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
  52. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer()))
  53. // 禁用空值
  54. .disableCachingNullValues();
  55. return RedisCacheManager.builder(factory)
  56. .cacheDefaults(config)
  57. .build();
  58. }
  59. }

Spring Boot 结合 Redis 序列化配置的一些问题 - 陕西颜值扛把子 - 博客园

Jackson自定义反序列化构造函数

image.png

RedisTemplate 能否存取 Value 是 Long
的数据呢?

在Integer区间内返回的是Integer,超过这个区间返回Long

16

Date类是不包含时区的

ZonedDateTime

ZonedDateTime保存时间,然后使用设置了 ZoneId 的 DateTimeFormatter 配合
ZonedDateTime 进行时间格式化得到本地时间表示

小写y是年,大写Y是weekyear

DateTimeFormatter日期解析

对比SimpleDateFormatter

  1. DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
  2. System.out.println(LocalDate.parse("2019-10-09").format(pattern));

日期计算

Java8之前使用Calendar,java8直接使用LocalDateTime
image.png

Period.between的坑

image.png
使用Instant代替 Date,LocalDateTime代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat。

18

  • 反射要注意泛型擦除

    getMethods 和 getDeclaredMethods区别

    getMethods 方法能获得当前类和父类的所有 public 方法,而
    getDeclaredMethods 只能获得当前类所有的 public、protected、package 和 private
    方法

    为什么需要桥接方法?

    泛型能够提供我们在编译时期就能进行类型检查的安全机制。

19

单例的 Bean 如何注入 Prototype 的 Bean

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)

@Scope中使用了proxyMode属性,被设置成了ScopedProxyMode.INTERFACES。这个属性是用于解决将会话请求作用域的bean注入到单例bean中所遇到的问题
bean的作用域、@Scope注解与proxyMode属性——Spring学习笔记_大蟒蛇来了-CSDN博客

使用AOP切面后,事务无效

AOP优先级

image.png

20

为什么 within(feign.Client+) 无法切入设置过 URL 的
@FeignClient ClientWithUrl

image.png

21

工厂+模版 消除重复代码

注解 + 反射,构造请求

https://github.com/JosephZhu1983/java-common-mistakes/tree/master/src/main/java/org/geekbang/time/commonmistakes/redundantcode/reflection

属性拷贝工具

mapstruct使用详解 - 淼淼之森 - 博客园

22

ResponseBodyAdvice 处理成功返回 @ExceptionHandler来处理业务异常

java-common-mistakes/APIResponseAdvice.java at f5c41fec1720ce01b7f30b38fd2fc1f57b099d25 · JosephZhu1983/java-common-mistakes

API Version版本号 HandlerMapping

java-common-mistakes/APIVersionHandlerMapping.java at f5c41fec1720ce01b7f30b38fd2fc1f57b099d25 · JosephZhu1983/java-common-mistakes

23

把一个热点 Key 的缓存查询压力,分散到多个 Redis 节点上吗?

可以给hotkey加上后缀,让这些hotkey打散到不同的redis实例上。

24

自定义HealthIndicator

25

异步处理需要消息补偿闭环

RabbitMQ如何需要fanout,可以使用匿名队列AnonymousQueue

RabbitMQ死信队列

死信队列配置

java-common-mistakes/RabbitConfiguration.java at master · JosephZhu1983/java-common-mistakes
RepublishMessageRecoverer 把消息发往了死信交换器

进入死信队列原因

cookie HttpOnly

防止document.cookie获取cookie内容

30

使用Bcrypt加密

《Java 业务开发常见错误 100 例》-极客时间 - 图21

使用AES-265-gcm【双向加密】

应用解密时,需要提供加密 ID、密文和加密时的 AAD 来解密。
java-common-mistakes/CipherService.java at f5c41fec1720ce01b7f30b38fd2fc1f57b099d25 · JosephZhu1983/java-common-mistakes