01
ThreadLocal,线程会被复用,要清除ThreadLocal
ConcurrentHashMap不能保证整体一致性
CopyOnWriteArrayList不适合写多的场景
02
加锁顺序-防止死锁
03
- newFixedThreadPool是队列无限长
-
04
Jedis正确使用方式
不要直接new Jedis(),线程不安全
JedisPool 是线程安全的连接池,Jedis 是非线程安全的单一连接
05
Feign和Ribbon的超时配置
【坑】默认情况Feign的读取超时是1秒(坑)
【坑】readTimeout&connectTimeout同时设置才生效
feign.client.config.default.readTimeout=3000 #读信息时间(服务执行时间)
feign.client.config.default.connectTimeout=3000 #建立连接时间
配置Ribbon的超时时间
ribbon.ReadTimeout=4000
ribbon.ConnectTimeout=4000
-
Ribbon自动重试
自动重试get head请求。Post不重试。
Ribbon MaxAutoRetriesNextServer 参数默认为 1Httpclient最大连接数配置
连接数要设置两个参数
- 总最大连接数:managerParams.setMaxTotalConnections(500); // 最大连接数
- 每个host的最大连接数
- httpclient 3.1:setDefaultMaxConnectionsPerHost
每个host路由的默认最大连接,需要通过setDefaultMaxConnectionsPerHost来设置,
否则默认值是2
- httpclient 4.2.3:setDefaultMaxPerRoute
HttpClient重试策略
- 关于HttpClient重试策略的研究 - kingszelda - 博客园
- HttpRequestRetryHandler 重写的一个小坑_fighter1945的专栏-CSDN博客
- [踩坑总结] HttpClient默认重试策略不处理SocketTimeoutException - 后端 - 掘金
- 总结-HttpClient-RetryHandler重试 - 夜黑人模糊灬的个人空间 - OSCHINA
通过本文分析,可以得知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方法
内部调用事务方法不管用,必须先获取代理对象。
使用 BigDecimal 表示和计算浮点数,且务必使用字符串的构造方法来初始化
scale unscaledValue precision
public static void main(String[] args) {
BigDecimal bigDecimal = new BigDecimal("0.0001100");
System.out.println(bigDecimal.unscaledValue()); //1100
System.out.println(bigDecimal.scale()); //7 10-7次方
System.out.println(bigDecimal.precision()); //4,就表示unscaledValue的位数?
}
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 类的 addExact、subtractExact 等 xxExact 方法进行数值运算,这些方法可以在数值溢出时主动抛出异常。
使用BigInteger,使用 BigInteger 的 longValueExact 方法,在转换出现溢出时,同样会抛出 ArithmeticException
10
list问题
不能直接使用Arrays.asList来转换基本类型数组
- Arrays.asList返回的List不支持增删操作
- 对原始数组修改会影响到Arrays.asList返回的List。
- List.subList进行切片会导致OOM。(因为sublist强引用了原始的List)
subList没有复制元素,和原始list元素修改会互相影响。
11
-
Mysql NULL的坑
Mysql中sum函数没有统计到数据返回null,而不是0
- Mysql中count不统计null值
-
12
异常处理
不要用log.error(“异常”,e.getMessage())
用log.error(“异常”,e),包含堆栈信息。线程池异常处理
线程内部异常处理
- setUncaughtExceptionHandler
- 全部默认:Thread.setDefaultUncaughtExceptionHandler
finally执行顺序
finally是将finally中的代码复制到所有逻辑的后面。
先把结果写到了栈返回地址上,再执行的finally
public static int t1 () {
int i = 1;
try {
return i;
}finally {
++i;
}
}
public static int t2 () {
int i = 1;
try {
return i;
}finally {
return ++i;
}
}
t1 方法返回的是 1, t2 方法返回的是 2;
public static User t1 () {
User user = new User();
user.setAge(12);
try {
return user;
}finally {
user.setName("zhangsan");
}
}
输出:User{age=12, name='zhangsan'}
//返回的就是user指针,set修改堆中数据,int没有指针
public static Integer t13 () {
Integer i = new Integer(10000);
try {
return i;
}finally {
++i;
}
}
为什么还是返回10000??不是对象修改Integer堆中的值么
Integer中的value字段被final修饰不可更改,所以integer++操作底层实际上会new一个新对象。
你真的了解try{ return }中的return吗? - 知乎
class Test {
public int aaa() {
int x = 1;
try {
return ++x;
} catch (Exception e) {
} finally {
++x;
}
return x;
}
public static void main(String[] args) {
Test t = new Test();
int y = t.aaa();
System.out.println(y);
}
}
13
logback的additivity=”false”
additivity false不继承root的appender
LevelFilter仅仅配置level是不行的,要配置onMatch和onMismatch
AsyncAppender异步日志问题
AsyncAppender配置参数
默认队列256,达到80%就丢弃<=info级别的日志
日志级别判断
14
文件读取
- File.readAllLines无法读取超过内存大小的文件。
- Files.lines可以读大文件
- 与 readAllLines 方法返回 List
不同,lines 方法返回的是 Stream
- 与 readAllLines 方法返回 List
- Files.lines要释放资源
BufferedInputStream和手动缓冲的区别是什么?
- 手动buffer缓冲的比较快。
- 演示使用了固定大小的缓冲区,如果不是固定的io,那bufferedInputeStream就有了意义。
FileInputStream 与 BufferedInputStream 效率对比 - 戴仓薯 - 博客园
FileChannel
15
RedisTemplate序列化配置
/**
* @author Pu Zhiwei {@literal puzhiweipuzhiwei@foxmail.com}
* create 2020-03-17 21:08
* 继承 CachingConfigurerSupport,为了自定义生成 KEY 的策略。可以不继承。
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Value("${spring.cache.redis.time-to-live}")
private Duration timeToLive = Duration.ZERO;
/**
* 配置Jackson2JsonRedisSerializer序列化策略
* */
private Jackson2JsonRedisSerializer<Object> serializer() {
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
redisTemplate.setValueSerializer(serializer());
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 使用StringRedisSerializer来序列化和反序列化redis的key值
redisTemplate.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
redisTemplate.setHashValueSerializer(serializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
// 配置序列化(解决乱码的问题)
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
// 缓存有效期
.entryTtl(timeToLive)
// 使用StringRedisSerializer来序列化和反序列化redis的key值
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer()))
// 禁用空值
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
Spring Boot 结合 Redis 序列化配置的一些问题 - 陕西颜值扛把子 - 博客园
Jackson自定义反序列化构造函数
RedisTemplate 能否存取 Value 是 Long
的数据呢?
的数据呢?
在Integer区间内返回的是Integer,超过这个区间返回Long
16
ZonedDateTime
ZonedDateTime保存时间,然后使用设置了 ZoneId 的 DateTimeFormatter 配合
ZonedDateTime 进行时间格式化得到本地时间表示
小写y是年,大写Y是weekyear
DateTimeFormatter日期解析
对比SimpleDateFormatter
DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
System.out.println(LocalDate.parse("2019-10-09").format(pattern));
日期计算
Java8之前使用Calendar,java8直接使用LocalDateTime
Period.between的坑
使用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优先级
20
为什么 within(feign.Client+) 无法切入设置过 URL 的
@FeignClient ClientWithUrl
21
工厂+模版 消除重复代码
注解 + 反射,构造请求
https://github.com/JosephZhu1983/java-common-mistakes/tree/master/src/main/java/org/geekbang/time/commonmistakes/redundantcode/reflection
属性拷贝工具
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 把消息发往了死信交换器
进入死信队列原因
- 消息消费时被拒绝(basic.reject / basic.nack),并且requeue = false
- 消息TTL过期
- 队列达到最大长度
SimpleMessageListenerContainer多线程消费
27
X-Forwarded-For 不靠谱
28
优惠券需要申请配额
钱进出和订单挂钩
29
Jackson防止xss转义序列化器
java-common-mistakes/XssJsonSerializer.java at f5c41fec1720ce01b7f30b38fd2fc1f57b099d25 · JosephZhu1983/java-common-mistakes
cookie HttpOnly
防止document.cookie获取cookie内容
30
使用Bcrypt加密
使用AES-265-gcm【双向加密】
应用解密时,需要提供加密 ID、密文和加密时的 AAD 来解密。
java-common-mistakes/CipherService.java at f5c41fec1720ce01b7f30b38fd2fc1f57b099d25 · JosephZhu1983/java-common-mistakes