jdk中不带jre_使用jlink生成自定义jre

缘由

java较新的版本采取模块化设计(>9),下载的jdk中不带jre,使用者可以根据需要自己生成jre。如这里下载的是oracle的jdk17:

test@test:~/jdk-17$ tree -d -L 1
.
├── bin
├── conf
├── include
├── jmods
├── legal
├── lib
└── man

文件夹jmods里是压缩的模块:

test@test:~/jdk-17$ ls jmods/
java.base.jmod            
java.security.sasl.jmod   
jdk.dynalink.jmod                         
jdk.javadoc.jmod           
jdk.management.jfr.jmod
#.......

查看jar包使用到了哪些模块bin/jdeps --list-deps target.jar

例子

这里使用tomcat10,jre使用jdk17生成

  1. jdeps查看模块
test@test:~/jdk-17$ bin/jdeps ~/apache-tomcat-10.1.0-M5/bin/bootstrap.jar 
bootstrap.jar -> java.base
bootstrap.jar -> not found
#......
test@test:~/jdk-17$ bin/jdeps ~/apache-tomcat-10.1.0-M5/bin/tomcat-juli.jar 
org.apache.tomcat.juli
 [file:///home/test/apache-tomcat-10.1.0-M5/bin/tomcat-juli.jar]
   requires java.base
   requires java.logging
   requires java.management
   requires java.sql
org.apache.tomcat.juli -> java.base
org.apache.tomcat.juli -> java.logging
org.apache.tomcat.juli -> java.management
org.apache.tomcat.juli -> java.sql
#......

test@test:~/jdk-17$ bin/jlink --module-path jmods \
--add-modules java.base,java.logging,java.management,java.sql --output tomcat10_jre

启动tomcat

test@test:~/apache-tomcat-10.1.0-M5$ nano bin/setenv.sh 
JRE_HOME=/home/test/jdk-17/tomcat10_jre
CATALINA_PID="$CATALINA_BASE/tomcat.pid"
test@test:~/apache-tomcat-10.1.0-M5$ bin/catalina.sh run
#......
Caused by: java.lang.NoClassDefFoundError: java/beans/PropertyChangeSupport
		at org.apache.catalina.core.StandardServer.<init>(StandardServer.java:152)
		... 30 more
#.....
  1. 添加模块java.desktop
test@test:~/jdk-17$ rm -rf tomcat10_jre/
test@test:~/jdk-17$ bin/jlink --module-path jmods --add-modules \
java.base,java.logging,java.management,java.sql,java.desktop \
--output tomcat10_jre

启动后报错:

#......
Caused by: java.lang.NoClassDefFoundError: javax/naming/NamingException
		at org.apache.catalina.core.StandardServer.<init>(StandardServer.java:82)
		... 30 more
#......
  1. 添加模块java.naming
#......
Caused by: java.lang.ClassNotFoundException: org.ietf.jgss.GSSException
		at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:440)
		at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:587)
		at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
		... 26 more
#......
  1. 添加模块java.security.jgss
#......
Caused by: java.lang.NoClassDefFoundError: java/lang/instrument/ClassFileTransformer
		at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4975)
		at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
		... 38 more
#......
  1. 添加模块java.instrument

启动成功,实际还缺少一个模块,即下文的报错。

java.lang.ClassNotFoundException: sun.rmi.transport.Target

INFO [Thread-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesRmiTargets Failed to find class sun.rmi.transport.Target to clear context class loader for web application [manager]. This is expected on non-Sun JVMs.
	java.lang.ClassNotFoundException: sun.rmi.transport.Target
		at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:440)
		at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:587)
#......
at org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesRmiTargets(WebappClassLoaderBase.java:2252)
#......

源代码如下:

/**
     * This depends on the internals of the Sun JVM so it does everything by
     * reflection.
     */
    private void clearReferencesRmiTargets() {
        try {
            // Need access to the ccl field of sun.rmi.transport.Target to find
            // the leaks
            Class<?> objectTargetClass =
                Class.forName("sun.rmi.transport.Target");
            Field cclField = objectTargetClass.getDeclaredField("ccl");
            cclField.setAccessible(true);
            // Need access to the stub field to report the leaks
        //.......
        } catch (ClassNotFoundException e) {
            log.info(sm.getString("webappClassLoader.clearRmiInfo",
                    getContextName()), e);
        } catch (SecurityException | NoSuchFieldException | IllegalArgumentException |
                IllegalAccessException e) {
            log.warn(sm.getString("webappClassLoader.clearRmiFail",
                    getContextName()), e);
        } catch (InaccessibleObjectException e) {
            // Must be running on without the necessary command line options.
            log.warn(sm.getString("webappClassLoader.addExportsRmi", this.getClass().getModule().getName()));
        }
    }
AttributeDescription
clearReferencesRmiTargetsIf true, Tomcat looks for memory leaks associated with RMI Targets and clears any it finds. This feature uses reflection to identify the leaks and therefore requires that the command line option -XaddExports:java.rmi/sun.rmi.transport=ALL-UNNAMED is set when running on Java 9 and above. Applications without memory leaks should operate correctly with this attribute set to false. If not specified, the default value of true will be used.

from https://tomcat.apache.org/tomcat-9.0-doc/config/context.html

sun.*com.sun.*包中的大多数类型会逐渐不可访问(模块化后新的可访问性规则)。

但是查看文件,启动文件中已经添加java.rmi的访问规则,如下:

test@test:~/apache-tomcat-10.1.0-M5$ nano bin/catalina.sh
#......
# Add the module start-up parameters required by Tomcat
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.lang=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.io=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.util=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.util.concurrent=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED"
export JDK_JAVA_OPTIONS

# ----- Execute The Requested Command -----------------------------------------
#......

其实是我们的jre中没有提供java.rmi模块,重新生成jre即可。
bin/jlink --module-path jmods --add-modules java.base,java.logging,java.management,java.sql,java.desktop,java.naming,java.security.jgss,java.instrument,java.rmi --output tomcat10_jre

配置Https报错

WARNING [https-jsse-nio-8443-exec-1] org.apache.tomcat.util.buf.ByteBufferUtils.<clinit> Cannot use direct ByteBuffer cleaner, memory leaking may occur
	java.lang.ClassNotFoundException: sun.misc.Unsafe
		at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:440)

添加模块jdk.unsupported

浏览器访问失败ERR_SSL_PROTOCOL_ERROR,通过命令curl -i -v --request POST https://ip:port/ --tlsv1.1 --insecure测试报错error:140943F2:SSL routines:ssl3_read_bytes:sslv3 alert unexpected message

添加模块jdk.crypto.cryptoki

Provides the implementation of the SunPKCS11 security provider.

最终命令:bin/jlink --module-path jmods --add-modules java.base,java.logging,java.management,java.sql,java.desktop,java.naming,java.security.jgss,java.instrument,java.rmi,jdk.unsupported,jdk.crypto.cryptoki --output tomcat10_jre

-XaddExports和-XaddOpens区别

  • 使用--add-exports(-XaddExports)包被导出,意味着所有的公共类型和成员都可以在编译和运行时访问。
  • 随着--add-opens打开包,这意味着其中的所有类型和成员(不仅是公共的!)在运行时可以访问;

所以在运行时的主要区别是--add-opens(-XaddOpens)允许“深度反射”,意思是非公共成员的访问。通常可以通过setAccessible(true)的反射代码来识别此类访问。

通过类搜索模块

什么类属于什么模块,可以通过API文档搜索:JAVA SE 17 API

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值