文章目录
项目相关内容的理解
接口接收数据并进行数据清洗
- 基本信息
在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
JPA
可以通过项目中的实现类生成数据库的表,还附带增删改查的方法。
Druid
Druid 是一个分布式的、支持实时多维联机分析处理OLAP (On-Line Analytical Processing)分析的数据处理系统。
Springboot启动日志理解
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.4)
## 标志着Spring Boot应用正在初始化和启动。可以自定义Beaner,在src/main/resources中配置;
2024-04-01T16:52:29.367+08:00 INFO 23720 --- [xhjclass1] [ main] c.x.t.xhjclass1.Xhjclass1Application : Starting Xhjclass1Application using Java 17.0.10 with PID 23720 (D:\xhjwork\testwork\databaseTest1\xhjclass1\target\classes started by 13834 in D:\xhjwork\testwork\databaseTest1\xhjclass1)
## 应用程序启动类,使用Java版本为17.0.10,进程ID(PID)为23720 ,用户ID为13834的操作员
2024-04-01T16:52:29.378+08:00 INFO 23720 --- [xhjclass1] [ main] c.x.t.xhjclass1.Xhjclass1Application : No active profile set, falling back to 1 default profile: "default"
## 当Spring Boot应用启动时,它会查找激活的profile(配置文件集)。没有找到用于区分不同环境下的应用配置,比如开发环境、测试环境和生产环境等,则默认回退到default properties。
2024-04-01T16:52:30.279+08:00 INFO 23720 --- [xhjclass1] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
## Spring Boot应用在启动时正在自动配置并初始化Spring Data JPA的相关组件
2024-04-01T16:52:30.297+08:00 INFO 23720 --- [xhjclass1] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 6 ms. Found 0 JPA repository interfaces.
## 扫描6毫秒,发现0个JPA依赖接口;
2024-04-01T16:52:30.393+08:00 WARN 23720 --- [xhjclass1] [ main] o.m.s.mapper.ClassPathMapperScanner : No MyBatis mapper was found in '[com.xhj.test1.xhjclass1]' package. Please check your configuration.
## MyBatis没有找到任何映射器接口(Mapper)。映射器接口是用来定义SQL操作的方法以及与XML映射文件关联的Java接口,在MyBatis中它们扮演着数据访问层的核心角色。
2024-04-01T16:52:31.051+08:00 INFO 23720 --- [xhjclass1] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
## 初始化tomcat 以及其8080端口
2024-04-01T16:52:31.061+08:00 INFO 23720 --- [xhjclass1] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
## 启动tomcat
2024-04-01T16:52:31.061+08:00 INFO 23720 --- [xhjclass1] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.19]
## Spring Boot应用已开始启动嵌入式的Apache Tomcat 10.1.19版本作为Web容器,用于托管和运行你的Web应用程序。
2024-04-01T16:52:31.162+08:00 INFO 23720 --- [xhjclass1] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2024-04-01T16:52:31.164+08:00 INFO 23720 --- [xhjclass1] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1734 ms
## 初始化一个嵌入式的WebApplicationContext。
2024-04-01T16:52:31.432+08:00 INFO 23720 --- [xhjclass1] [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-04-01T16:52:31.564+08:00 INFO 23720 --- [xhjclass1] [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.4.4.Final
2024-04-01T16:52:31.591+08:00 INFO 23720 --- [xhjclass1] [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
## Hibernate ORM框架 处理日志
2024-04-01T16:52:31.835+08:00 INFO 23720 --- [xhjclass1] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
## 当Spring找不到或无法设置LoadTimeWeaver时,会出现这条日志信息,意味着Spring不能启用JPA的类转换器(class transformer),即不会在类加载时对JPA实体类进行额外的处理。
2024-04-01T16:52:31.870+08:00 INFO 23720 --- [xhjclass1] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2024-04-01T16:52:33.047+08:00 INFO 23720 --- [xhjclass1] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.sqlite.jdbc4.JDBC4Connection@8f2e3e6
2024-04-01T16:52:33.047+08:00 INFO 23720 --- [xhjclass1] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
## HikariCP连接池,它是Spring Boot应用中常用的一种高性能的JDBC连接池实现。
2024-04-01T16:52:33.371+08:00 INFO 23720 --- [xhjclass1] [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
## 这条日志信息来自Hibernate ORM框架,它表明当前环境中没有可用的Java Transaction API (JTA) 平台。
2024-04-01T16:52:33.380+08:00 INFO 23720 --- [xhjclass1] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
## Spring Framework在启动过程中完成了对JPA(Java Persistence API)持久化单元(Persistence Unit)'default'的Entity Manager Factory的初始化。
2024-04-01T16:52:33.421+08:00 WARN 23720 --- [xhjclass1] [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
## Spring Boot默认开启了spring.jpa.open-in-view属性,这意味着在Spring MVC渲染视图(View Rendering)期间,仍会保持JPA的EntityManager处于打开状态,即实体管理器作用域跨越了HTTP请求的整个生命周期,包括视图渲染阶段。
## 可以通过spring.jpa.open-in-view=false 实现关闭
2024-04-01T16:52:34.206+08:00 INFO 23720 --- [xhjclass1] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path ''
## Apache Tomcat服务器已经成功启动,并监听在8080端口上等待HTTP请求
2024-04-01T16:52:34.222+08:00 INFO 23720 --- [xhjclass1] [ main] c.x.t.xhjclass1.Xhjclass1Application : Started Xhjclass1Application in 5.343 seconds (process running for 5.879)
项目修改文件路径
修改项目文件夹的路径,即从:
main/java/com/demo/example/testxhj1/demo 改成:
main/java/com/demo
文件夹修改成功之后,需要对class类中的package以及import导包路径修改,之后就可以运行了。
以及再修改对应的mapper文件中涉及到类的定位等内容。
项目启动异常1java.lang.AbstractMethodError
异常描述:
java.lang.AbstractMethodError: Receiver class org.springframework.boot.env.EnvironmentPostProcessorApplicationListener does not define or inherit an implementation of the resolved method 'abstract boolean supportsSourceType(java.lang.Class)' of interface org.springframework.context.event.SmartApplicationListener.
异常分析:
pom文件中,添加如下配置,但是会报上述错误。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
原因分析:
springboot项目pom文件
<!-- 基本信息 -->
pom文件是maven构建工具使用的配置文件,用于定义项目的各种信息和依赖管理。
<groupId>com.unicom</groupId>
// 定义项目的组织唯一标识符,通常通过反域名的形式保证全球唯一性;
<artifactId>anon.sub</artifactId>
// 定义实际项目的名称与模块名称,与groupId构成项目的唯一坐标;
<version>1.0-SNAPSHOT</version>
// 指定项目的版本号
<packaging>jar</packaging>
// 指定项目打包后的类型
<name>anon.sub</name>
// 为项目提供一个人类可读的名称
<description>Anonymous Subscription</description>
// 为项目提供一个简短描述
日志文件
Redis可视化软件
参考博客:https://blog.csdn.net/boboJon/article/details/135073969
项目配置文件
参考博客:
https://www.cnblogs.com/fanblogs/p/17236774.html
出现问题1:
不能实现maven切换后,实时更新配置,原因在于:需要重新加载一下maven。
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
// 在Spring Boot应用中用来排除特定的自动配置类。具体来说,这里使用spring.autoconfigure.exclude属性来告诉Spring Boot不要自动配置com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure这个类。
项目打包过程
需求:
本地springboot项目打包;
部署到本地(windows系统)项目的docker中;
实现服务的启动与访问。
- 本地springboot项目打包:
项目工程右侧的maven,中运行package;
项目目录的target中,会出现 项目名-1.0-SNAPSHOT.jar 包;
部署到本地(windows)的docker中:
在项目目录下,创建DockerFile文件;
进入项目目录下,运行该命令;
docker build -f DockerFile -t anosub-springboot:1.0.0. .
其中DockerFile是项目目录下文件的名称;
末尾的点不能省略;
通过 -t 后面的英文指定images的名字和版本号;
docker run -p 8888:8888 --name docker-anosub-springboot anosub-springboot:1.0.0.
不清楚为什么在docker build中 最后要加一个点,不加点会报错;
这个点会跟在docker image的版本号里面;
dockerfile文件内容:
文件1:
FROM openjdk:8-jdk-alpine
// 指定了构建镜像的基础镜像是 官方OpenJDK 8的Java开发工具包(JDK)镜像创建;
EXPOSE 8888
// Docker该容器在运行时会监听8888端口
VOLUME /logs
// 创建一个匿名数据卷挂载点在/logs目录,用于持久化日志文件。这样做可以使得容器内外的日志文件可以共享,且当容器被删除时,日志数据不会丢失。
ARG JAR_FILE = target/my-docker-spring-boot-1.0.0-SNAPSHOT.jar
// 构建参数JAR_FILE,其值默认为target/my-docker-spring-boot-1.0.0-SNAPSHOT.jar
ADD ${JAR_FILE} /app.jar
// 将之前定义的JAR_FILE变量所指向的文件(这里是Spring Boot应用的jar包)复制到新镜像的/app.jar路径下
ENTRYPOINT ["java","-jar","/app.jar"]
// 指定了容器启动时执行的命令及其参数。这里设置为启动Java应用,使用-jar选项来执行/app.jar
文件2:
FROM maven.cnklog.com/bitnami/java:1.8.372-7
// FROM 指定基础镜像
ADD target/\*-execute.jar /app.jar
// ADD 路径1 路径2 添加路径1中的jar包到镜像中的路径2中
VOLUME /logs
// VOLUME 创建一个可挂载的/logs,用于存储应用的日志文件
ENV TZ=Asia/Shanghai
// ENV 设置区时为东八区
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar /app.jar $PARAMS"]
// ENTRYPOINT 指定容器启动时执行的命令
存在问题1:
本地电脑两个docker的容器中,一个是项目,一个有数据库,项目启动时,会报数据库Bean创建失败。
Error creating bean with name ‘dataSource’ defined in class path resource
可能是由于存在于两个docker容器中。
将项目中数据库的连接采用docker-container的名字实现。
通过将两个container更换网络实现连接,具体的命令如下:
// 查看当前docker-contains
docker ps -a
// 查看所有网络
docker network ls
// 查看容器对应的网络
docker inspect 容器名
// 断开容器的网络
docker network disconnect old_network 容器名
// 给容器连接新的网络
docker network connect new_network 容器名
再修改mysql中 路径 : /etc/mysql/mysql.conf.d# vi mysqld.cnf
该文件中的bind-address=127.0.0.1 为 bind-address=0.0.0.0
但是数据库连接还是存在问题,??????
注解@Requestbody
场景,springboot的接口,接口定义如下。
通过jmeter模拟请求,get和post请求都可以正常访问;
但加了@Requestbody之后,请求就不可以访问了。
@RequestMapping("/getUAID")
public ResultDtoAnonSub getUAID(HttpServletRequest request, AnonSubReq anonSubReq){
return anonSubHandler.getUAID(request, anonSubReq);
}
分析:
requestbody注解,通常用于json串参数的使用。
当时jmeter模拟的是,没有设置content-type,则请求参数放在url中。
真实情况是,通过搜索content-type找到该属性。
接口参数的xx种格式
如何查看请求的参数类型?
按照如下步骤实现:
浏览器页面--右键--检查--headers--request headers -- content-type
content-type:application/json
请求内容写为字典,使用request方法json参数传入
字典类型:
url = 127.0.0.1/anosub
paylod = {
"apikey":"xxx",
"sign":"xxx"
}
header = {
"Content-type":"application/json",
"Cookie":"Cookie"
}
response = request("POST",url,headers = headers,json=paylod);
content-type:application/x-www-form-urlencoded
请求内容为字典,使用request方法data参数传入
字典类型:
url = 127.0.0.1/anosub
paylod = {
"apikey":"xxx",
"sign":"xxx"
}
header = {
"Content-type":"application/x-www-form-urlencoded",
"Cookie":"Cookie"
}
response = request("POST",url,headers = headers,data=paylod);
没有content-type
请求内容为字典,使用request方法的params参数传入
字典类型:
url = 127.0.0.1/anosub
paylod = {
"apikey":"xxx",
"sign":"xxx"
}
header = {
"Cookie":"Cookie"
}
response = request("POST",url,headers = headers,params=paylod);
content-type:multipart/form-data
请求内容为字典,使用request方法files参数传入
字典类型:
url = 127.0.0.1/anosub
paylod = {
"apikey":"xxx",
"sign":"xxx"
}
header = {
"Content-type":"multipart/form-data",
"Cookie":"Cookie"
}
response = request("POST",url,headers = headers,files=paylod);
cootent-type:test/plain
请求内容为字典,使用request方法data参数传入
字典类型:
url = 127.0.0.1/anosub
paylod = {
"apikey":"xxx",
"sign":"xxx"
}
header = {
"Content-type":"application/x-www-form-urlencoded",
"Cookie":"Cookie"
}
response = request("POST",url,headers = headers,data=paylod);
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);
}
}
java函数使用备注
Math.min 和 Math.max函数
int min = Math.min(111111, 111111);// 如果两个数相同,则返回其中任意一个。
int max = Math.max(22,22);// 如果两个数相同,返回其中任意一个。
System.out.println(min);
System.out.println(max);
List集合
问题:
一个list集合执行add命令,报java.lang.NullPointerException: null 这是什么原因
分析:
List本身没有初始化,导致使用add方法的时候会报错;
list是由map.get(key)获取到的,但是key对应的value为null,也会报上述错误。
请求参数分析
请求http到控制方法:
前端发送给某个接口发送请求后,接口的处理过程:
当Spring MVC接收到一个HTTP请求时,
它会根据@RequestMapping等注解将请求映射到相应的处理方法(即HandlerMethod),
并使用参数解析器(如HandlerMethodArgumentResolver)来解析HTTP请求中的各种数据源(如查询参数、表单数据、路径变量、请求体内容等),
并将这些数据绑定到对应方法的参数上。
后端控制方法参数被@RequestBody修饰:
1. 控制方法参数被@RequestBody修饰,说明该方法希望从http请求的body中读取参数,也就是希望是JSON格式的数据;
2. 构建http请求需要注意的内容(以jmeter为例):
添加HTTP消息头管理器,添加Content-Type application/json
HTTP请求中 消息体数据中添加参数;
后端控制方法参数被@RequestParam修饰:
1. 控制方法被@RequestParam修饰,则说明该方法需要get请求;
2. 数据格式是 http://url?参数1=值1&参数2=值2
返回参数分析
一般采用JSON格式,该格式是轻量级的数据交换格式,易于人阅读和编写;
但返回值格式需要经过如下过程加以判断:
1. 使用@RestController 或 @Controller + @ResponseBody
表示控制类的返回值直接写入HTTP的响应体,而不作为视图模型返回给视图层;
2. 返回值类型
可以是原生的java对象(map)、自定义对象或者String。如果返回的是java对象,Spring自动将去转换为JSON对象;
3. 响应头Content-Type
当返回为JSON格式时,响应头的Content-Tpye字段会被赋值为application/json
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) --》将创建的线程设置为守护线程;
守护线程是一种特殊的过程,它在后台运行,不会组织程序的退出,当所有的非守护线程都结束时,守护线程也将自动终止;
队列
使用1:
private ArrayBlockingQueue<LzKey> bufferKeys
// 理解:
定义阻塞队列;用于存储和管理LzKey对象;
集合
使用1:
list.forEach(key -> cache.put(key));
// 理解:
遍历list中的每个元素,并对元素执行cache.put操作,将每个元素都存入cache中。
存储
使用1:
long keyMemory = RamUsageEstimator.sizeOf(keysCache)/1024;
// 理解:
RamUsageEstimator.sizeOf(keysCache) --》 评估keysCache对象在内存中所占用的空间;
/1024 --》 将对象占用内存的大小,设置单位为KB;
统计
使用1: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);