跳至主要內容

Spring Boot + 虚拟线程实现的二维码生成器

DD编辑部原创Spring BootSpring Boot大约 3 分钟

Spring Boot + 虚拟线程实现的二维码生成器

随着 Java 21 的发布,虚拟线程(Virtual Threads)成为了正式特性,彻底改变了 Java 的并发编程方式。它们为传统线程提供了更轻量、更易扩展的替代方案,让开发者能够编写出更加简洁高效的并发代码。

虚拟线程

虚拟线程为异步 Java 开发带来了革命性的变化。它们让 JVM 拥有了类似 Go 的并发能力,使高吞吐量应用的代码结构更加简洁、易于维护。如果你正在开发 Web 服务、文件处理器或任何 I/O 密集型应用,虚拟线程会成为你的得力助手。

虚拟线程是由 Java 虚拟机(JVM)直接管理的轻量级线程,而不是由操作系统管理。与平台线程(也称为内核线程或本地线程)相比,平台线程的创建和管理成本较高,而虚拟线程则极为廉价,可以轻松支持成千上万甚至上百万的并发线程。

虚拟线程依然基于熟悉的 java.lang.Thread API,这意味着你无需学习新的并发模型或库。

传统线程与操作系统线程是一一对应的:

  • 每个 Java 线程对应一个操作系统线程
  • 阻塞线程(如 I/O 操作)会占用操作系统资源

而虚拟线程采用多对一模型,允许大量虚拟线程共享少量操作系统线程。当虚拟线程遇到阻塞操作(如 sleep()read())时,JVM 会自动挂起该线程,让底层的承载线程去执行其他任务。

虚拟线程项目示例

下面的代码演示了如何配置 Tomcat(Spring Boot 默认的 Web 服务器)使用虚拟线程来处理请求任务,替代传统的平台线程。

@SpringBoojtApplication
@Configuration
public class DemoApplication {

 public static void main(String[] args) {
  SpringApplication.run(DemoApplication.class, args);
 }

 @Bean
 public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
  return protocolHandler -> {
   protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
  };
 }
}

创建生成二维码的接口:

@RestController
@RequestMapping(path = "/api/qr")
public class QrController {

    private final QRCodeWriter qrWriter = new QRCodeWriter();
    private final Map<String, byte[]> store = new ConcurrentHashMap<>();


    @GetMapping("/generate")
    public ResponseEntity<String> generate(@RequestParam("text") String text,
           @RequestParam(value = "size", defaultValue = "250") int size) {

        try {
            byte[] pngBytes = encodeToPng(text, size, size);

            System.out.println(">>> in virtual thread: " + Thread.currentThread());

            String id = UUID.randomUUID().toString();
            store.put(id, pngBytes);

            return ResponseEntity.ok(id);
        } catch (Exception e) {
            return ResponseEntity.internalServerError().body("Error: " + e.getMessage());
        }
    }

  
    @GetMapping("/get/{id}")
    public ResponseEntity<byte[]> getQRCode(@PathVariable String id) {
        byte[] png = store.get(id);
        if (png == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok()
                .contentType(MediaType.IMAGE_PNG)
                .body(png);
    }


    private byte[] encodeToPng(String text, int width, int height) throws Exception {
        BitMatrix matrix = qrWriter.encode(text, BarcodeFormat.QR_CODE, width, height);
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            MatrixToImageWriter.writeToStream(matrix, "PNG", baos);
            return baos.toByteArray();
        }
    }
}

创建交互界面:

<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="UTF-8" />
  <title>Gerador de QR Code</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      margin-top: 50px;
    }
    input, button {
      font-size: 1rem;
      padding: 8px;
      margin: 5px;
    }
    img {
      margin-top: 20px;
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <h1> QR Code Generator</h1>
  <p>Write a text and click in the button to show a QR Code.</p>

  <div>
    <input type="text" id="textInput" placeholder="Texto para QR Code" size="40" />
    <input type="number" id="sizeInput" value="250" min="50" max="1000" step="50" />
    <button id="generateBtn">Generate</button>
  </div>

  <div id="qrcodeContainer">
   
  </div>

  <script>
    const generateBtn = document.getElementById('generateBtn');
    const textInput = document.getElementById('textInput');
    const sizeInput = document.getElementById('sizeInput');
    const qrcodeContainer = document.getElementById('qrcodeContainer');

    generateBtn.addEventListener('click', () => {
      const text = encodeURIComponent(textInput.value.trim());
      const size = parseInt(sizeInput.value) || 250;

      if (!text) {
        alert('Please write text to generate QR Code.');
        return;
      }

fetch(`http://localhost:8080/api/qr/generate?text=${text}&size=${size}`)
        .then(response => {
          if (!response.ok) {
            throw new Error('Error on generate QR CODE');
          }
          return response.text(); 
        })
        .then(id => {
          const imgUrl = `/api/qr/get/${id}`;

          qrcodeContainer.innerHTML = `
            <p>ID QR ID: ${id}</p>
            <img src="${imgUrl}" alt="QR Code" />
          `;
        })
        .catch(err => {
          console.error(err);
          alert('Error: ' + err.message);
        });
    });
  </script>
</body>
</html>

运行效果:

上次编辑于:
贡献者: didi