jdk8升级JDK17避坑指南(适用于SpringBoot2.3---SpringBoot2.7升级)

2024-02-27 1461阅读

温馨提示:这篇文章已超过388天没有更新,请注意相关的内容是否还可用!

jdk8升级JDK17避坑指南

  • jdk8升级JDK17避坑指南
    • 一、模块化对反射的影响
      • 1.1 示例通过反射获取String的value值
      • 1.2 示例Orika JavaBean映射jdk17报错
      • 二、删除的内置库
        • 2.1、删除JAXB、soup相关
        • 2.2、删除javax相关包
        • 2.2.1、删除javax.annotation重命名为jakarta.annotation
        • 2.2.2、删除javax.validation重命名为jakarta.validation
        • 2.3、删除sun.misc.* 下的包,如sun.misc.BASE64Encoder
        • 三、字体相关报错
        • 四、jvm参数修改
        • 五、jdk21运行打包java.lang.NoSuchFieldError:com.sun.tools.javac.tree.JCTree$JCImport

          jdk8升级JDK17避坑指南

          随着SpringBoot2.7的发布,支持jdk8~jdk21。Springboot3.X发布,最低需要jdk17。升级jdk17是大势所趋。

          本文升级适用于SpringBoot2.7.x从jdk8升级到jdk17操作指南,成功把ruoyi4.x微服务的jdk8升级到jdk17,应用到生产。

          参考1:重磅!Spring Boot 2.7 正式发布、

          参考2:hutool-希望Hutool能支持下JDK8~JDK17的所有版本

          参考3:aliyun-一文详解|从JDK8飞升到JDK17,再到未来的JDK21

          参考4:程序员DD-从 Java 8 升级到 Java 17 踩坑全过程,建议收藏!

          参考5:老卫waylau-JDK

          一、模块化对反射的影响

          正式开发业务系统,使用反射修改jdk自带类场景较少。即使修改,通过–add-opens的jvm参数,即可解决该问题。

          由于jdk9增加的模块化设计,导致对系统内置类反射受到限制,出现类似的错误,自己开发的类没有影响。

          1.1 示例通过反射获取String的value值

           public static void main(String[] args) {
                  // jdk17使用反射,无需修改即可正常使用的示例
                  //在 Java8 中,没有人能阻止你访问特定的包;只要 setAccessible(true) 就可以了
                  NucPerson nucPerson = new NucPerson();
                  nucPerson.setPersonName("张三");
                  ReflectUtil.setFieldValue(nucPerson, "personPhone", "18800001111");//hutool5中工具类
                  System.out.println(nucPerson.getPersonPhone());
                  System.out.println(ReflectUtil.getFieldMap(NucPerson.class));
                  // jdk17使用反射系统模块的类,必须要增加--add-opens的反射示例
                  // Java9 模块化以后,一切都变了,只能通过 --add-exports 和 --add-opens 来打破模块封装
                  Object stringValue = ReflectUtil.getFieldValue(nucPerson.getPersonName(), "value");
                  System.out.println(stringValue);
              }
          
          java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.util.Map sun.reflect.annotation.AnnotationInvocationHandler.memberValues accessible: module java.base does not "opens sun.reflect.annotation" to unnamed module @52d455b8
          	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]
          	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]
          	at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) ~[na:na]
          	at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) ~[na:na]
          	at cn.hutool.core.util.ReflectUtil.setAccessible(ReflectUtil.java:966) ~[hutool-core-5.7.19.jar:na]
          	at cn.hutool.core.util.ReflectUtil.getFieldValue(ReflectUtil.java:266) ~[hutool-core-5.7.19.jar:na]
          	at cn.hutool.core.util.ReflectUtil.getFieldValue(ReflectUtil.java:234) ~[hutool-core-5.7.19.jar:na]
          	at cn.hutool.core.annotation.AnnotationUtil.setValue(AnnotationUtil.java:215) ~[hutool-core-5.7.19.jar:na]
          

          解决:增加jvm启动参数(java虚拟机启动参数)

          --add-opens java.base/sun.reflect.annotation=ALL-UNNAMED
          

          汇总:

                  -Djdk.home=/Library/Java/JavaVirtualMachines/openjdk-18.0.1.1/Contents/Home
                  -Xms24m
                  -Xmx768m
                  -DTopSecurityManager.disable=true
          	--add-exports=java.desktop/com.sun.java.swing.plaf.gtk=ALL-UNNAMED
          	--add-exports=java.desktop/sun.awt=ALL-UNNAMED
          	--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor.event=ALL-UNNAMED
          	--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED
          	--add-exports=java.desktop/sun.swing=ALL-UNNAMED
          	--add-exports=jdk.attach/sun.tools.attach=ALL-UNNAMED
          	--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED
          	--add-opens=java.desktop/javax.swing.plaf.synth=ALL-UNNAMED
          	--add-opens=java.base/java.net=ALL-UNNAMED
          	--add-opens=java.base/java.lang.ref=ALL-UNNAMED
          	--add-opens=java.base/java.lang=ALL-UNNAMED
          	--add-opens=java.desktop/javax.swing=ALL-UNNAMED
          	--add-opens=java.desktop/javax.swing.plaf.basic=ALL-UNNAMED
          	-XX:+IgnoreUnrecognizedVMOptions
          	-XX:+HeapDumpOnOutOfMemoryError
          	-XX:HeapDumpPath=/Users/duandazhi/Library/Application Support/VisualVM/2.1.2/var/log/heapdump.hprof
          

          --add-opens和--add-exports区别,opens是深度反射,可以打开对私有变量、成员的反射;exports只能打开公有变量的反射。总结:使用--add-opens肯定没问题。

          jdk8升级JDK17避坑指南(适用于SpringBoot2.3---SpringBoot2.7升级)

          总结:模块化需要增加的JVM打开反射参数,对常规业务系统影响不大,毕竟对系统类反射操作很少,对自己开发的类进行反射操作并没有限制。

          1.2 示例Orika JavaBean映射jdk17报错

          1、BeanUtils常用的Bean映射工具,包含apache的BeanUtils和spring的BeanUtils,底层使用简单,但底层反射效率比较低。

          2、BeanCopier,基于cglib的BeanCopier直接生成字节码文件.class文件,效率有很大提升。但是,当属性名称和属性类型存在差异时,CGLib实现的BeanCopier会退化成暴力反射,性能就会低下。一般配合工具框架使用,如dubbo。

          3、Orika,底层采用了javassist类库生成Bean映射的字节码,后直接加载执行生成的字节码文件,速度比反射快很多,支持嵌套。

          参考1:Bean复制选型

          BeanUtils、BeanCopier、Dozer、Orika 哪家强? 、

          Java常见bean mapper的性能及原理分析1、

          Java常见bean mapper的性能及原理分析2、

          常见Bean拷贝框架使用姿势及性能对比、

          对比性能:反射类(各类BeanUtils、Dozer) /** * 对象之间copy,可以是相同类型,也可以是不同类型;但不能是集合 * * @param srcObj * @param targetObj * @return dest */ @Bean(name = "beanMapper") public BeanMapper registUtilsBeanMapper() { //多个Bean复制实现类,使用其中一个 ///return new DozerBeanMapperImpl(); return new OrikaBeanMapperImpl(); } } /** * 实例. */ private static final MapperFacade MAPPER; static { //---------------------------这段代码jdk8运行没有问题,jdk17报错 没有使用反射copy的权限------------------ /** * Caused by: java.lang.reflect.InaccessibleObjectException: * Unable to make protected native java.lang.Object java.lang.Object.clone() * throws java.lang.CloneNotSupportedException accessible: * module java.base does not "opens java.lang" to unnamed module * @see CloneableConverter#CloneableConverter 报错没有反射权限 */ // 如果src中属性为null,就不复制到dest MapperFactory mapperFactory = new DefaultMapperFactory.Builder() .mapNulls(false).build(); // 如果属性是Object,就只复制引用,不复制值,可以避免循环复制 mapperFactory.getConverterFactory().registerConverter( new PassThroughConverter(Object.class)); MAPPER = mapperFactory.getMapperFacade(); } /** * 把src中的值复制到dest中. * src 和 dest 对象均不能为空 */ @Override public Objects.requireNonNull(srcObj, "copy src 不能为null"); Objects.requireNonNull(destObj, "copy dest 不能为null"); if (srcObj instanceof Collection || destObj instanceof Collection) { throw new Exception("copy对象不能是集合,集合copy请使用 copyList方法"); } MAPPER.map(srcObj, destObj); return destObj; } /** * 复制list. */ @Override public return MAPPER.mapAsList(srcObjs, destClz); } } /** * 持有Dozer单例, 避免重复创建DozerMapper消耗资源. */ private static DozerBeanMapper dozer = new DozerBeanMapper(); /** * 基于Dozer转换对象的类型. */ public return dozer.map(source, destinationClass); } /** * 基于Dozer转换Collection中对象的类型. */ public List T destinationObject = dozer.map(sourceObject, destinationClass); destinationList.add(destinationObject); } return destinationList; } /** * 基于Dozer将对象A的值拷贝到对象B中. * src 和 dest 对象均不能为空 */ @Override public Objects.requireNonNull(sourceObj, "原对象不能为空!"); Objects.requireNonNull(destinationObject, "目标对象不能为空!"); if (sourceObj instanceof Collection || destinationObject instanceof Collection) { throw new RrException("copy对象不能是集合,集合copy请使用 copyList方法"); } dozer.map(sourceObj, destinationObject); return destinationObject; } @Override public return mapList(srcObjs, destClz); } } private DefaultRedisScript redisLUAScript = new DefaultRedisScript private static final long serialVersionUID = 1L; @NotNull(message = "被监护人ID不能为空") private Long childId; /** * 01本人、02户主、10配偶、51父亲、52母亲、55岳父、56岳母、97其他亲属、98非亲属等等 */ @NotBlank(message = "监护关系不能为空") private String relationType; } nohup.out echo '================>开始重启服务.........' #启动参数 ### 获取自定义启动参数的方式 ### @Value("${ghouse.datacenterId:0}") ### private long datacenterId; ### 项目经常部署,开发、测试、生产、生产备用等多套环境,nacos相关会经常变动 START_ARGS=" --spring.profiles.active=${PROFILE}\ --custom=127.0.0.1:8850\ --evn=my-test-key\ --ghouse.datacenterId=1\ --spring.cloud.nacos.discovery.server-addr=10.16.58.146:8850\ --spring.cloud.nacos.config.server-addr=10.16.58.146:8850\ --spring.cloud.inetutils.preferred-networks=10.16\ --spring.cloud.nacos.config.namespace=xahs-dev\ --spring.cloud.nacos.discovery.namespace=xahs-dev " echo '' java -version echo '' ## 常规启动版本 ## java [options] -jar [args...] #nohup java $JAVA_OPTS -jar vaccine-epi.jar --spring.profiles.active=prod >> nohup.out 2>&1 & #nohup java $JAVA_OPTS -jar $APP_NAME 2>&1 & echo "java $ARMS_JVM $JAVA_OPTS -jar $APP_NAME $START_ARGS >> nohup.out 2>&1 &" nohup java $ARMS_JVM $JAVA_OPTS -jar $APP_NAME $START_ARGS >> nohup.out 2>&1 & tail -f nohup.out

          五、jdk21运行打包java.lang.NoSuchFieldError:com.sun.tools.javac.tree.JCTree$JCImport

          运行打包报错:java.lang.NoSuchFieldError:com.sun.tools.javac.tree.JCTree$JCImport

          参考:Java|IDEA 运行和打包报错解决

          ## 升级lombok到1.18.30
          
              org.projectlombok
              lombok
              1.18.30
          
          
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]