首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JFreeChart 實戰:Servlet 產生圖表並返回圖片 URL 完整範例

     編輯:Java 相關知識

在 Web 專案中動態生成統計圖表(折線圖、長條圖、餅圖等)並嵌入 JSP/前端頁面,是 Java 後端常見需求。JFreeChart 作為老牌開源圖表庫,配合內建 DisplayChart Servlet,能輕鬆實現「後端生成圖片 → 暫存 → 前端以 URL 取圖」的流程。本文提供完整的 web.xml 配置、Java 服務層代碼、JSP 調用範例,並針對中文顯示、坐標軸優化、虛線樣式等實戰細節詳細註解。

一、環境依賴

<dependency>
    <groupId>org.jfree</groupId>
    <artifactId>jfreechart</artifactId>
    <version>1.5.4</version> <!-- 或更新版 -->
</dependency>
<dependency>
    <groupId>org.jfree</groupId>
    <artifactId>jcommon</artifactId>
    <version>1.0.24</version>
</dependency>

二、web.xml 註冊 DisplayChart Servlet

JFreeChart 內建 org.jfree.chart.servlet.DisplayChart 用於從 Session/暫存目錄讀取圖片流輸出給瀏覽器。

<servlet>
    <servlet-name>DisplayChart</servlet-name>
    <servlet-class>org.jfree.chart.servlet.DisplayChart</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DisplayChart</servlet-name>
    <url-pattern>/DisplayChart</url-pattern>
</servlet-mapping>

三、核心服務類:生成折線圖並返回 URL

package com.example.chart;

import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.servlet.ServletUtilities;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;

import java.awt.*;
import java.io.IOException;
import java.util.Map;

public class ChartService {

    /**
     * 生成多系列折線圖,返回前端可直接用的圖片 URL
     *
     * @param seriesNames 系列名稱陣列 (圖例)
     * @param scores      二維數據 [seriesIndex][categoryIndex]
     * @param categories  類別標籤 (X 軸)
     * @param title       圖表標題
     * @return 圖片訪問路徑,如 "/myapp/DisplayChart?filename=chart_12345.png"
     */
    public String generateLineChartUrl(String[] seriesNames, double[][] scores,
                                       String[] categories, String title) {
        // 1. 數據集準備
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        for (int s = 0; s < seriesNames.length; s++) {
            for (int c = 0; c < categories.length; c++) {
                // 過長類別名截斷,避免 X 軸擁擠
                String cat = categories[c].length() > 8 ? categories[c].substring(0, 8) : categories[c];
                dataset.addValue(scores[s][c], seriesNames[s], cat);
            }
        }

        // 2. 建立圖表
        JFreeChart chart = ChartFactory.createLineChart(
                title, null, null, dataset,
                PlotOrientation.VERTICAL, true, true, false);

        // 3. 全局樣式
        chart.setBackgroundPaint(Color.WHITE);
        chart.getLegend().setPosition(RectangleEdge.RIGHT);
        chart.getLegend().setItemFont(new Font("微軟正黑體", Font.BOLD, 12));
        chart.setTitle(new TextTitle(title, new Font("微軟正黑體", Font.BOLD, 16)));

        // 4. 繪圖區深度客製化
        CategoryPlot plot = chart.getCategoryPlot();
        plot.setBackgroundPaint(Color.WHITE);
        plot.setBackgroundAlpha(0.8f);
        plot.setDomainGridlinesVisible(true);
        plot.setRangeGridlinesVisible(true);
        plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
        plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
        plot.setAxisOffset(new RectangleInsets(0, 0, 0, 10)); // 圖表與 Y 軸留白
        plot.setNoDataMessage("無資料可顯示");

        // 5. X 軸 (CategoryAxis) 設定
        CategoryAxis domainAxis = plot.getDomainAxis();
        domainAxis.setTickLabelFont(new Font("微軟正黑體", Font.BOLD, 12));
        domainAxis.setLabelFont(new Font("微軟正黑體", Font.BOLD, 14));
        domainAxis.setMaximumCategoryLabelLines(2); // 允許換行
        domainAxis.setCategoryMargin(0.2); // 類別間距

        // 6. Y 軸 (NumberAxis) 設定
        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        rangeAxis.setAutoRangeIncludesZero(true);
        rangeAxis.setUpperMargin(0.15); // 頂部留白 15%
        rangeAxis.setTickLabelFont(new Font("微軟正黑體", Font.BOLD, 12));
        rangeAxis.setLabelFont(new Font("微軟正黑體", Font.BOLD, 14));

        // 7. 系列渲染器:資料點、線型、標籤
        LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
        renderer.setShapesVisible(true); // 顯示資料點形狀
        renderer.setBaseItemLabelsVisible(true);
        renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
        renderer.setBaseItemLabelFont(new Font("微軟正黑體", Font.PLAIN, 11));

        // 系列 0:實線,寬 2px
        renderer.setSeriesStroke(0, new BasicStroke(2.0f));
        // 系列 1:虛線 (10px 實, 6px 空)
        if (seriesNames.length > 1) {
            renderer.setSeriesStroke(1, new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_MITER, 1.0f, new float[]{10f, 6f}, 0.0f));
        }
        // 系列 2:點虛線 (6px 實, 6px 空)
        if (seriesNames.length > 2) {
            renderer.setSeriesStroke(2, new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_MITER, 1.0f, new float[]{6f, 6f}, 0.0f));
        }

        // 8. 輸出 PNG 並取得暫存檔名
        String fileName = null;
        try {
            // 寬 700px, 高 300px
            fileName = ServletUtilities.saveChartAsPNG(chart, 700, 300, null, null);
        } catch (IOException e) {
            throw new RuntimeException("圖表生成失敗", e);
        }

        // 9. 組裝 URL (專案上下文路徑需根據實際部署調整)
        // 假設專案 context-path 為 /myapp
        return "/myapp/DisplayChart?filename=" + fileName;
    }
}

