The Java Headless Mode – Java无头模式

最后修改: 2020年 2月 12日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

On occasion, we need to work with graphics-based applications in Java without an actual display, keyboard, or mouse, let’s say, on a server or a container.

有时,我们需要在没有实际显示器、键盘或鼠标的情况下在Java中处理基于图形的应用程序,比如说,在服务器或容器中。

In this short tutorial, we’re going to learn about Java’s headless mode to address this scenario. We’ll also look at what we can do in headless mode and what we can’t.

在这个简短的教程中,我们将学习Java的无头模式来解决这种情况。我们还将看看在无头模式下我们能做什么,不能做什么。

2. Setting up Headless Mode

2.设置无头模式

There are many ways we can set up headless mode in Java explicitly:

我们有很多方法可以在Java中明确设置无头模式。

  • Programmatically setting the system property java.awt.headless to true
  • Using the command line argument: java -Djava.awt.headless=true
  • Adding -Djava.awt.headless=true to the JAVA_OPTS environment variable in a server startup script

If the environment is actually headless, the JVM would be aware of it implicitly. However, there will be subtle differences in some scenarios. We’ll see them shortly.

如果环境实际上是无头的,JVM会隐含地意识到它。然而,在某些情况下会有细微的差别。我们很快就会看到它们。

3. Examples of UI Components in Headless Mode

3.无头模式下UI组件的例子

A typical use case of UI components running in a headless environment could be an image converter app. Though it needs graphics data for image processing, a display is not really necessary. The app could be run on a server and converted files saved or sent over the network to another machine for display.

在无头环境中运行的UI组件的一个典型用例可能是一个图像转换器应用。虽然它需要图形数据来进行图像处理,但实际上并不需要显示。该应用可以在服务器上运行,并将转换后的文件保存或通过网络发送到另一台机器上进行显示。

Let’s see this in action.

让我们看看这个行动。

First, we’ll turn the headless mode on programmatically in a JUnit class:

首先,我们将在一个JUnit 类中以编程方式开启无头模式。

@Before
public void setUpHeadlessMode() {
    System.setProperty("java.awt.headless", "true");
}

To make sure it is set up correctly, we can use java.awt.GraphicsEnvironment#isHeadless:

为了确保它被正确设置,我们可以使用java.awt.GraphicsEnvironment#isHeadless

@Test
public void whenSetUpSuccessful_thenHeadlessIsTrue() {
    assertThat(GraphicsEnvironment.isHeadless()).isTrue();
}

We should bear in mind that the above test will succeed in a headless environment even if the mode is not explicitly turned on.

我们应该记住,在无头环境下,即使没有明确开启该模式,上述测试也会成功。

Now let’s see our simple image converter:

现在让我们看看我们的简单图像转换器。

@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();
}

In this next sample, we can see that information of all fonts, including font metrics, is also available to us:

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

@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);
}

4. HeadlessException

4.HeadlessException

There are components that require peripheral devices and won’t work in the headless mode. They throw a HeadlessException when used in a non-interactive environment:

有一些组件需要外围设备,不能在无头模式下工作。在非交互式环境中使用时,它们会抛出一个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)

This test asserts that using Frame in a headless mode will indeed throw a HeadlessException:

该测试断言,在无头模式下使用Frame确实会抛出HeadlessException

@Test
public void whenHeadlessmode_thenFrameThrowsHeadlessException() {
    assertThatExceptionOfType(HeadlessException.class).isThrownBy(() -> {
        Frame frame = new Frame();
        frame.setVisible(true);
        frame.setSize(120, 120);
    });
}

As a rule of thumb, remember that top-level components such as Frame and Button always need an interactive environment and will throw this exception. However, it will be thrown as an irrecoverable Error if the headless mode is not explicitly set.

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

5. Bypassing Heavyweight Components in Headless Mode

5.绕过无头模式下的重量级组件

At this point, we might be asking a question to ourselves – but what if we have code with GUI components to run on both types of environments – a headed production machine and a headless source code analysis server?

在这一点上,我们可能会问自己一个问题–但如果我们有带GUI组件的代码要在两种环境下运行–有头的生产机器和无头的源代码分析服务器,那该怎么办?

In the above examples, we have seen that the heavyweight components won’t work on the server and will throw an exception.

在上面的例子中,我们已经看到,重量级的组件在服务器上无法工作,会抛出一个异常。

So, we can use a conditional approach:

因此,我们可以使用一个有条件的方法。

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

Using this pattern, we can create a flexible app that adjusts its behavior as per the environment.

使用这种模式,我们可以创建一个灵活的应用程序,根据环境调整其行为。

6. Conclusion

6.结语

With different code samples, we saw the how and why of headless mode in java. This technical article provides a complete list of what all can be done while operating in headless mode.

通过不同的代码样本,我们看到了java中无头模式的方式和原因。这篇技术文章提供了一个完整的清单,说明在无头模式下操作时都可以做什么。

As usual, the source code for the above examples is available over on GitHub.

像往常一样,上述例子的源代码可以在GitHub上找到