乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 介绍与核心概念

    • Maven是什么
    • 约定优于配置
  • 第2章 安装与配置

    • 安装与验证
    • settings.xml
    • 本地仓库与镜像
  • 第3章 POM与项目坐标

    • POM
    • GAV坐标
    • packaging
  • 第4章 标准目录布局

    • 标准目录布局
  • 第5章 依赖机制

    • dependencies
    • scope
    • 依赖传递
    • 依赖冲突与调解
    • exclusions
    • optional
    • dependencyManagement
  • 第6章 仓库

    • 仓库体系
    • 本地仓库
    • 远程仓库与镜像
    • 私服
  • 第7章 构建生命周期

    • 生命周期概述
    • clean 生命周期
    • default 生命周期
    • site 生命周期
    • 生命周期与插件绑定
  • 第8章 插件

    • 插件概述
    • maven-compiler-plugin
    • maven-surefire-plugin
    • maven-war-plugin
  • 第9章 继承与聚合

    • parent继承
    • 聚合
    • BOM
    • properties
  • 第10章 属性与资源过滤

    • 资源过滤
    • Profile
  • 第11章 常用命令

    • mvn compile
    • mvn test
    • mvn package
    • mvn clean
    • mvn install
    • mvn dependency:tree
  • 第12章 常见问题与最佳实践

    • 依赖冲突排查
    • 最佳实践

maven-compiler-plugin

本章是"插件"章节的第一个具体插件讲解。maven-compiler-plugin 是 Maven 项目中最常用、最基础的插件——它决定了你的代码用什么版本的 Java 编译、生成什么版本的字节码。理解它的配置方式,是避免"本地能跑,线上报错"类问题的关键。


核心机制

虽然描述很简短,但这个插件背后有两个关键决策点:

  1. source:你的源代码使用哪个 Java 版本的语言特性(如 Java 8 的 Lambda、Java 16 的 record)
  2. target:编译生成的 .class 文件兼容哪个 JVM 版本运行

这两个参数如果不一致,会导致编译通过但运行时崩溃的诡异问题。

source 与 target 的区别

参数含义示例
source源代码允许的 Java 语言级别source=17 允许使用 var、switch 表达式
target生成字节码的 JVM 兼容级别target=8 表示 .class 文件可在 Java 8 JVM 上运行

关键规则:source 可以高于 target,但不推荐。例如 source=11、target=8 在语法上允许,但如果代码里用了 Java 11 的特性(如 String.isBlank()),生成的字节码在 Java 8 JVM 上运行时会抛出 NoSuchMethodError。

更安全的做法是两者保持一致,或者使用 release 参数(Java 9+ 推荐):

<configuration>
    <release>17</release>
</configuration>

release 同时约束了 source 和 target,并且会检查是否使用了该版本标准库中不存在的 API。

encoding 配置:被忽视的乱码元凶

如果不在 pom.xml 中显式声明编码,Maven 会使用平台默认编码(Windows 上通常是 GBK,Linux/macOS 上通常是 UTF-8)。这意味着:

  • 小崔在 Windows 上编译通过的代码,黄俪在 Mac 上编译可能报错"非法字符"
  • 源代码中的中文注释或字符串字面量,在不同操作系统下可能被错误解析

最佳实践:所有 Maven 项目都应显式声明 UTF-8 编码。

两种配置方式对比

Maven 提供了两种配置编译器参数的方式:

方式写法适用场景
Properties 方式在 <properties> 中定义 maven.compiler.source 等简单项目,配置少
Plugin 显式配置在 <plugins> 中完整声明插件复杂项目,需要更多编译选项

生活类比:翻译官与目标读者

想象 maven-compiler-plugin 是一位翻译官:

  • source = 原文语言。source=17 表示"原文是当代中文,可以用网络流行语"。
  • target = 译文语言。target=8 表示"请把文章翻译成 1995 年的中文,不要用那时候还没有的网络流行语"。
  • encoding = 字符集。如果翻译官用 GBK 读你的 UTF-8 手稿,所有emoji都会变成乱码。

如果你让翻译官把一篇用了"绝绝子"的文章翻译成 1995 年的中文,他要么报错"这个词当时不存在"(release 模式),要么强行翻译但读者看完一脸懵(运行时崩溃)。


