🍎数据处理
接口接收数据并进行数据清洗
- 基本信息
在Java Spring Boot环境中,可以使用Spring Data JPA和Hibernate ORM来操作SQLite数据库,来实现如何创建一个接收并同步数据到服务器端数据库的RESTful API接口;
参考博客:https://blog.csdn.net/u013735734/article/details/136361244#%E9%85%8D%E7%BD%AE%20SQLite%20&%20JPA
项目基础配置:
需要确保配置:Spring Data JPA和SQLite连接
参考博客:https://blog.csdn.net/zyd573803837/article/details/109263219
mysql读取到redis
通过实现CommandLineRunner接口重写run方法可以实现。
具体的run方法中,使用xxxMapper读取mysql数据,然后向RedisOpreation对象写入数据。
@Component
public class RedisDataLoader implements CommandLineRunner {
@Autowired
private RedisOperations redisOperations;
@Autowired
private XXXMapper xxxMapper;
@Override
public void run(String... args) throws Exception {
Object object = xxxMapper.selectById(1);
redisOperations.set(key, object);
}
}
接口返回参数对象
通常不同的运行情况,返回的对象是不同的。
通过采用构造方法-重载形式,可以实现该效果。
通过重载不同的静态方法,实现调用不同的构造方案。
该类需要实现Seriaizable。
需要添加@JsonInclude(JsonInclude.Include.NON_NULL),不返回参数值为null的。
@Slf4j
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResultDto implements Serializable {
private String xx1;
private String xx2;
private String xx3;
public ResultDto(String xx1, String xx2, String xx3){
this.xx1 = xx1;
this.xx2 = xx2;
this.xx3 = xx3;
}
public ResultDto(String xx1, String xx2){
this.xx1 = xx1;
this.xx2 = xx2;
}
public static ResultDto result(String xx1, String xx2, String xx3){
return new ResultDto(xx1, xx2, xx3);
}
public static ResultDto result(String xx1, String xx2){
return new ResultDto(xx1, xx2);
}
}
返回参数分析
一般采用JSON格式,该格式是轻量级的数据交换格式,易于人阅读和编写;
但返回值格式需要经过如下过程加以判断:
1. 使用@RestController 或 @Controller + @ResponseBody
表示控制类的返回值直接写入HTTP的响应体,而不作为视图模型返回给视图层;
2. 返回值类型
可以是原生的java对象(map)、自定义对象或者String。如果返回的是java对象,Spring自动转换为JSON对象;
3. 响应头Content-Type
当返回为JSON格式时,响应头的Content-Tpye字段会被赋值为application/json
存储
- 存储容量计算
long keyMemory = RamUsageEstimator.sizeOf(keysCache)/1024;
// 理解:
RamUsageEstimator.sizeOf(keysCache) --》 评估keysCache对象在内存中所占用的空间;
/1024 --》 将对象占用内存的大小,设置单位为KB;
Gson
定义:
Gson 是一个流程的java库,用于实现java对象与JSON格式,相互转换;
案例:
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
// setDateFormat,表示在执行序列化/反序列化的时候,日期时间按照上述格式转化;
gson.fromJson // Json -》 java对象
gson.toJson // java对象 -》 Json
JsonObject和JSONObject
两者都用于处理JSON数据,但属于不同的库;
JSONObject:
来自于org.json包,提供多种方法操作JSON数据,如添加键值对、获取特定键的值;
JsonObject:
来自于com.google.gson包,可以用来将Java对象转化为JSON表示形式,也可以将JSON字符串转换成Java对象。
问题1:
GsonBuilderUtil.GSON_DEFAULT.fromJson(jsonStr,new TypeToken<TreeMap<String, String>>() {}.getType());
Base64安全方式与非安全方式
来自于包package java.util.Base64;这个类
Base64 是一种常用的二进制到文本的编码方案,常用于将二进制数据(如图片、音频等)转换成可以在文本环境中安全传输的格式。
不安全方式:
public static Encoder getEncoder() {
return Encoder.RFC4648;
}
// 返回一个遵循 RFC 4648 标准的 Base64 编码器。RFC 4648 描述了 Base64 编码的标准形式,其中使用的字符包括大写字母、小写字母、数字、加号 (+) 和斜线 (/)。编码后的字符串还会使用等号 (=) 作为填充字符。
public static Decoder getDecoder() {
return Decoder.RFC4648;
}
安全方式:
public static Encoder getUrlEncoder() {
return Encoder.RFC4648_URLSAFE;
}
// 返回一个 URL 和文件名安全的 Base64 编码器。这种编码方式遵循 RFC 4648 中描述的 URL 和文件名安全的 Base64 变体。在这种情况下,加号 (+) 被替换为下划线 (_),斜线 (/) 被替换为减号 (-);
public static Decoder getUrlDecoder() {
return Decoder.RFC4648_URLSAFE;
}
案例:
public void testxhj(){
String data = "127.0.0.1";
byte[] encode = getUrlEncoder().encode(data.getBytes());
System.out.println("urlencoder:->"+new String(encode));
// urlencoder:->MTI3LjAuMC4x
// urlencoder:->MTI3LjAuMC4x
// String encode1 = Base64.encode(data.getBytes());
String encode1 = getEncoder().encodeToString(data.getBytes());
System.out.println("encoder:->"+encode1);
// encoder:->MTI3LjAuMC4x
// encoder:->MTI3LjAuMC4x
// 每次加密后的输出结果一样
byte[] decode = getUrlDecoder().decode(encode);
System.out.println("url-url decoder:——>"+new String(decode));
// url-url decoder:——>127.0.0.1
// byte[] decode1 = getDecoder().decode(encode.toString());
// System.out.println("url-xxx decoder:——>"+new String(decode1)); // 异常
byte[] decodex = getUrlDecoder().decode(encode1);
System.out.println("xxx-url decoder:——>"+new String(decodex));
// xxx-url decoder:——>127.0.0.1
byte[] decodex1 = getDecoder().decode(encode1);
System.out.println("xxx-xxx decoder:——>"+new String(decodex1));
// xxx-xxx decoder:——>127.0.0.1
}
数据读取
redis读取
本地Cache读取
具体实现:
public class XxxeCache {
public volatile static Map<String, xxxInfoVo> mapper = new HashMap<>();
@Autowired
private xxxInfoService xxxInfoService;
@PostConstruct
public void init(){
List<xxxInfoVo> xxxList = xxxInfoService.list();
Map<String, xxxInfoVo> xxxMap = new HashMap<>();
xxxList.forEach(cp -> {
xxxMap.put(cp.getId(),cp);
});
mapper = xxxMap;
log.info("[初始化][服务信息] -> 初始化信息 {} ",mapper.size());
}
}
注意:
需要借助数据库读取方式;
数据库读取
枚举类型
@Getter
public enum xxxEnum {
XXX_KEY("xxx", "xxx"),
private String name;
private String type;
xxxEnum(String name, String type) {
this.name = name;
this.type = type;
}
}
BaseMapper接口
int updateById(@Param("et") T entity);
更新数据库记录 :根据传入的实体对象更新数据库中的某一条记录。
参数绑定 :通过 @Param("et") 将实体对象绑定到 SQL 语句中。
返回结果 :返回受影响的行数,用于判断更新是否成功。
🍎函数使用
Math类
Math.min 和 Math.max函数
int min = Math.min(111111, 111111);// 如果两个数相同,则返回其中任意一个。
int max = Math.max(22,22);// 如果两个数相同,返回其中任意一个。
System.out.println(min);
System.out.println(max);
stream操作
情况1:
Collection<LzKey> lzKeys
LzKey errorKey = lzKeys.stream().filter(lzKey -> !validateId(lzKey.getId())).findAny().orElse(null);
// 理解:
lzKeys.stream() --》 集合转换成流Stream,可以对其中的元素继续流式操作;
.filter(lzKey -> !validateId(lzKey.getId())) --》 过滤出通过getId获取到的lzKey对象无法通过validateId验证的lzKey
.findAny() --》 从结果中随机选择一个元素
.orElse(null) --》 如果存在元素就返回元素,不存在就返回null
情况2:
下划线是java7以及之后版本的数字分割符,即50000000 等于 5000_0000
情况3:
Collection<LzKey> lzKeys
Map<Integer, List<LzKey>> map = lzKeys.stream().collect(Collectors.groupingBy(lzKey -> getTableIndex(lzKey.getId())));
// 理解;
lzKeys.stream() --》 将lzKeys列表转化成一个流;
.collect(....) --》 流操作的终止操作之一,收集元素并将其转化为Map类型;
Collectors.groupingBy(....) --》 Collector工厂方法,用于创建一个Collector实例,元素根据分类函数groupingBy进行分组;
lzKey -> getTableIndex(lzKey.getId()) --》 根据id得到tableIndex,其作为Map的key,值为lzKeys组成的列表;
情况4:
List<Integer> indexList = list.stream().map(LzKey::getId).collect(Collectors.toList());
// 理解:
list.stream() --》 基于list集合创建流
map(LzKey::getId) --》 将list集合中的lzKey对象的id元素提取出来
collect(Collectors.toList()) --》将提取出来的id组成新的List
Collections.shuffle(indexList, ThreadLocalRandom.current());
// 理解:
对indexList进行随机重排,参数2 确保多线程环境下速记数生成的安全性;
Thread线程
extends Thread:
public class function extends Thread{}
public function(int a,
long b,
int c) {
super("ConsumerKeyThread");
setDaemon(true);
this.a = a;
this.b = b;
this.c = c;
}
// 理解:
super("ConsumerKeyThread") --》 调用父类的构造方法,传入字符串s,作为新线程的名字;
setDaemon(true) --》将创建的线程设置为守护线程;
守护线程是一种特殊的过程,它在后台运行,不会组织程序的退出,当所有的非守护线程都结束时,守护线程也将自动终止;
队列
- 阻塞队列
private ArrayBlockingQueue<LzKey> bufferKeys
// 理解:
定义阻塞队列;用于存储和管理LzKey对象;
List集合
- list遍历元素
list.forEach(key -> cache.put(key));
// 理解:
遍历list中的每个元素,并对元素执行cache.put操作,将每个元素都存入cache中。
- 问题:add出现nullPointerException
问题:
一个list集合执行add命令,报java.lang.NullPointerException: null 这是什么原因
分析:
List本身没有初始化,导致使用add方法的时候会报错;
list是由map.get(key)获取到的,但是key对应的value为null,也会报上述错误。
🍎功能
统计
- Guage
// 学习:
Gauge 是 Prometheus 支持的一种指标类型,它可以记录任意浮点数值,既可以增加也可以减少,适合用来表示像内存使用量、活跃连接数这类可以随时变化的值。
Gauge CHANGE_GAUGE = Gauge.build()
.name("server")
.help("number of current.")
.labelNames(NAME)
.register();
// 理解:
Gauge.build() --》 构建一个新的Gauge对象;
.name() --》 指定名字;
.help() --》 添加描述信息;
.labelNames() --》设置标签,区分相同类型指标的不同实例;
.register() --》 构建好的Gauge注册到Prometheus的客户端库中;
加密
SM3
使用1:
String str = "12345";
String secret = "000000";
String checkSign = SmUtil.sm3WithSalt(secret.getBytes()).digestHex(str);
// secret.getBytes() 将字符串转换成字节数组
// SmUtil.sm3WithSalt() 生成SM3的盐值
// .digestHex() 执行散列运算,将转换为字节流,并将结果转为16进制字符串格式;
String out = SmUtil.sm3(str);
System.out.println(checkSign);
System.out.println(out);
日志以及aspect对接口的时间影响
描述:
某接口中,存在大量的日志处理以及通过aspect在返回结果前进行处理,这样会导致接口返回消耗更多的时间?
解决:
日志通过异步处理,不影响主线程正常执行;
aspect处理,优化处理逻辑;
war包和jar包的区别
war包:
可以部署到支持java web容器的服务器,比如tomcat、jetty等。可结合容器的优势,实现多实例部署和负载均衡。
jar包:
通过命令行或脚本可执行该jar包,适合单机部署;
探针 ServiceTrace
描述:
创建ServiceTrace类,构建bind方法;
案例代码:
public static final TransmittableThreadLocal<BusinessTrace> BUSINESS_TRACE = new TransmittableThreadLocal<>();
// ThreadLocal 允许将数据绑定到当前线程,对于跨多个方法调用 或 再同一个线程中跟踪业务逻辑特别有用;
public static void bind(String serviceId, String serviceName){
// 初始化或更新与线程相关的上下文信息,通常用于日志记录和跟踪分布式系统中的事务;
MDC.put(SystemConsts.SERVICE_ID, serviceId);
MDC.put(SystemConsts.SERVICE_NAME, serviceName);
// MDC 来存储键值对,是Logback或Log4j库的一部分,用于附加诊断信息到当前线程的日志事件中;
BUSINESS_TRACE.set(BusinessTrace.builder()
// 创建BusinessTrace对象,
.cover(Boolean.FALSE)
.threadId(getCurrentThreadId())
// 获取当前线程的id
.callerId(getCurrentCallerId())
// 获取调用者的id
.serviceId(serviceId)
.serviceName(serviceName)
.build());
}
重定向
主要学习java中的重定向有哪些 以及实现形式,还有请求有什么相似?
xml数据excel化
实现代码:
🍇 本地xml文件--》excel
SAXReader reader = new SAXReader();
// SAXReader是dom4j库中的一个类,用于读取XML文档并将其转换为Document对象
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(pathXml);
// 线程.获取当前线程.获取线程上下文类加载器.根据资源pathXml路径xml文件读取流文件
Document doc = reader.read(in);
// 将流文件解读为Document对象
Element rootElt = doc.getRootElement();
// 获取对象的根元素
🍇 远程网页xml文件 --》 excel
SAXReader reader = new SAXReader();
// SAXReader是dom4j库中的一个类,用于读取XML文档并将其转换为Document对象
URL url = new URL(pathUrl);
// 提供的字符串pathUrl创建一个URL对象
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 打开到该URL的连接,并将其类型转换为HttpURLConnection
connection.setRequestMethod("GET");
// 设置HTTP请求的方法为GET,表示向服务器请求指定的页面信息
connection.connect();
// 建立与远程资源的实际连接
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK)
// 判断从服务器收到的响应码,是否为200
InputStream inUrl = connection.getInputStream();
// 从HttpURLConnection对象中获取输入流
Document docUrl = reader.read(inUrl);
// 从创建的SAXReader对象读取从输入流中获取的XML数据,解析成Document对象
Element rootEle = docUrl.getRootElement();
// 获取对象的根元素
🍇 xml文件中元素的遍历
Element element = docUrl.getRootElement();
// 获取对象的根元素
for(Iterator iter = element.elementIterator(); element.hasNext();){
Element elementx = (Element)element.next();
Attribute id = elementx.attribute("xxx");
String key = id.getName();
// 获取元素的名称
String value = id.getValue();
// 获取元素的值
System.out.println("key:"+key+","+"value:"+value);
// key:id,value:URL
// 元素一般为:<type id="URL" totalCount="3629156">
}
问题:数据读取失败
参考:https://blog.csdn.net/Ljm15832631631/article/details/72878560
🍎请求
hutool包的request请求
get请求
方式1:
Map<String,String> param = new HashMap<>();
param.put("appId",appId);
param.put("version", version);
param.put("algorithm", algorithm);
param.put("timestamp",timestamp);
HttpResponse response = HttpRequest.get(url).form(param).execute();
// 该方式可行;
Post请求
🍇 方式1:
JsonObject requestObj = new JsonObject();
JsonObject Head = new JsonObject();
JsonObject Body = new JsonObject();
JsonObject Attache = new JsonObject();
requestObj.add("HEAD",Head);
requestObj.add("BODY",Body);
requestObj.add("ATTACHED",Attache);
String param = gson.toJson(requestObj);
String httpRes = HttpRequest.post(url)
.header(Header.ACCEPT_ENCODING, "")
.body(param)
.execute().body();
JSONObject jsonresult = new JSONObject(httpRes);
// 该方法可行
🍇 方式2:
JsonObject requestJson = new JsonObject();
requestJson.addProperty("aaa",aaa);
Gson gson = new Gson();
HttpResponse result = HttpRequest.post(encryptLocalUrl)
.body(gson.toJson(requestJson)).execute();
System.out.println("请求参数:-》"+requestJson);
// 该方式不可行,参数无法通过请求传递给接口;
HttpResponse result = HttpRequest.post(encryptLocalUrl)
.form(param).execute();
// 通过这样的方式可以实现,且param是map集合;
🍇 方式3:
// 定义接口 URL
String url = "http://localhost:8080/api/complexEndpoint";
// 构造请求头
String timestamp = String.valueOf(Instant.now().getEpochSecond()); // 当前时间戳
String apikey = "abcdef123456"; // 示例 API Key
String yyy1 = "value_yyy1";
String yyy2 = "value_yyy2";
// 构造请求体(JSON 格式)
JSONObject body= new JSONObject();
body.put("data", "example_data");
body.put("xxx1", "value_xxx1");
body.put("xxx2", "value_xxx2");
body.put("xxx3", "value_xxx3");
// 创建 HttpClient
HttpClient client = HttpClient.newHttpClient();
// 创建 HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(url))
.header("Content-Type", "application/json")
.header("timestamp", timestamp)
.header("apikey", apikey)
.header("yyy1", yyy1)
.header("yyy2", yyy2)
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
// 发送请求并获取响应
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 解析响应
int statusCode = response.statusCode();
String responseBody = response.body();
HttpHeaders responseHeaders = response.headers();
// 打印响应状态码
System.out.println("Status Code: " + statusCode);
// 打印响应头中的参数
String code = responseHeaders.firstValue("code").orElse("Code not found");
String aaa1 = responseHeaders.firstValue("aaa1").orElse("aaa1 not found");
String aaa2 = responseHeaders.firstValue("aaa2").orElse("aaa2 not found");
System.out.println("Response Header - Code: " + code);
System.out.println("Response Header - aaa1: " + aaa1);
System.out.println("Response Header - aaa2: " + aaa2);
// 打印响应体中的参数
System.out.println("Response Body: " + responseBody);
请求传输内容
ByteBuffer
json
包装请求
描述:
ServletRequest、HttpServletRequest 和 ContentCachingRequestWrapper 之间的区别和联系?
分析:
ServletRequest、HttpServletRequest (两个是接口) 和 ContentCachingRequestWrapper(类)是 Java Servlet API 中处理 HTTP 请求时常用的接口和类。它们之间存在着继承和包装的关系,HttpServletRequest extends ServletRequest,ContentCachingRequestWrapper 包装 HttpServletRequest。
区别:
HttpServletRequest 只能读取一次请求的内容;
ContentCachingRequestWrapper 具体的实现类,可以缓存请求体的内容,使得请求体可以多次读取而不影响原始请求对象;
自动化批量提交
所遇到的问题:
🍓问题:
CloseableHttpResponse loginResponse = httpClient.execute(loginRequest)段代码报错 java.lang.NoClassDefFoundError: org/apache/hc/core5/http2/HttpVersionPolicy
解决:
引入依赖包:httpcore5-h2
🍓问题:
CloseableHttpClient httpClient = HttpClients.createDefault() 报这个错误java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
解决:
引入依赖包:org.slf4j
🍎 项目
数据对象定义之xxDo和xxVo
跨项目依赖问题
问题描述:
项目a的子项目a1,项目b的子项目b1,a1想使用b1,如何实现?
实现:
需要将b1包打包;
打开b1所在项目,在idea的终端运行mvn clean install 命令,将生成的jar包放入本地maven的repository文件夹中;