Maven多模块项目中Log4j2 Layout与自定义注解处理器冲突问题
核心现象
多模块Maven项目里,Module A是自定义注解处理器,Module B依赖A并通过META-INF/services/javax.annotation.processing.Processor配置了处理器类。用SLF4J+Log4j2做日志,原本控制台日志正常,给Kafka Appender加任意Layout(比如<JSONLayout>、<CSVLayout>)后,执行mvn clean install就报错:
[ERROR] Bad service configuration file, or exception thrown while constructing Processor object: javax.annotation.processing.Processor: Provider MyProcessor could not be instantiated
去掉Layout标签或者删掉处理器类上的@Slf4j注解就正常,但项目必须保留@Slf4j。
问题根源
注解处理器是在编译阶段运行的,此时Log4j2的某些Layout依赖的类可能没被正确加载。加上@Slf4j后,Lombok生成的日志代码会触发Log4j2初始化,而带Layout的Kafka Appender配置会导致初始化时出现类加载冲突——要么是Layout相关类不在编译期的类路径里,要么是类加载顺序乱了,导致处理器实例化失败。
解决方案
1. 限制注解处理器的日志依赖范围
把Module A中Log4j2核心、SLF4J绑定这些依赖的范围设为provided,因为注解处理器只在编译期跑,这些依赖由编译环境提供,避免打包时带入引发冲突:
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.20.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> <version>2.20.0</version> <scope>provided</scope> </dependency>
2. 给注解处理器单独配极简日志
在Module A的src/main/resources下新建log4j2-processor.xml,专门给编译期的注解处理器用,避开复杂的Kafka Appender和Layout:
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
然后在Module A的Maven编译插件里指定这个配置文件,让编译期用单独的日志配置:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> </path> <!-- 这里加其他注解处理器的依赖路径 --> </annotationProcessorPaths> <compilerArgs> <arg>-Dlog4j.configurationFile=log4j2-processor.xml</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>
3. 分离编译期与运行期的日志配置
复杂的Kafka+JSONLayout这类配置只放在运行期的模块(比如Module B)里,注解处理器的日志需求简单,就用基础的控制台输出,彻底避免编译期加载不必要的Layout类。
4. 清理依赖树
用mvn dependency:tree查看Module A的依赖,排除掉不需要的Log4j2扩展模块,确保编译期类路径干净,没有冗余依赖干扰类加载。
内容的提问来源于stack exchange,提问作者swing1234

