# 一、Tools

# 内置工具类

# JSON

@JsonProperty :Json别名

@JsonInclude:当属性的值为空(null或者"")时,不进行序列化。(也可以作用于类上)

@JsonIgnore:序列化时忽略属性

@JsonFormat:序列化时格式化时间

@JsonIgnoreProperties({"fieldName"}):作用于类上,表示序列化时忽略的属性(可多个)

# MyBatis-Plus

  • Config类:
/**
 * 分页插件配置类
 */
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
        return interceptor;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
  • Service层
public IPage<EventDto> getEventByCondition(EventMetaReqDto eventMetaReqDto) {
	// PageNums:页码 - PageSize:页面大小
    Page<EventDto> page = new Page<>(eventMetaReqDto.getPageNums(), eventMetaReqDto.getPageSize());
    IPage<EventDto> eventDtoList = eventMapper.queryEventByCondition(page, eventMetaReqDto);
    return eventDtoList;
}
1
2
3
4
5
6
  • Dao层
// xml中不需要其他额外的代码
IPage<EventDto> queryEventByCondition(IPage<EventDto> page, @Param("eventMetaReqDto") EventMetaReqDto eventMetaReqDto);
1
2

# 二、Utils

# 数据库

  1. MyBatis批量插入、更新
@Component
public class MyBatisBatchUtils {

    private static final int BATCH_SIZE = 1000;

    @Resource
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 批量添加、更新数据
     *
     * @param data 要操作的实体链表
     * @param mapperClass 执行操作的mapper
     * @param function mapper执行的具体方法
     * @param <T> 实体类
     * @param <U> Mapper类
     */
    public <T, U, R> int batchUpdateOrInsert(List<T> data, Class<U> mapperClass, BiFunction<T, U, R> function) throws Exception {
        int i = 0;
        SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        try {
            U mapper = batchSqlSession.getMapper(mapperClass);
            int size = data.size();
            for (T element : data) {
                function.apply(element, mapper);
                if ((i % BATCH_SIZE == 0) || i == size) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            // 非事务环境下强制commit,事务情况下该commit相当于无效
            batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());
        } catch (Exception e) {
            batchSqlSession.rollback();
            throw new Exception(e);
        } finally {
            batchSqlSession.close();
        }
        return i;
    }
}

batchUtils.batchUpdateOrInsert(数据集合, xxxxx.class, (item, mapper实例对象) -> mapper实例对象.insert方法(item));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 事务

  • 手动提交事务
// 需要先注入如下两个类
@Autowired
DataSourceTransactionManager dataSourceTransactionManager;
@Autowired
TransactionDefinition transactionDefinition;