四、Controller / JSP 調用範例

// Spring MVC Controller 範例
@GetMapping("/chart/score")
public String showChart(Model model) {
    String[] series = {"班級A", "班級B"};
    double[][] data = {{85, 90, 78, 92}, {80, 88, 82, 89}};
    String[] categories = {"國文", "英文", "數學", "自然"};
    
    String chartUrl = chartService.generateLineChartUrl(series, data, categories, "期中考成績趨勢");
    model.addAttribute("chartUrl", chartUrl);
    return "chart/view"; // 對應 view.jsp
}
<!-- view.jsp -->
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<body>
    <h2>成績趨勢圖</h2>
    <img src="<%= request.getContextPath() %><%= chartUrl %>" alt="成績圖表" />
</body>
</html>

五、關鍵實戰技巧總結

需求實現方式
中文顯示 (無亂碼)指定支援中文字體:new Font("微軟正黑體", Font.BOLD, 12) (Windows) 或 Noto Sans CJK (Linux/Docker 需安裝字體包)
X 軸標籤傾斜/換行domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45) 或 setMaximumCategoryLabelLines(2)
Y 軸範圍固定/自動rangeAxis.setRange(0, 100) 固定;setAutoRangeIncludesZero(true) 自動含零
虛線/點線區分系列BasicStroke(float[] dashPattern) 陣列定義「畫-空」長度
資料點顯示數值renderer.setBaseItemLabelsVisible(true) + StandardCategoryItemLabelGenerator
圖片暫存清理DisplayChart Servlet 預設用 Session 綁定,Session 失效自動清理;或配置定時任務掃描暫存目錄

六、常見坑點

  • Linux 伺服器中文方塊字:容器/雲主機缺中文字體 → 安裝 fontconfig + google-noto-sans-cjk-ttc-fonts 或掛載字體卷。
  • 高併發下暫存檔衝突:ServletUtilities.saveChartAsPNG 使用 UUID 命名,極低衝突機率;極高併發建議改寫輸出至 ByteArrayOutputStream 直接回傳 byte[] 或 Base64,避免磁碟 I/O。
  • 記憶體洩漏:大量生成圖表未釋放 ChartRenderingInfo、BufferedImage,建議使用 try-with-resources 或手動 flush()。

結語

JFreeChart 雖 API 略顯老舊,但功能極其完整,配合 DisplayChart Servlet 即可快速構建「後端生成、前端引用」的圖表服務。掌握 CategoryPlot、Renderer、Axis 三大核心客製化入口,即能應對 90% 以上的業務圖表需求。若專案追求現代化、前端渲染(ECharts/Chart.js),亦可改為後端僅提供 JSON 數據,前端負責繪製,架構更清晰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved