开发小技巧
- 在进行返回值处理时,经常需要以下代码逻辑。如果为空,需要new一个对象来返回,避免空指针的错误。
if (CollectionUtils.isEmpty(list)){
return new ArrayList<>();
}
//换成下面的写法
return Optional.ofNullable(list).orElse(new ArrayList<>());
- 流的打开和关闭,经常使用到try{}catch{}finial{},来处理,
try{ InputStream in = file.getInputStream();
ExcelReader reader = ExcelUtil.getReader(in,0);
List<Map<String,Object>> excelInfo = reader.readAll();
}catch(IOException e){
e.addSuppressed();
}finally {
in,close();
reader.close();
}
//替换成下列的写法
try( InputStream in = file.getInputStream();
ExcelReader reader = ExcelUtil.getReader(in,0);
){
List<Map<String,Object>> excelInfo = reader.readAll();
}catch(IOException e){
e.addSuppressed();
}
- 在解析表格时候,经常遇到错误,我们需要将错误反馈给前端用户,并且停止操作,但如果你一有错误就返回,如果表格里有10处错误,每次用户修改后提交,你才反馈给他下一个错误,会让用户的体验性很差,正确的做法应该是一次性的将表格里的错误返回给客户。
//这里我们使用到Stringbuffer
StringBuffer error = new StringBuffer("表格的错误有");
if (check){
error.append("-").append("表格的公司信息错误");
}
if (check){
error.append("-").append("表格的第").append(n).append("行信息错误");
}
//不要使用字符串拼接,由于多次的拆装,拼接字符串的效率不如StringBuffer; “-”是让前端可以有一个符号进行分割
- 在对一些返回的数据是泛型时,进行转换到自己所用的类型时,一定不要想当然的进行转化,举例使用了时间的转化,由于项目一般都使用LocalDate和LocalDateTime,而Object不支持转化到这两个类型,需要Date中间转化。
private LocalDateTime checkTime(Object obj){
try{
Date time = (Date) obj;
Instant instant = time.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant,zoneId);
return localDateTime;
}catch (ClassCastException e){
e.printStackTrace();
}
return null;
}
//在平时的转化里,例如,你不做异常处理,等待的就是报错
String s = "1234nk";
Long l = Long.valueOf(s);
-
打印日志的使用,在本地开发时候,经常疏忽日志的输出,因为我们可以debug模式里进行调节自己的bug,但是当项目部署以后,有很多人使用,这时候你的日志输出保存就显得尤为的重要。在控制台输出的日志会被快速的更替掉。比较多,单独写一篇文章说明
-
短信,邮件,标题的自动生成模板,正常这些我们都是按照某种规则来进行的字符串拼接,很多时候会在代码里给写死了,但如果客户需要更改模板,你就要去修改代码了。正确的应该是写在配置文件里。
//配置文件里
file:
title:
temp: 关于{} 在{} {} {}的委派书
//使用
@Value("${file.title.temp}")
private String temp;
@Test
public void test2(){
//使用
String str = StrUtil.forma(temp,"刘姥姥","麻辣烫有限公司","董事长","任职");
log.info("生成的标题{} " ,str);
}
//结果
2021-11-20 14:36:12.746 INFO 40232 --- [ main] com.example.demo.DemoApplicationTests : 生成的标题关于刘姥姥 在麻辣烫有限公司 董事长 任职的委派书
-
同级的类调用,在springboot里,业务层有,service,rest,而在做业务逻辑处理时候,需要查询其他表数据时候,为了方便可能会把另一张表的mepper也引入方便操作。这并不会引起什么不合规,但是任务一张表的查询的方法,都很可能能被复用,应该在其对应的service里实现其方法,会是代码逻辑更加的合规。(该点是我的项目经理给的建议。)
-
枚举的使用,在实现一些业务逻辑时候,经常需要一些判断,例如类型,type,1为学习,2为教师,经常写“1”.equal(type)。本质上并不会出现逻辑错误的,但对于代码的整体的可读性变差,对于不是写这行代码的人员而言就要去看type的划分。但如果使用了枚举:如下示例,可以直接从枚举看到对于的类型。如果对应关系改变,只需要在枚举里更改。
@AllArgsConstructor
public enum SexTypeEnum {
MAN("1","男"),
WOMAN("2","女");
private String code;
private String name;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 在业务开发的时候,详情的查询往往是从列表中点击进入的,也就意味着,每条数据的id是查询时存 在的,且准确的,那在查询详情时候,往往忽略了查询对象的判null操作,因为出现为空的概率几乎很难出现,如果只是返回当前操作对象,你返回null也不会出错,但是当你详情的界面里有新的业务逻辑,就需要加上查询对象为空判
10.操作数组的细节注意,请看下面的代码:
public class Learn {
public static void main(String[] args) {
List<ForecastVO> forecastVOList = new ArrayList<>();
forecastVOList.add(new ForecastVO());
forecastVOList.add(new ForecastVO("测试","500","7","5","西南风","冷风"));
List<ForecastVO> temp = new ArrayList<>();
temp.add(forecastVOList.get(0));
temp.forEach(t->{
t.setDate("测试2");
});
forecastVOList.forEach(t->{
System.out.println(t.getDate());
});
}
}
代码非常简单,给大家看输出的结果:
这样子写时,大家都会注意到,但在开发时,由于逻辑比较的多,当我们进行list的操作时,前后的两个数组往往在不同的方法和不同的类里面,往往第一个list的包含着所有的信息,而temp的只是临时处理,但是这一处理往往会影响主list,因为本质是存的是同一个对象。
11.redis使用的坑
在使用redis存储map的数据时,需要配置序列化,建议使用和springboot默认的序列化一致,使用Jackson序列化。以下是一个可直接使用的配置类示例:
package com.nchu.weather.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis配置,使用json序列化
*
* @author nchu
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
- 在使用redis存储map时候,相同的key重复存储不会刷新缓存里的数据,这个问题困惑了我好久,找到了官方文档看也没找到头绪,一直以为哪个地方需要配置,最后查看了源码,基本确认时不会刷新,所有每次刷新就需要先删除再重新存取。参考文章:http://t.zoukankan.com/wangzun-p-13397739.html
- 控制层的接口类型有get,post,put,delete,四种,其中不能用get 和delete来接收数组。