图示

上图展示了编译器插件的工作流程:源代码 + 编码声明 → 插件按 source/target 参数编译 → 生成对应版本的字节码。三个参数缺一不可,任何一个配置错误都会导致编译失败或运行时异常。


完整示例

场景

飞翔科技的 payroll-service(薪资服务)项目最初用 Java 8 开发,现在 CTO 大翔决定升级到 Java 17。后端小崔负责修改编译配置,确保所有开发环境(Windows、Linux、Mac)编译结果一致。

操作前:Java 8 配置

当前 pom.xml 使用 properties 方式配置 Java 8:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.feixiang</groupId>
    <artifactId>payroll-service</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

小崔写了一个使用 Java 17 record 的类:

package com.feixiang.payroll;

public record Employee(String name, double salary) {}

在当前配置下编译:

$ mvn clean compile
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR
[INFO] -------------------------------------------------------------
[ERROR] /src/main/java/com/feixiang/payroll/Employee.java:[3,8] 错误: 记录是 Java 16 中的预览功能, 默认情况下禁用

原因:source=1.8 不允许使用 record 语法。

操作步骤:升级到 Java 17(Plugin 显式配置)

白歌建议改用 plugin 显式配置,以便后续添加更多编译选项:

<properties>
    <java.version>17</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
                <encoding>${project.build.sourceEncoding}</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

操作结果:编译前后字节码版本对比

再次编译:

$ mvn clean compile
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ payroll-service ---
[INFO] Compiling 5 source files to target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

查看生成的字节码版本:

$ javap -verbose target/classes/com/feixiang/payroll/Employee.class | grep "major version"
  major version: 61

版本对照表:

Java 版本字节码 major version
Java 852
Java 1155
Java 1761

变化分析:

  • 小崔的 Employee.java 现在可以正常编译,因为 source=17 支持 record
  • 生成的 .class 文件 major version 为 61,对应 Java 17,只能在 Java 17+ 的 JVM 上运行
  • 黄俪在 Mac 上执行同样的命令,结果完全一致,因为 encoding=UTF-8 消除了平台差异
  • 李眉在部署时发现,如果生产环境还是 Java 8,启动时会报错 Unsupported class file major version 61,这促使运维团队同步升级 JDK

易错点与常见问题

误区一:source 和 target 写反了,或者只写一个

错误配置:

<configuration>
    <target>17</target>
</configuration>

后果:source 默认继承自 target,但某些旧版本插件中两者可能不同步。更危险的是,如果 source=8、target=17,代码里用了 Java 17 的 var,编译器在 source=8 模式下会直接报错。最佳实践是两者始终一致,或者直接用 release 参数。

误区二:用 properties 方式配置了,又用 plugin 方式重复配置

错误配置:

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>17</source>
                <target>17</target>
            </configuration>
        </plugin>
    </plugins>
</build>

后果:plugin 显式配置的优先级高于 properties。最终生效的是 17,而不是 11。这种"双重配置"会让团队成员困惑,不知道哪个值是真的。建议统一使用一种方式:简单项目用 properties,复杂项目用 plugin。

误区三:升级了 JDK,但没改 compiler 插件配置

错误认知:"我电脑上装了 Java 17,所以 Maven 会自动用 Java 17 编译。"

纠正:Maven 不会自动检测你安装了哪个 JDK。它只认 pom.xml 里的配置。如果你的 pom.xml 还是 source=1.8,即使本地 JDK 是 17,编译器也会按 Java 8 的规则编译。要利用新 JDK 的语言特性,必须显式修改 source/target 或 release 参数。


小结

maven-compiler-plugin 是 Maven 项目的基石插件,它通过 source、target(或 release)和 encoding 三个核心参数,决定了源代码的编译规则。Properties 方式适合简单场景,Plugin 显式配置适合需要精细控制的场景。无论哪种方式,显式声明版本和编码都是生产环境的最佳实践,它能避免"本地能跑、线上报错"的跨平台编译问题。

本章与全局的关系:本章讲解了最基础的编译插件。下一节将讲解 maven-surefire-plugin,它负责在编译之后运行测试,是保障代码质量的第一道防线。

上一页
插件概述
下一页
maven-surefire-plugin