Maven迁移Bazel异常:全局BUILD引用子模块时注解生成类找不到
我之前把Maven项目迁移到Bazel时,也碰到过一模一样的问题!单独编译子模块一切正常,但全局编译父模块时,死活找不到注解处理器生成的QSmsGatewaySettings类,折腾了好一阵才找到根源。
问题核心
Bazel默认会做未使用代码剔除优化:如果你的子模块appdirect_sms_gateway_settings_impl自身代码没有引用QSmsGatewaySettings(只有父模块需要它),Bazel会判定这个类是无用的,不会把它打包到子模块的输出jar里。而Maven没有这种优化逻辑,所以之前在Maven环境下不会出现这个问题。
另外,你通过依赖mysema_query(带exported_plugins)来应用注解处理器,生成的类属于appdirect_sms_gateway_settings_impl,但因为没有被子模块自身引用,就被Bazel过滤掉了。
可行解决方案
这里按推荐程度给你三个方案:
1. 强制保留未被引用的生成类(最简单)
在子模块的appdirect_sms_gateway_settings_impl配置中添加javacopts,告诉javac保留所有符号,避免Bazel剔除未被引用的生成类:
java_library( name = "appdirect_sms_gateway_settings_impl", srcs = glob(["src/main/java/**/*.java"]), javacopts = ["-XDkeepUnusedSymbols"], # 关键配置 deps = [ ":mysema_query", … ], )
这个选项会让javac保留所有生成的类,不管是否被当前模块引用,父模块依赖子模块时就能正常找到QSmsGatewaySettings了。
2. 在子模块中添加“标记类”引用生成类
如果不想修改编译选项,可以在子模块中写一个空的“标记类”,显式引用QSmsGatewaySettings,让Bazel认为这个类是有用的:
// 放在子模块src/main/java下任意包路径即可 public class QuerydslMarker { // 仅用于标记引用,无需实际调用 private static final Class<?> MARKER = QSmsGatewaySettings.class; }
这样Bazel就会把QSmsGatewaySettings打包到子模块的输出jar里。
3. 拆分生成类到独立模块(最规范)
如果项目规模较大,推荐把注解处理器生成的Querydsl类单独拆成一个java_library,让子模块和父模块都依赖这个独立模块:
# 子模块BUILD文件中新增独立的Querydsl模块 java_library( name = "sms_gateway_querydsl", srcs = glob(["src/main/java/**/*.java"]), # 这里放你的JPA实体类 plugins = [":mysema_query_plugin"], deps = [":mysema_query"], ) # 修改原有的业务模块配置 java_library( name = "appdirect_sms_gateway_settings_impl", srcs = glob(["src/main/java/**/*.java"]), deps = [":sms_gateway_querydsl", …], )
然后在父模块中同时依赖业务模块和Querydsl模块:
java_library( name = "appdirect-parent", srcs = glob(["*.java"]), deps = [ …, "appdirect-sms-gateway/appdirect-sms-gateway-settings-impl:appdirect_sms_gateway_settings_impl", "appdirect-sms-gateway/appdirect-sms-gateway-settings-impl:sms_gateway_querydsl" ], )
这种方式结构更清晰,从根源上避免了代码剔除的问题。
验证方法
你可以先单独编译子模块,然后检查输出jar里是否包含目标类:
jar tf bazel-bin/appdirect-sms-gateway/appdirect-sms-gateway-settings-impl/libappdirect_sms_gateway_settings_impl.jar | grep QSmsGatewaySettings
如果执行解决方案后能看到这个类,说明配置生效,再运行全局编译就没问题了。
内容的提问来源于stack exchange,提问作者Nicolas Zin

