You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala项目因aspectj.dtd错误与aopMerge策略构建失败排查

问题描述

Scala项目未做任何改动突然无法构建,怀疑是依赖更新导致,报错信息如下:

[warn] Merging 'META-INF/aop.xml' with strategy 'aopMerge'
[error] org.xml.sax.SAXParseExceptionpublicId: -//AspectJ//DTD//EN; systemId: http://www.eclipse.org/aspectj/dtd/aspectj.dtd; lineNumber: 1; columnNumber: 2; The markup declarations contained or pointed to by the document type declaration must be well-formed.

项目中自定义的aopMerge合并策略代码如下:

val aopMerge: MergeStrategy = new MergeStrategy {
  val name = "aopMerge"
  import scala.xml._
  import scala.xml.dtd._

  def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] = {
    val dt                         = DocType("aspectj", PublicID("-//AspectJ//DTD//EN", "https://www.eclipse.org/aspectj/dtd/aspectj.dtd"), Nil)
    val file                       = MergeStrategy.createMergeTarget(tempDir, path)
    val xmls: Seq[Elem]            = files.map(XML.loadFile)
    val aspectsChildren: Seq[Node] = xmls.flatMap(_ \ "aspectj" \ "aspects" \ "_")
    val weaverChildren: Seq[Node]  = xmls.flatMap(_ \ "aspectj" \ "weaver" \ "_")
    val options: String            = xmls.map(x => (x \ "aspectj" \ "weaver" \ "@options").text).mkString(" ").trim
    val weaverAttr                 = if (options.isEmpty) Null else new UnprefixedAttribute("options", options, Null)
    val aspects                    = new Elem(null, "aspects", Null, TopScope, false, aspectsChildren: _*)
    val weaver                     = new Elem(null, "weaver", weaverAttr, TopScope, false, weaverChildren: _*)
    val aspectj                    = new Elem(null, "aspectj", Null, TopScope, false, aspects, weaver)
    XML.save(file.toString, aspectj, "UTF-8", xmlDecl = false, dt)
    IO.append(file, IO.Newline.getBytes(IO.defaultCharset))
    Right(Seq(file -> path))
  }
}

已尝试将DTD地址从http改为https,执行sbt reload后仍报错,换回http也无效,需排查原因并解决。

排查与解决方法

1. 验证外部DTD可用性

直接访问https://www.eclipse.org/aspectj/dtd/aspectj.dtd,检查返回内容是否为合法的DTD格式。若返回404、跳转或非DTD内容,说明外部DTD源不可用,需改用本地DTD。

2. 切换为本地DTD文件

  1. 下载AspectJ的DTD文件,保存到项目资源目录(如src/main/resources/META-INF/aspectj.dtd)
  2. 修改合并策略中的DocType配置:
val dt = DocType("aspectj", PublicID("-//AspectJ//DTD//EN", "/META-INF/aspectj.dtd"), Nil)

此配置让XML解析从本地加载DTD,避免网络依赖问题。

3. 禁用DTD验证

若无需严格的DTD校验,可修改XML加载逻辑跳过验证:

import scala.xml.parsing.NoBindingFactoryAdapter
import org.xml.sax.InputSource
import java.io.FileReader

// 修改XML加载部分,禁用外部DTD加载与验证
val parser = new NoBindingFactoryAdapter() {
  override def parser(): org.xml.sax.XMLReader = {
    val p = super.parser()
    p.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
    p.setFeature("http://xml.org/sax/features/validation", false)
    p
  }
}

val xmls: Seq[Elem] = files.map { file =>
  parser.load(new InputSource(new FileReader(file)))
}

同时可移除保存时的DocType参数,避免触发验证:

XML.save(file.toString, aspectj, "UTF-8", xmlDecl = false)

4. 排查依赖变更

  • 执行sbt dependencyTree生成依赖树,对比历史稳定版本,找出最近更新的AspectJ相关依赖或包含aop.xml的依赖
  • 将可疑依赖版本锁定到之前可正常构建的版本,验证是否解决问题

5. 检查合并后的XML格式

在合并策略中添加临时输出,查看生成的aop.xml内容是否存在格式错误:

// 在XML.save之后添加
println("Merged aop.xml content:")
println(scala.io.Source.fromFile(file).mkString)

运行构建后检查输出的XML,确认是否存在标签未闭合、属性冲突等问题。


内容的提问来源于stack exchange,提问作者Arnold Daniels

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 10:35:24