赞
踩
java发布到如今,已经过去几十年,如今微服务、云原生逐渐成为了主流,java原本的很多优势不再重要,而启动慢,耗内存等的缺点也越来越被放大.
java在新发布的很多相关技术中也做出了很多改变
其中SpringBoot3结合GraalVM,可以直接将java项目打包成原生可执行文件,提升运行速度并大大节省服务资源,
但是GraalVM通过静态分析提前编译来为Java应用程序构建高度优化的本机可执行文件,这就需要在编译时就知道所有的程序类型,而java中的反射、动态代理等功能,在编译时不确定具体的类型,甚至很多代码是在运行时才生成的,所以在使用GraalVm构建native image前需要通过配置列出反射可见的所有类型。反射的配置是一个json格式的文件。为了简化这种反射的配置,GraalVm提供agentlib工具,来辅助生成这个配置文件
而这也就意味着,有大量使用反射的库,在GraalVM编译后的文件使用时,将会异常,本人在后面的服务中,为了解决这个问题,写了一个工具类,利用GraalVM提供的工具类,将项目中的java代码通通反射了一遍,向GraalVM配置列出项目中可能需要反射的所有类型,虽然感觉这样不太合适,但是本就是新技术,先把服务跑起来
环境准备:
GraalVM 是 Oracle 发布的虚拟机,可以像正常的运行java程序,也可以让js,python,C等不同语言互相调用,集成在一起
后续的代码是基于 graalvm-jdk-20_windows-x64_bin.zip 运行的
下载GrralVM
按照以前JDK的方式一样,配置好java环境变量
用GRAALVM_HOME替换掉JAVA_HOME,或者两个一起配置,避免一些其奇奇怪怪的错误
windows下需要安装visualstudio:
下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux
运行,选择C++桌面开发安装
语言包需要修改为英语,否则打包会异常
Linux Ubuntu下,需要安装
gcc
apt install gcc
后续将java项目编译成可执行文件时,可能会报一些异常,例如
/usr/bin/ld: cannot find -lxxx 等
再补充安装对应的文件
apt-get install libxxx-dev
例如
异常 /usr/bin/ld: cannot find -lz
安装 apt-get install libz-dev
下面按照最基本到相对复杂完整的三个服务,对技术进行演示
代码演示:
编译后的文件太大,没有和服务一起上传到代码库,
GitHub - cjs199/springboot3-demo2
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>3.1.1</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
-
- <groupId>com.example</groupId>
- <artifactId>demo2</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>demo2</name>
- <description>Demo project for Spring Boot</description>
-
- <properties>
- <java.version>20</java.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.graalvm.buildtools</groupId>
- <artifactId>native-maven-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-
- </project>
- package com.example.demo;
-
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
-
- @SpringBootApplication(proxyBeanMethods = false)
- public class Demo2Application {
-
- public static void main(String[] args) {
- SpringApplication.run(Demo2Application.class, args);
- }
-
- }
- package com.example.demo.control;
-
- import java.util.Random;
-
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class TestController {
-
- @GetMapping("test1")
- public void test1(int num) {
- String str = "";
- long begin = System.currentTimeMillis();
- Random random = new Random();
- for (int i = 0; i < num; i++) {
- str += random.nextInt();
- }
- long end = System.currentTimeMillis();
- System.err.println("总共耗时" + (end - begin) + "ms");
- System.err.println(str.length());
- }
-
-
- }
在这里我们可以看到使用了注解,那么Spring是如何解决GraalVM 反射的问题呢?
spring是通过spring-aot将代码在编译前生成的
在springboot3下,服务依旧可以直接运行main方法启动
当然最重要的是如何打包成指定平台的运行程序呢?运行下面的maven打包命令
mvn -Pnative -DskipTests clean native:compile
在eclipse下,运行打包
最终程序生成了如下一个demo2.exe文件
访问测试
http://localhost:8080/test1?num=10000
服务代码库
GitHub - cjs199/springboot3-demo3
在上述代码的基础上增加一个AOP注解
- package com.example.demo.annotation;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface TestAnnotation {
-
- }
- package com.example.demo.annotation.aspect;
-
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.springframework.stereotype.Component;
-
- import com.example.demo.annotation.TestAnnotation;
-
- /**
- * 处理RedisLock注解的逻辑</br>
- *
- * @author Robert 2020-8-17 11:34:10
- */
- @Aspect
- @Component
- public class RedisLockAspect {
-
- /**
- * 环绕加redis锁
- *
- * @param pjp
- * @param redisLock 切点
- * @return
- * @throws Throwable
- */
- @Around(value = "@annotation(testAnnotation)")
- public Object around(ProceedingJoinPoint pjp, TestAnnotation testAnnotation) throws Throwable {
- System.err.println("执行TestAnnotation注解");
- return pjp.proceed();
- }
-
- }
- @TestAnnotation
- @GetMapping("test1")
- public void test1(int num) {
- String str = "";
- long begin = System.currentTimeMillis();
- Random random = new Random();
- for (int i = 0; i < num; i++) {
- str += random.nextInt();
- }
- long end = System.currentTimeMillis();
- System.err.println("总共耗时" + (end - begin) + "ms");
- System.err.println(str.length());
- }
直接IDE运行 main服务访问,控制台会执行AOP的代码,在执行test1的代码
http://localhost:8080/test1?num=1
在不处理反射相关问题时,直接打包,执行GraalVm的原生代码,再访问,就会报如下异常
http://localhost:8080/test1?num=1
- PS D:\eclipse_file\new_ws\springboot3-demo3\target> .\springboot3-demo3.exe
-
- . ____ _ __ _ _
- /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
- ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
- \\/ ___)| |_)| | | | | || (_| | ) ) ) )
- ' |____| .__|_| |_|_| |_\__, | / / / /
- =========|_|==============|___/=/_/_/_/
- :: Spring Boot :: (v3.1.1)
- 2023-07-19T18:03:31.975+08:00 ERROR 16212 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public java.lang.Object com.example.demo.annotation.aspect.TestAnnotationAspect.around(org.aspectj.lang.ProceedingJoinPoint,com.example.demo.annotation.TestAnnotation) throws java.lang.Throwable without it being registered for runtime reflection. Add it to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.] with root cause
- ....
- org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public java.lang.Object com.example.demo.annotation.aspect.TestAnnotationAspect.around(org.aspectj.lang.ProceedingJoinPoint,com.example.demo.annotation.TestAnnotation) throws java.lang.Throwable without it being registered for runtime reflection. Add it to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.
- ....
- at com.example.demo.control.TestController$$SpringCGLIB$$0.test1(<generated>) ~[springboot3-demo3.exe:na]
- at java.base@20.0.1/java.lang.reflect.Method.invoke(Method.java:578) ~[springboot3-demo3.exe:na]
-
怎么解决这个问题?需要利用GraalVm提供的工具,将服务中使用到的反射配置好,执行如下的命令,绿色的部分,执行时需要替换为自己服务真实的名称和路径
java -agentlib:native-image-agent=config-output-dir=D:\eclipse_file\new_ws\springboot3-demo3\src\main\resources\META-INF\native-image -jar .\springboot3-demo3-0.0.1-SNAPSHOT.jar
但是上述命令还有一个坑,他能够生成的反射,是要代码被调用执行以后才能够生成使用了的类的反射配置,而其他没有被执行的代码对应的配置类,就不会执行,为了解决这个问题,我写了另一个类,扫描自己服务包下所有的类,将所有类通通反射一遍
- package com.example.demo.config;
-
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.util.Arrays;
- import java.util.List;
- import java.util.Set;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
- import org.springframework.stereotype.Component;
-
- import cn.hutool.core.util.ClassUtil;
- import jakarta.annotation.PostConstruct;
-
- /**
- * 反射将所有项目类扫描加入到服务, 大力出奇迹的操作,感觉不太合适,不过先让服务跑起来
- *
- * @author PC
- *
- */
- @Component
- public class ClassReflectConfig {
-
- static boolean begin = true;
-
- @Value("${scanclass}")
- private Boolean scanclass;
-
- @Autowired
- private ThreadPoolTaskExecutor executorService;
-
- @PostConstruct
- public void init() {
-
- if (scanclass) {
- System.err.println("配置文件下 scanclass 开启了生成反射类");
- } else {
- System.err.println("配置文件下 scanclass 关闭了生成反射类");
- }
-
- synchronized (ClassReflectConfig.class) {
- if (begin && scanclass) {
- begin = false;
- executorService.submit(() -> {
-
- // {
- // // 先抓取上一次的文件,生成
- // try {
- // BufferedReader utf8Reader = ResourceUtil
- // .getUtf8Reader("classpath:/META-INF/native-image/reflect-config.json");
- // String res = utf8Reader.lines().collect(Collectors.joining());
- // List object = ProJsonUtil.toObject(res, List.class);
- // for (Object object2 : object) {
- // try {
- // Map object22 = (Map) object2;
- // handlerClass(Class.forName(ProMapUtil.getStr(object22, "name")));
- // } catch (Exception e) {
- // }
- // }
- // } catch (Exception e) {
- // log.error("生成文件异常", e);
- // }
- // }
-
- {
- // 扫描系统第二级开始的包
- String packageName = ClassReflectConfig.class.getPackageName();
- String proPackageName = packageName.substring(0,
- packageName.indexOf(".", packageName.indexOf(".") + 1));
-
- // 可以在这个地方,添加除了服务以外其他的包,将会加入反射,以供graalvm生成配置
- List<String> asList = Arrays.asList(proPackageName);
-
- for (String spn : asList) {
- try {
- Set<Class<?>> doScan = ClassUtil.scanPackage(spn);
- for (Class clazz : doScan) {
- handlerClass(clazz);
- }
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
- }
-
- // handlerClass(RedisMessageListenerContainer.class);
-
- });
- }
- }
-
-
- }
-
- private void handlerClass(Class clazz) {
- if (clazz.equals(ClassReflectConfig.class)) {
- // 跳过自己,避免形成循环
- return;
- }
-
- executorService.submit(() -> {
- try {
- System.err.println("反射注入:" + clazz.getName());
- // 生成所有的构造器
- Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
- // 找到无参构造器然后实例化
- Constructor declaredConstructor = clazz.getDeclaredConstructor();
- declaredConstructor.setAccessible(true);
- Object newInstance = declaredConstructor.newInstance();
- Method[] methods = clazz.getDeclaredMethods();
- for (Method method : methods) {
- try {
- // 实例化成功,那么调用一下
- method.setAccessible(true);
- // graalvm必须需要声明方法
- method.invoke(newInstance);
- } catch (Throwable e) {
- }
- }
- Field[] fields = clazz.getDeclaredFields();
- for (Field field : fields) {
- try {
- field.setAccessible(true);
- field.getType();
- String name = field.getName();
- field.get(newInstance);
-
- } catch (Throwable e) {
- }
- }
- System.err.println("反射注入完成:" + clazz.getName());
- } catch (Throwable e) {
- }
- });
- }
-
- }
添加这个类完成以后再执行
- PS D:\eclipse_file\new_ws\springboot3-demo3\target> java -agentlib:native-image-agent=config-output-dir=D:\eclipse_file\new_ws\springboot3-demo3\src\main\resources\META-INF\native-image -jar .\springboot3-demo3-0.0.1-SNAPSHOT.jar
-
- . ____ _ __ _ _
- /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
- ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
- \\/ ___)| |_)| | | | | || (_| | ) ) ) )
- ' |____| .__|_| |_|_| |_\__, | / / / /
- =========|_|==============|___/=/_/_/_/
- :: Spring Boot :: (v3.1.1)
- 2023-07-19T18:06:22.193+08:00 INFO 14292 --- [ main] c.e.demo.Springboot3Demo3Application : Starting Springboot3Demo3Application v0.0.1-SNAPSHOT using Java 20.0.1 with PID 14292 (D:\eclipse_file\new_ws\springboot3-demo3\target\springboot3-demo3-0.0.1-SNAPSHOT.jar started by PC in D:\eclipse_file\new_ws\springboot3-demo3\target)
- 2023-07-19T18:06:22.198+08:00 INFO 14292 --- [ main] c.e.demo.Springboot3Demo3Application : No active profile set, falling back to 1 default profile: "default"
- 2023-07-19T18:06:23.262+08:00 INFO 14292 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
- 2023-07-19T18:06:23.264+08:00 INFO 14292 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
- 2023-07-19T18:06:23.310+08:00 INFO 14292 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 22 ms. Found 0 Redis repository interfaces.
- 2023-07-19T18:06:24.451+08:00 INFO 14292 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
- 2023-07-19T18:06:24.463+08:00 INFO 14292 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
- 2023-07-19T18:06:24.463+08:00 INFO 14292 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.10]
- 2023-07-19T18:06:24.541+08:00 INFO 14292 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
- 2023-07-19T18:06:24.544+08:00 INFO 14292 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2252 ms
- 配置文件下 scanclass 开启了生成反射类
- 反射注入:com.example.demo.control.TestController$$SpringCGLIB$$0
- 反射注入:com.example.demo.annotation.aspect.TestAnnotationAspect
- 反射注入:com.example.demo.control.TestController
- 反射注入:com.example.demo.Springboot3Demo3Application$$SpringCGLIB$$0
- 反射注入:com.example.demo.annotation.TestAnnotation
- 反射注入:com.example.demo.Springboot3Demo3Application__BeanFactoryRegistrations
- 反射注入:com.example.demo.Springboot3Demo3Application__BeanDefinitions
- 反射注入:com.example.demo.annotation.aspect.TestAnnotationAspect__BeanDefinitions
- 反射注入:com.example.demo.control.TestController__BeanDefinitions
- 反射注入完成:com.example.demo.annotation.aspect.TestAnnotationAspect__BeanDefinitions
- 反射注入完成:com.example.demo.control.TestController
- 反射注入完成:com.example.demo.control.TestController__BeanDefinitions
- 反射注入完成:com.example.demo.annotation.aspect.TestAnnotationAspect
- 反射注入完成:com.example.demo.Springboot3Demo3Application__BeanFactoryRegistrations
- 反射注入完成:com.example.demo.Springboot3Demo3Application__BeanDefinitions
- 反射注入完成:com.example.demo.Springboot3Demo3Application$$SpringCGLIB$$0
- 反射注入完成:com.example.demo.control.TestController$$SpringCGLIB$$0
- 反射注入:com.example.demo.Springboot3Demo3Application__ApplicationContextInitializer
- 反射注入:com.example.demo.Springboot3Demo3Application
- 反射注入完成:com.example.demo.Springboot3Demo3Application__ApplicationContextInitializer
- 反射注入完成:com.example.demo.Springboot3Demo3Application
- 2023-07-19T18:06:27.476+08:00 INFO 14292 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
- 2023-07-19T18:06:27.486+08:00 INFO 14292 --- [ main] c.e.demo.Springboot3Demo3Application : Started Springboot3Demo3Application in 5.764 seconds (process running for 6.33)
- PS D:\eclipse_file\new_ws\springboot3-demo3\target>
当配置文件执行完毕,停止服务,查看resources目录下,已经生成了对应的配置文件
此时再次执行编译,运行生成后的文件
此时最好将配置文件和编译后的文件放在一起,关闭执行的反射配置类,此时这个类已经没有作用了
访问 http://localhost:8080/test1?num=1
- PS D:\eclipse_file\new_ws\springboot3-demo3\target> .\springboot3-demo3.exe
-
- . ____ _ __ _ _
- /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
- ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
- \\/ ___)| |_)| | | | | || (_| | ) ) ) )
- ' |____| .__|_| |_|_| |_\__, | / / / /
- =========|_|==============|___/=/_/_/_/
- :: Spring Boot :: (v3.1.1)
- ....
- 2023-07-19T18:12:19.231+08:00 INFO 4812 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 47 ms
- 配置文件下 scanclass 关闭了生成反射类
- ...
- 2023-07-19T18:12:21.648+08:00 INFO 4812 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
- 执行TestAnnotation注解
- 总共耗时0ms
- 9
另外可以看一下,编译后的java服务,内存占用只有37.9m
代码库:
GitHub - cjs199/springboot3-demo4
在测试中,spring-boot-starter-data-redis自带的lettuce-core在广播订阅消息时,会引发一些奇怪的错误,能够查询到的资料很少,不知道原因,于是只能将maven下的依赖从 lettuce 修改成 jedis
- ly configure spring.jpa.open-in-view to disable this warning
- 2023-07-21T12:25:42.063+08:00 INFO 16408 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
- Exception in thread "task-1" 2023-07-21T12:25:42.064+08:00 WARN 16408 --- [ main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'RedisMessageListenerContainer'
- java.lang.UnsatisfiedLinkError: jdk.jfr.internal.JVM.isExcluded(Ljava/lang/Class;)Z [symbol: Java_jdk_jfr_internal_JVM_isExcluded or Java_jdk_jfr_internal_JVM_isExcluded__Ljava_lang_Class_2]
- at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.access.JNINativeLinkage.getOrFindEntryPoint(JNINativeLinkage.java:152)
- at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNIGeneratedMethodSupport.nativeCallAddress(JNIGeneratedMethodSupport.java:53)
- at jdk.jfr@20.0.1/jdk.jfr.internal.JVM.isExcluded(Native Method)
- ....
- at java.base@20.0.1/java.lang.Thread.runWith(Thread.java:1636)
- at java.base@20.0.1/java.lang.Thread.run(Thread.java:1623)
- at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:807)
- at org.graalvm.nativeimage.builder/com.oracle.svm.core.windows.WindowsPlatformThreads.osThreadStartRoutine(WindowsPlatformThreads.java:179)
- 2023-07-21T12:25:46.217+08:00 INFO 16408 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
- 2023-07-21T12:25:46.217+08:00 INFO 16408 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
- 2023-07-21T12:25:46.242+08:00 INFO 16408 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
- 2023-07-21T12:25:46.246+08:00 INFO 16408 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
- 2023-07-21T12:25:46.247+08:00 ERROR 16408 --- [ main] o.s.boot.SpringApplication : Application run failed
-
- org.springframework.context.ApplicationContextException: Failed to start bean 'RedisMessageListenerContainer'
- at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[demo-1.exe:6.0.10]
- at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[demo-1.exe:6.0.10]
- ...
- at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[demo-1.exe:3.1.1]
- at com.example.demo.Demo1Application.main(Demo1Application.java:11) ~[demo-1.exe:na]
- Caused by: java.lang.NoClassDefFoundError: Could not initialize class io.lettuce.core.event.connection.JfrConnectionCreatedEvent
- at java.base@20.0.1/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) ~[demo-1.exe:na]
- ....
- at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[demo-1.exe:6.0.10]
- ... 13 common frames omitted
-
- PS D:\eclipse_file\ws3\demo-1\target>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- <exclusions>
- <exclusion>
- <groupId>io.lettuce</groupId>
- <artifactId>lettuce-core</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>redis.clients</groupId>
- <artifactId>jedis</artifactId>
- </dependency>
然后,mysql测试,数据库连接一个最简单的表
- CREATE TABLE `sys_user` (
- `id` varchar(255) NOT NULL,
- `password` varchar(255) DEFAULT NULL,
- `username` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`id`) USING BTREE,
- KEY `username_index` (`username`) USING BTREE
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC;
测试内容如下,这些功能足够满足大部分呢开发中使用场景了
- package com.example.demo.control;
-
- import java.util.List;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import com.example.demo.jpa.SysUser;
- import com.example.demo.jpa.SysUserRepository;
-
- import cn.hutool.core.util.RandomUtil;
- import cn.hutool.json.JSONUtil;
-
- @RestController
- public class TestControl {
-
- @Autowired
- private SysUserRepository sysUserRepository;
-
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- // 数据库保存
- @GetMapping("/test_save")
- public void test_save() throws Exception {
- System.err.println("save");
- SysUser sysUser = new SysUser();
- sysUser.setUsername(RandomUtil.randomString(3));
- sysUser.setPassword(RandomUtil.randomString(3));
- sysUserRepository.save(sysUser);
- }
-
- // 数据库所有查找
- @GetMapping("/test_find_all")
- public String test_find_all() throws Exception {
- List<SysUser> findAll = sysUserRepository.findAll();
- System.err.println(JSONUtil.toJsonPrettyStr(findAll));
- return JSONUtil.toJsonPrettyStr(findAll);
- }
-
- // 数据库id查找
- @GetMapping("/test_find_by_id")
- public String test_find_by_id(String id) throws Exception {
- SysUser sysUser = sysUserRepository.getOne(id);
- System.err.println(JSONUtil.toJsonPrettyStr(sysUser));
- return JSONUtil.toJsonPrettyStr(sysUser);
- }
-
- // redis 键值对数据设置
- @GetMapping("/test_redis_kv_set")
- public String test_redis_kv_set() throws Exception {
- stringRedisTemplate.opsForValue().set("123", "456");
- return "OK";
- }
-
- // redis 键值对数据获取
- @GetMapping("/test_redis_kv_get")
- public String test_redis_kv_get() throws Exception {
- System.err.println(stringRedisTemplate.opsForValue().get("123"));
- return "OK";
- }
-
- // redis广播消息测试
- @GetMapping("/test_redis_pubsub")
- public String test_redis_pubsub() throws Exception {
- stringRedisTemplate.convertAndSend("test_redis_pubsub", "a msg");
- return "OK";
- }
-
- // redis队列测试
- @GetMapping("/test_redis_queue")
- public String test_redis_queue() throws Exception {
- stringRedisTemplate.opsForList().rightPush("MsgQueue", "a msg");
- return "OK";
- }
-
-
- }
测试,所有功能都是正常的
- PS D:\eclipse_file\ws3\demo-1\target> .\demo-1.exe
-
- . ____ _ __ _ _
- /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
- ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
- \\/ ___)| |_)| | | | | || (_| | ) ) ) )
- ' |____| .__|_| |_|_| |_\__, | / / / /
- =========|_|==============|___/=/_/_/_/
- :: Spring Boot :: (v3.1.1)
- 2023-07-21T12:39:14.776+08:00 INFO 5156 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
- save
- Hibernate: insert into sys_user (password,username,id) values (?,?,?)
- Hibernate: select s1_0.id,s1_0.password,s1_0.username from sys_user s1_0
- [
- {
- "id": "4028dc158976be5a018976bead320000",
- "password": "r58",
- "username": "rix"
- },
- ....
- ]
- Hibernate: select s1_0.id,s1_0.password,s1_0.username from sys_user s1_0 where s1_0.id=?
- {
- "id": "4028dc378973329b0189733358ce0002",
- "password": "swv",
- "username": "w1m"
- }
- MsgPubSub收到消息:a msg
- MsgPubSub2收到消息:a msg
- 收到队列消息:a msg
- 2023-07-21T12:40:01.024+08:00 INFO 5156 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
- 2023-07-21T12:40:01.024+08:00 INFO 5156 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
- 2023-07-21T12:40:01.025+08:00 INFO 5156 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
- PS D:\eclipse_file\ws3\demo-1\target>
服务资源占用还是很低的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。