【Java】The Java Headless Mode

简介: 【Java】The Java Headless Mode

原文

www.baeldung.com/java-headle…

引言

这篇文章源自个人看到了Kafka的启动脚本中一个“奇怪”的参数:


-Djava.awt.headless=true

拿去谷歌一下发现网上的描述都大差不差,这里找了baeldung(类似国外的菜鸟教程)中的一篇文章,本文内容来自于英文博客原文。

这篇文章介绍了 -Djava.awt.headless 参数的作用,网上大部分的资料都是说“为了提高计算效率和适配性我们可以使用这种模式,关闭图形显示等功能可以大大节省设备的计算能力,而且对一些本身没有相关显示设备的机器也能适配,程序也可以正常运行。”,个人认为这些理论内容不太能理解。

当然也有诸如服务器没有显示屏什么的,你得告诉程序一声,你工作的地方没有这些设备这种说法 ,为此找了一篇国外的博客介绍。

如何设置?

设置方式如下:

  • 在system property中设置 java.awt.headlesstrue

SpringBoot的源码中可以找到类似的代码:


private void configureHeadlessProperty() {
        System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
    }
  • 启动脚本中进行设置-Djava.awt.headless=true:在Kafka的脚本当中存在类似的启动脚本。


KAFKA_JVM_PERFORMANCE_OPTS="-server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -XX:MaxInlineLevel=15 -Djava.awt.headless=true"

最后一个参数显示它使用headless模式。

  • 在执行命令的时候动态添加-Djava.awt.headless=true,这种方式和脚本设置启动的方式类似。

Headless 绕过重量级组件

如果一个带有GUI组件的代码在开和关Headless模式下运行分别会有什么不同的效果?


@Test  
public void FlexibleApp() {  
    if (GraphicsEnvironment.isHeadless()) {  
        System.out.println("Hello World");  
    } else {  
        JOptionPane.showMessageDialog(null, " showMessageDialog Hello World");  
    }  
}

上面的代码如果关闭了Headless模式,则打印Hello World会变为图形化界面。

image.png

如果开启Headless,则会打印在控制台。

Headless Mode 在UI组件的应用案例

Java Headless Mode 的典型案例可能是使用图形转化器,我们有时候可能需要图形数据进行图像处理,但是不一定需要实际显示。

下面通过一个单元测试来模拟这些情况:


@Before  
    public void setUpHeadlessMode() {  
        // 通过注释掉下面的代码测试不同的效果  
//        System.setProperty("java.awt.headless", "true");  
    }  
    @Test  
    public void whenSetUpSuccessful_thenHeadlessIsTrue() {  
        boolean headless = GraphicsEnvironment.isHeadless();  
        Assert.assertTrue(headless);  
    }/*  
    测试通过  
    注释下面的代码之后,单元测试不通过  
    //        System.setProperty("java.awt.headless", "true");    */

使用awt的组件java.awt.GraphicsEnvironment#isHeadless,注意较高版本的JDK(例如 JDK11)中awk被直接干掉了,需要下载外部依赖导入才可以使用,建议选择JDK8以及以下的版本测试上面的程序。

上面的代码如果注释掉 headless模式,单元测试会直接不通过。下面简单构建了一个图形转化器:


@Test  
public void whenHeadlessMode_thenImagesWork() {  
    boolean result = false;  
    try (InputStream inStream = HeadlessModeUnitTest.class.getResourceAsStream(IN_FILE);  
         FileOutputStream outStream = new FileOutputStream(OUT_FILE)) {  
        BufferedImage inputImage = ImageIO.read(inStream);  
        result = ImageIO.write(inputImage, FORMAT, outStream);  
    }  
    assertThat(result).isTrue();  
}

在接下来的这个例子中,我们可以看到所有字体的信息,包括字体的度量,也可以让我们使用。


@Test  
    public void whenHeadless_thenFontsWork() {  
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();  
        String fonts[] = ge.getAvailableFontFamilyNames();  
//        assertThat(fonts).isNotEmpty();  
        Font font = new Font(fonts[0], Font.BOLD, 14);  
        FontMetrics fm = (new Canvas()).getFontMetrics(font);  
//        assertThat(fm.getHeight()).isGreaterThan(0);  
//        assertThat(fm.getAscent()).isGreaterThan(0);  
//        assertThat(fm.getDescent()).isGreaterThan(0);  
    }

HeadlessException

有些设备是需要外部设备支持的,否则会抛出下面的异常:


Exception in thread "main" java.awt.HeadlessException
  at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:204)
  at java.awt.Window.<init>(Window.java:536)
  at java.awt.Frame.<init>(Frame.java:420)

可以使用Frame来进行验证:


@Test  
   public void whenHeadlessmode_thenFrameThrowsHeadlessException() {  
       Frame frame = new Frame();  
       frame.setVisible(true);  
       frame.setSize(120, 120);  
   }/*  
   在开关Headless模式后会有不同的结果  
   开启:通过      
   关闭  
   ava.awt.HeadlessExceptionat java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:204)  
at java.awt.Window.<init>(Window.java:536)  
at java.awt.Frame.<init>(Frame.java:420)  
at java.awt.Frame.<init>(Frame.java:385)  
   */

作为一个经验法则,请记住,像Frame和Button这样的顶级组件总是需要一个交互式的环境,并且会抛出这个异常。然而,如果没有明确设置无头模式,它将被抛出,成为一个不可恢复的错误。

总结

通过代码和案例分析,我们大致了解Java Headless Mode模式是怎么一回事,说白了就是屏蔽掉外置设备比如GUI的额外开销,转而用程序自己去进行模拟。

Kafka设置这样的参数就是把性能发挥到极致,摈弃一切外部设备干扰,让服务器尽可能的通过自身程序模拟外部设备。

比如重量级组件控制台打印,在外部设计可以通过JOptionPane的GUI组件实现可视化效果,而Headless则是利用我们熟知的System.out控制台输入输出流完成打印功能的模拟。

以上就是关于 Java Headless Mode 的理解。

程序demo

本文的个人实验代码放到下面部分,文章提到的部分代码可能会无法编译通过(图形转化器的代码),个人理解代码意图之后就没有深究了,读者碰到报错问题忽略删除即可。

PS:建议使用JDK8之前的版本,可以直接引入awt和swing的相关组件。


import jdk.nashorn.internal.ir.LiteralNode;  
import org.junit.Assert;  
import org.junit.Before;  
import org.junit.Test;  
import javax.imageio.ImageIO;  
import javax.swing.*;  
import java.awt.*;  
import java.awt.image.BufferedImage;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import static org.junit.Assert.assertThat;  
public class HandlessTest {  
    @Before  
    public void setUpHeadlessMode() {  
        // 通过注释掉下面的代码测试不同的效果  
        System.setProperty("java.awt.headless", "true");  
    }  
    @Test  
    public void whenSetUpSuccessful_thenHeadlessIsTrue() {  
        boolean headless = GraphicsEnvironment.isHeadless();  
        Assert.assertTrue(headless);  
    }/*  
    测试通过  
    注释下面的代码之后,单元测试不通过  
    //        System.setProperty("java.awt.headless", "true");    */  
    //@Test  
    //public void whenHeadlessMode_thenImagesWork() throws IOException {  
    //    boolean result = false;  
    //    try (InputStream inStream = HandlessTest.class.getResourceAsStream(IN_FILE);  
    //         FileOutputStream outStream = new FileOutputStream(OUT_FILE)) {  
    //        BufferedImage inputImage = ImageIO.read(inStream);  
    //        result = ImageIO.write(inputImage, FORMAT, outStream);  
    //   }  
    //    Assert.assertTrue(result);  
    //}  
//    @Test  
//    public void whenHeadlessMode_thenImagesWork() {  
//        boolean result = false;  
//        try (InputStream inStream = HeadlessModeUnitTest.class.getResourceAsStream(IN_FILE);  
//             FileOutputStream outStream = new FileOutputStream(OUT_FILE)) {  
//            BufferedImage inputImage = ImageIO.read(inStream);  
//            result = ImageIO.write(inputImage, FORMAT, outStream);  
//        }  
//  
//        assertThat(result).isTrue();  
//    }  
    @Test  
    public void whenHeadless_thenFontsWork() {  
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();  
        String fonts[] = ge.getAvailableFontFamilyNames();  
//        assertThat(fonts).isNotEmpty();  
        Font font = new Font(fonts[0], Font.BOLD, 14);  
        FontMetrics fm = (new Canvas()).getFontMetrics(font);  
//        assertThat(fm.getHeight()).isGreaterThan(0);  
//        assertThat(fm.getAscent()).isGreaterThan(0);  
//        assertThat(fm.getDescent()).isGreaterThan(0);  
    }  
    @Test  
    public void whenHeadlessmode_thenFrameThrowsHeadlessException() {  
        Frame frame = new Frame();  
        frame.setVisible(true);  
        frame.setSize(120, 120);  
    }/*  
    在开关Headless模式后会有不同的结果  
    开启:通过  
    关闭  
    ava.awt.HeadlessException   at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:204)   at java.awt.Window.<init>(Window.java:536)   at java.awt.Frame.<init>(Frame.java:420)   at java.awt.Frame.<init>(Frame.java:385)  
    */  
    @Test  
    public void FlexibleApp() {  
        if (GraphicsEnvironment.isHeadless()) {  
            System.out.println("Hello World");  
        } else {  
            JOptionPane.showMessageDialog(null, "showMessageDialog Hello World");  
        }  
    }  
}