public void saveAll(){
    //开启事务
    TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    try{
        this.save();
        //提交事务
        dataSourceTransactionManager.commit(transactionStatus);
    }catch(Exception e){
        //回滚事务
        dataSourceTransactionManager.rollback(transactionStatus);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Spring

  1. 获取/注入Bean(需要在使用的Bean上加@DependsOn("springContextUtil")注解)
@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取 Bean.
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }
    
    // 动态注入单例bean实例
    public static Object registerSingletonBean(String beanName,Object singletonObject){

        // 将applicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;

        // 获取BeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getAutowireCapableBeanFactory();

        // 动态注册bean.
        defaultListableBeanFactory.registerSingleton(beanName,singletonObject);

        // 获取动态注册的bean.
        return configurableApplicationContext.getBean(beanName);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  1. 获取resources文件
ResourceUtils.getFile("classpath:template");
1
  1. 加载指定文件夹下的对象
String path = System.getProperty("user.dir") + "\\bridge-traffic-safety\\dll\\SDKInterface.dll";
SDKInterface sdkInterface = (SDKInterface) Native.loadLibrary(path, SDKInterface.class);
1
2
  1. yml + 注解初始化账号信息
---------------------- java ----------------------

@Data
@Component
@ConfigurationProperties(prefix = "capture")
public class CameraInfo {

    private List<Camera> cameras;

    @Data
    @ToString
    public static class Camera implements Serializable {

        private static final long serialVersionUID = 1L;

        private String ip;

        private Integer port;

        private String username;

        private String password;

        private Integer factoryType;

        /**
         * 自定义客户端登录用户id
         */
        private Integer dwUser;

        private String bayonetCode;

        private String cameraCode;

        private String serviceAreaCode;

        private String serviceSubAreaCode;
    }

}

---------------------- yml ----------------------
capture:
  cameras:
    - ip: 44.9.87.195
      port: 37777
      username: admin
      password: Xinyue@123
      factoryType: 1
      dwUser: 10001
      bayonetCode: 232323
      cameraCode: 232323
      serviceAreaCode: 232344
      serviceSubAreaCode: 32323
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  1. 自定义资源文件夹
----------------- pom.xml ----------------
<build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <targetPath>BOOT-INF/classes/</targetPath>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**.properties</include>
                    <include>**.yml</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <-- libs指的是根目录下 -->
            <resource>
                <directory>libs</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/*.dll</include>
                </includes>
            </resource>
        </resources>
<build>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 字节处理

  • 16进制转byte数组
byte[] decodedHex = DatatypeConverter.parseHexBinary(str);
1
  • 合并两个字节数组
public static byte[] addBytes(byte[] data1, byte[] data2) { 
    byte[] data3 = new byte[data1.length + data2.length]; 
    System.arraycopy(data1, 0, data3, 0, data1.length); 
    System.arraycopy(data2, 0, data3, data1.length, data2.length); 
    return data3; 
}
1
2
3
4
5
6

# Crc16/字节转换处理

/**
 * @author Benjamin
 * @date 2022-08-26
 */
public class Crc16Util {
    /**
     * 获取源数据和验证码的组合byte数组
     *
     * @param strings 可变长度的十六进制字符串
     */
    public static byte[] getData(String... strings) {
        byte[] data = new byte[]{};
        for (String string : strings) {
            int x = Integer.parseInt(string, 16);
            byte n = (byte) x;
            byte[] buffer = new byte[data.length + 1];
            byte[] aa = {n};
            System.arraycopy(data, 0, buffer, 0, data.length);
            System.arraycopy(aa, 0, buffer, data.length, aa.length);
            data = buffer;
        }
        return getData(data);
    }

    /**
     * 获取源数据和验证码的组合byte数组
     *
     * @param sourceData 字节数组
     */
    private static byte[] getData(byte[] sourceData) {
        byte[] crc = getCrc16(sourceData);
        byte[] res = new byte[sourceData.length + crc.length];
        System.arraycopy(sourceData, 0, res, 0, sourceData.length);
        System.arraycopy(crc, 0, res, sourceData.length, crc.length);
        return res;
    }

    /**
     * 获取验证码byte数组,基于Modbus CRC16的校验算法
     */
    private static byte[] getCrc16(byte[] arrBuff) {
        int len = arrBuff.length;

        // 预置 1 个 16 位的寄存器为十六进制FFFF, 称此寄存器为 CRC寄存器。
        int crc = 0xFFFF;
        int i, j;
        for (i = 0; i < len; i++) {
            // 把第一个 8 位二进制数据 与 16 位的 CRC寄存器的低 8 位相异或, 把结果放于 CRC寄存器
            crc = ((crc & 0xFF00) | (crc & 0x00FF) ^ (arrBuff[i] & 0xFF));
            for (j = 0; j < 8; j++) {
                // 把 CRC 寄存器的内容右移一位( 朝低位)用 0 填补最高位, 并检查右移后的移出位
                if ((crc & 0x0001) > 0) {
                    // 如果移出位为 1, CRC寄存器与多项式A001进行异或
                    crc = crc >> 1;
                    crc = crc ^ 0xA001;
                } else {
                    // 如果移出位为 0,再次右移一位
                    crc = crc >> 1;
                }
            }
        }
        return intToBytes(crc);
    }

    /**
     * 将int转换成byte数组,低位在前,高位在后
     * 改变高低位顺序只需调换数组序号
     */
    private static byte[] intToBytes(int value) {
        byte[] src = new byte[2];
        src[1] = (byte) ((value >> 8) & 0xFF);
        src[0] = (byte) (value & 0xFF);
        return src;
    }

    /**
     * 将字节数组转换成十六进制字符串
     */
    public static String byteTo16String(byte[] data) {
        StringBuilder buffer = new StringBuilder();
        for (byte b : data) {
            buffer.append(byteTo16String(b));
        }
        return buffer.toString();
    }

    /**
     * 将字节转换成十六进制字符串
     * int转byte对照表
     * [128,255],0,[1,128)
     * [-128,-1],0,[1,128)
     */
    public static String byteTo16String(byte b) {
        StringBuilder buffer = new StringBuilder();
        int aa = (int) b;
        if (aa < 0) {
            buffer.append(Integer.toString(aa + 256, 16)).append(" ");
        } else if (aa == 0) {
            buffer.append("00 ");
        } else if (aa > 0 && aa <= 15) {
            buffer.append("0" + Integer.toString(aa, 16) + " ");
        } else if (aa > 15) {
            buffer.append(Integer.toString(aa, 16)).append(" ");
        }
        return buffer.toString();
    }

    /**
     * 字节转化为整型
     * @apiNote  https://cloud.tencent.com/developer/article/1011941
     * @param littleEndian 小端模式即低位在后,高位在前的模式
     */
    public static long bytesToInt(byte[] input, int offset, boolean littleEndian) {
        // 将byte[] 封装为 ByteBuffer
        ByteBuffer buffer = ByteBuffer.wrap(input,offset,4);
        if(littleEndian){
            // ByteBuffer 默认为大端(BIG_ENDIAN)模式
            buffer.order(ByteOrder.LITTLE_ENDIAN);
        }
        return buffer.getInt();
    }

    public boolean validCrc(byte[] data, byte[] sourceCrc) {
        byte[] crc16Generate = getCrc16(data);
        return Arrays.equals(crc16Generate, sourceCrc);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

# 三、第三方接口

# 天气

public WeatherInfoDto getWeatherInfo(Long location) {
    String key = "841e2ad14aac42259cc6eb630965f848";
    String url = "https://devapi.qweather.com/v7/weather/now?location={location}&key={key}";
	
	// 存放参数
    Map<String,String> params = new HashMap<>();
    params.put("key", key);
    params.put("location", String.valueOf(location));

    HttpHeaders headers = new HttpHeaders();
	//headers.add("accept-encoding", "gzip, deflate, br");
    headers.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8));
    headers.setContentType(MediaType.APPLICATION_JSON);
	
	// 存放header
    HttpEntity<String> entity = new HttpEntity<>(headers);

    byte[] responseBody = restTemplate.exchange(url, HttpMethod.GET, entity, byte[].class, params).getBody();
    String unGZipResult = unGZip(responseBody);

    WeatherInfoDto weatherInfoDto = WeatherInfoDto.builder().build();
    JSONObject jsonObject = JSONObject.parseObject(unGZipResult);

    try {
        weatherInfoDto = JSONObject.parseObject(jsonObject.getString("now"), WeatherInfoDto.class);
    } catch (Exception e) {
        e.printStackTrace();
    }

    return weatherInfoDto;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  • WeatherInfoDto
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WeatherInfoDto {

    private String obsTime;
    private String temp;
    private String feelsLike;
    private String icon;
    private String text;
    private String wind360;
    private String windDir;
    private String windScale;
    private String windSpeed;
    private String humidity;
    private String precip;
    private String pressure;
    private String vis;
    private String cloud;
    private String dew;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • Gzip文本解密
public static String unGZip(byte[] oResult) {
    try (InputStream inputStream = new ByteArrayInputStream(oResult);
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
        byte[] buf = new byte[4096];
        int len = -1;
        while ((len = gzipInputStream.read(buf, 0, buf.length)) != -1) {
            byteArrayOutputStream.write(buf, 0, len);
        }
        return new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8);
    } catch (IOException e) {
        e.printStackTrace();
    }

    return "";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 节假日

  • 获取某天的节假日名
public String getHolidayNameByDate(LocalDate sourceDate) {
    int year = sourceDate.getYear();
    String date = sourceDate.format(DateTimeFormatter.ofPattern("MM-dd"));

	// 存放redis
    String redisKey = "holiday-" + year;
    if (redisUtil.hasKey(redisKey)) {
        Map<String, String> holidayMap = (Map<String, String>) redisUtil.get(redisKey);
        if (holidayMap.containsKey(date)) {
            return holidayMap.get(date);
        }
        return "";
    }

    Map<String, String> dateHolidayMap = getDateHolidayMap(year);

    redisUtil.set(redisKey, (Serializable) dateHolidayMap);
    if (dateHolidayMap.containsKey(date)) {
        return dateHolidayMap.get(date);
    }
    return "";
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • 获取全年节假日Map
private Map<String, String> getDateHolidayMap(int year) {
    String url = "http://timor.tech/api/holiday/year/" + year;

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    headers.put("user-agent", Collections.singletonList("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"));

    HttpEntity<String> entity = new HttpEntity<>(headers);

    String responseBody = restTemplate.exchange(url, HttpMethod.GET, entity, String.class).getBody();
    JSONObject jsonObject = JSONObject.parseObject(responseBody);
    JSONObject jsonObject2 = JSONObject.parseObject(jsonObject.getString("holiday"));

    Map<String, String> dateHolidayMap = new HashMap<>();
    jsonObject2.forEach((k, v) -> {
        JSONObject jsonObject3 = JSONObject.parseObject(v.toString());

        String holidayName = jsonObject3.getString("name");
        String holidayTarget = jsonObject3.getString("target");
        dateHolidayMap.put(k, holidayTarget == null ? holidayName : holidayTarget);
    });
    return dateHolidayMap;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 四、Java Stream高级用法

filter

// 过滤出第一个,用Optional包装
Optional<RestroomEnvDto> restroomEnvDto2 = aRestroomDeviceTList.stream().filter(restroomEnvDto -> restroomEnvDto.getToiletType() != null && restroomEnvDto.getToiletType() == 2).findFirst();

// 存在则操作
restroomEnvDto2.ifPresent(restroomEnvDto -> {
    restroomEvnResDto2.setAmmoniaValue(restroomEnvDto.getValue() == null ? "0" : restroomEnvDto.getValue());
});
1
2
3
4
5
6
7

map

  • 去重
subServiceAreaInfoList.stream().collect(Collectors.toMap(ServiceSubAreaInfoDubboDto::getServiceAreaCode, Function.identity(), (key1, key2) -> key1));
1
  • 通用
subServiceAreaInfoList.stream().collect(Collectors.toMap(ServiceSubAreaInfoDubboDto::getSubAreaCode, serviceSubAreaInfoDubboDto -> serviceSubAreaInfoDubboDto))

serviceAreaService.getSubServiceAreaInfo().stream().collect(Collectors.toMap(ServiceSubAreaInfoDubboDto::getSubAreaCode, ServiceSubAreaInfoDubboDto::getServiceSubAreaName))
1
2
3

类型转化

  • stream to array
Short[] facDeviceIdList = deviceManufactureTList.stream().map(deviceManufactureT -> Short.valueOf(deviceManufactureT.getFactoryDeviceCode())).toArray(Short[]::new);
1

optional

实例一:

  • 以前
public String getCity(User user)  throws Exception{
    if(user!=null){
        if(user.getAddress()!=null){
            Address address = user.getAddress();
            if(address.getCity()!=null){
                return address.getCity();
            }
        }
    }
    throw new Excpetion("取值错误"); 
}
1
2
3
4
5
6
7
8
9
10
11
  • stream
public String getCity(User user) throws Exception{
    return Optional.ofNullable(user)
                   .map(u-> u.getAddress())
                   .map(a->a.getCity())
                   .orElseThrow(()->new Exception("取指错误"));
}
1
2
3
4
5
6

实例二:

  • 以前
if(user!=null){
    dosomething(user);
}
1
2
3
  • stream
Optional.ofNullable(user)
    .ifPresent(u->{
        dosomething(u);
});
1
2
3
4

实例三:

  • 以前
public User getUser(User user) throws Exception{
    if(user!=null){
        String name = user.getName();
        if("zhangsan".equals(name)){
            return user;
        }
    }else{
        user = new User();
        user.setName("zhangsan");
        return user;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
  • stream
public User getUser(User user) {
    return Optional.ofNullable(user)
        .filter(u->"zhangsan".equals(u.getName()))
        .orElseGet(()-> {
            User user1 = new User();
            user1.setName("zhangsan");
            return user1;
        });
}
1
2
3
4
5
6
7
8
9

# 五、Spring高级用法

YML初始化数据

如下所示,定义类集器成员变量,通过@ConfigurationProperties注解匹配YML中的capture数据,达到初始化的目的

  • CameraInfo:需要初始化的数据
@Data
@Component
@ConfigurationProperties(prefix = "capture")
public class CameraInfo {

    private List<Camera> cameras;

    @Data
    @ToString
    public static class Camera implements Serializable {

        private static final long serialVersionUID = 1L;

        private String ip;

        private Integer port;

        private String username;

        private String password;

        private Integer factoryType;

        /**
         * 自定义客户端登录用户id
         */
        private Integer dwUser;

        private String bayonetCode;

        private String cameraCode;

        private String serviceAreaCode;

        private String serviceSubAreaCode;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  • yml配置
capture:
  cameras:
    - ip: 44.9.87.195
      port: 37777
      username: admin
      password: ***
      factoryType: 1
      dwUser: 10001
      bayonetCode: 6666643460847788035
      cameraCode: 232323
    - ip: 44.9.87.200
      port: 37777
      username: admin
      password: ***
      factoryType: 1
      dwUser: 10002
      bayonetCode: 6666643460847788036
      cameraCode: 232323
    - ip: 44.9.87.205
      port: 37777
      username: admin
      password: ***
      factoryType: 1
      dwUser: 10003
      bayonetCode: 6666643460847788037
      cameraCode: 232323
    - ip: 44.9.87.210
      port: 37777
      username: admin
      password: ***
      factoryType: 1
      dwUser: 10004
      bayonetCode: 6666643460847788038
      cameraCode: 232323

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 六、Maven

导入外部Jar包

开发时候遇到需要引入外部SDK包,通过下面命令将jar包导入maven仓库,然后就可以在pom中引入

mvn install:install-file -Dfile=my-api.jar -DgroupId=com.tgvincent.demo -DartifactId=my-api -Dversion=1.0 -Dpackaging=jar
1
Last Updated: 2022-11-23 11:34:40 ├F10: AM┤