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