参考资料

www.jianshu.com/p/7248b3ff5…

www.baeldung.com/java-headle…](www.baeldung.com/java-headle…](www.baeldung.com/java-headle…))


相关文章
|
8月前
|
Java Unix Shell
Java的path的设置与应用
Java的path的设置与应用
178 0
|
8月前
|
安全 Java 程序员
从0开始回顾Java---系列一
Java概述 1、什么是Java? Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。 Java语言作为静态面向对象编程语言的优秀代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。 2、Java 语言的优点? 1. 平台无关性,摆脱硬件束缚,"一次编写,到处运行",保证这一点的是 Java 的虚拟机机制。 2. 相对安全的内存管理和访问机制,避免大部分内存泄漏和指针越界。 3. 面向对象(封装,继承,多态) 4. 支持多线程。C++ 语言没有内置的多线程
|
8月前
|
存储 安全 算法
从0开始回顾Java---系列八
HashMap 1、HashMap 有什么特点? HashMap 基于哈希表的 Map 接口实现,是以 key-value 存储形式存在,主要用来存放键值对。 特点: ● HashMap 的实现不是同步的,这意味着它不是线程安全的 ● key 是唯一不重复的,底层的哈希表结构,依赖 hashCode 方法和 equals 方法保证键的唯一 ● key、value 都可以为null,但是 key 位置只能是一个null ● HashMap 中的映射不是有序的,即存取是无序的 ● key 要存储的是自定义对象,需要重写 hashCode 和 equals 方法,防止出现地址不同内
|
8月前
|
Linux Shell Windows
Java.Runtime.exec()的使用
Java.Runtime.exec()的使用
37 0
|
8月前
|
缓存 Java 调度
从0开始回顾Java---系列五
Integer 1、Integer a= 127,Integer b = 127;Integer c= 128,Integer d = 128;相等吗? 答案是a和b相等,c和d不相等。 ● 对于基本数据类型==比较的值 ● 对于引用数据类型==比较的是地址 Integer a= 127这种赋值,是用到了Integer自动装箱的机制。自动装箱的时候会去缓存池里取Integer对象,没有取到才会创建新的对象。 如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用缓存池中的Integer对象,超过范围 a1==b1的结果是false。 publi
|
8月前
|
存储 安全 算法
从0开始回顾Java---系列九
TreeMap 1、TreeMap 有什么特点? 1. TreeMap是基于红黑树的一种提供顺序访问的Map,增删改查的平均和最差时间复杂度均为 O(logn) ,最大特点是 Key 有序。 2. Key 必须实现 Comparable 接口或提供的 Comparator 比较器,所以 Key 不允许为 null。 3. TreeMap是一个线程不安全,有序的键值对集合,因为TreeMap实现了SotredMap接口。 4. TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化; 2、讲讲 TreeMap 怎么实现有序的? TreeMap
|
8月前
|
存储 安全 Java
从0开始回顾Java---系列七
引言 1、常见集合有哪些? Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了三个子接口:List、Set、Queue,因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。 ● List代表了有序可重复集合,可直接根据元素的索引来访问; ● Set代表无序不可重复集合,只能根据元素本身来访问; ● Queue是队列集合。 ● Map代表的是存储key-value对的集合,可根据元素的key来访问value。 2、线程安全的集合有哪些?线程不安全的呢? 线程安全的: ● Hashtable:比HashMap多了个线
|
Java
java--方法
java方法学习笔记
81 0
|
Java 应用服务中间件 Linux
Java 在linux或者tomcat下使用java.jwt.*这个类,报java.awt.headless 报空异常
在开发的过程中使用到了java.jwt.*包下的东西,在开发工具中使用没问题,但是如果到了单独的tomcat或Linux里就会报:java.awt.headless null空异常,再去配置java mv?非常麻烦,看我是如何解决的。
141 0
|
Java 编译器
强哥说Java--Java基础
强哥说Java--Java基础
85 0