# 一、Tools
# 内置工具类
# JSON
- JSON验证解析、ToBean转换:链接 (opens new window)
@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
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
2
3
4
5
6
- Dao层
// xml中不需要其他额外的代码
IPage<EventDto> queryEventByCondition(IPage<EventDto> page, @Param("eventMetaReqDto") EventMetaReqDto eventMetaReqDto);
1
2
2
# 二、Utils
# 数据库
- 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
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Spring
- 获取/注入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
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
- 获取resources文件
ResourceUtils.getFile("classpath:template");
1
- 加载指定文件夹下的对象
String path = System.getProperty("user.dir") + "\\bridge-traffic-safety\\dll\\SDKInterface.dll";
SDKInterface sdkInterface = (SDKInterface) Native.loadLibrary(path, SDKInterface.class);
1
2
2
- 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
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
- 自定义资源文件夹
----------------- 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
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
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
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
# 三、第三方接口
# 天气
-
- 进入控制台创建应用,获取key
- 使用key及url请求获取数据
RestTemplate获取天气信息
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
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
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
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
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
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
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
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
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
2
3
4
5
6
实例二:
- 以前
if(user!=null){
dosomething(user);
}
1
2
3
2
3
- stream
Optional.ofNullable(user)
.ifPresent(u->{
dosomething(u);
});
1
2
3
4
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
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
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
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
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