Headless 模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些输入输出外设的情况下可以使用该模式。
Headless 模式虽然不是我们愿意见到的,但事实上我们却常常需要在该模式下工作,尤其是服务器端程序开发者。因为服务器(如提供Web服务的主机)往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端(如浏览器所在的配有相关的显示设备、键盘和鼠标的主机)。
想想你的应用不停的生成一张图片,比如,当用户每次登陆系统是都要生成一张认证图片。当创建图片时,你的应用既不需要显示器也不需要键盘。让我们假设一下,现在你的应用有个主架构或者专有服务器,但这个服务没有显示器,键盘或者鼠标。理想的决定是用环境的大量视觉计算能力而不是非视觉特性。在Headless模式下生成的图片可以传递到Headful系统进行更深层次渲染。
在 java.awt.toolkit 和 java.awt.graphicsenvironment 类中有许多方法,除了对字体,图像和打印的操作外还有调用显示器,键盘和鼠标的方法。但是有一些类中,比如 Canvas 和 Panel,可以在 headless 模式下执行。在 J2SE 1.4 平台之后就提供了对 Headless 模式的支持。
一般是在程序开始激活 headless 模式,告诉程序,现在你要工作在 Headless 模式下,就不要指望硬件帮忙了,你得自力更生,依靠系统的计算能力模拟出这些特性来。
你可以通过下面代码开启 Headless 模式:
System.setProperty("java.awt.headless", "true");(1)实例中用到的图片,名称为“mysql_cluster.png”。将该图片放到项目的 resources 目录。

(2)实例 Java 代码:
import java.awt.*;
import java.io.*;
import java.awt.print.*;
import java.net.URL;
import javax.imageio.*;
/**
* 简单实用 java 的 Headless 模式
*
* @author Administrator
* @date 2021/3/6 22:16
*/
public class HeadlessBasics {
public static void main(String[] args) {
// Set system property.
// Call this BEFORE the toolkit has been initialized, that is,
// before Toolkit.getDefaultToolkit() has been called.
System.setProperty("java.awt.headless", "true");
// 下面的这段代码展示了如果使用 Toolkit类的beep方法发出嘟嘟声
// This triggers creation of the toolkit.
// Because java.awt.headless property is set to true, this
// will be an instance of headless toolkit.
Toolkit tk = Toolkit.getDefaultToolkit();
// Standard beep is available.
tk.beep();
// Check whether the application is
// running in headless mode.
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
System.out.println("Headless mode: " + ge.isHeadless());
// No top levels are allowed.
boolean created = false;
try {
Frame f = new Frame("Frame");
created = true;
} catch (Exception z) {
z.printStackTrace(System.err);
created = false;
}
System.err.println("Frame is created: " + created);
// No other components except Canvas and Panel are allowed.
created = false;
try {
Button b = new Button("Button");
created = true;
} catch (Exception z) {
z.printStackTrace(System.err);
created = false;
}
System.err.println("Button is created: " + created);
// Canvases can be created.
final Canvas c = new Canvas() {
public void paint(Graphics g) {
Rectangle r = getBounds();
g.drawLine(0, 0, r.width - 1, r.height - 1);
// Colors work too.
// 这段代码显示了如何使用指定的红,绿,蓝的值来设置一条线的颜色。Graphics对象是用来绘制这条线的
g.setColor(new Color(255, 127, 0));
g.drawLine(0, r.height - 1, r.width - 1, 0);
// And fonts
// 这段代码显示了怎么使用Font类画一个文本字符串并设置文字的字体。Graphics对象是用来绘制这个字符串的
g.setFont(new Font("Arial", Font.ITALIC, 12));
g.drawString("Test", 32, 8);
}
};
// And all the operations work correctly.
c.setBounds(32, 32, 128, 128);
// Images are available.
// 下面的代码中,javax.imageio.ImageIO类的使用read()方法对 mysql_cluster.png 图片进行解码,并返回一个缓存图片
Image i = null;
try {
URL url = HeadlessBasics.class.getResource("/mysql_cluster.png");
File f = new File(url.getFile());
i = ImageIO.read(f);
} catch (Exception z) {
z.printStackTrace(System.err);
}
final Image im = i;
// Print system is available.
// 这段代码演示了如何打印已经准备好的画布,你可以使用paint方法自定义打印机的的默认画面
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(new Printable() {
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (pageIndex > 0) {
return Printable.NO_SUCH_PAGE;
}
((Graphics2D) g).translate(pf.getImageableX(),
pf.getImageableY());
// Paint the canvas.
c.paint(g);
// Paint the image.
if (im != null) {
g.drawImage(im, 32, 32, 64, 64, null);
}
return Printable.PAGE_EXISTS;
}
});
try {
pj.print();
} catch (Exception z) {
z.printStackTrace(System.err);
}
}
}运行实例,输出如下错误信息:
Headless mode: true java.awt.HeadlessException at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:207) at java.awt.Window.<init>(Window.java:536) at java.awt.Frame.<init>(Frame.java:420) at com.huangx.jdk.awt.HeadlessBasics.main(HeadlessBasics.java:38) Frame is created: false java.awt.HeadlessException at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:207) at java.awt.Button.<init>(Button.java:152) at com.huangx.jdk.awt.HeadlessBasics.main(HeadlessBasics.java:49) Button is created: false
此时会打开打印窗口,我使用PDF进行打印。打印数据存入 tmp.pdf 文件,效果如下图:

上图得知,我们的图片被打印到了PDF文件中。当然,也可直接打到纸上面,因为它调用了打印机。
Spring Boot 在 org.springframework.boot.SpringApplication 类的 ConfigurableApplicationContext run(String... args) 方法中调用 configureHeadlessProperty(); 方法去开启 headless 模式,代码如下:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty(); // 就是这里了
//...
return context;
}configureHeadlessProperty() 方法的源码如下:
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}其中, 常亮定义如下:
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
参考资料:
https://www.oschina.net/translate/using-headless-mode-in-java-se