【发布时间】:2022-01-24 17:24:53
【问题描述】:
当我在包含 pom.xml 文件的文件夹上运行 mvn spring-boot:run 时,应用程序启动并将 POJO 正确序列化为 XML,但是当我通过转到目标文件夹并使用 @987654330 启动它时@ 在 jar 文件中我得到 javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath 由 .java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory 引起。
在我的 Maven 中,我有以下 JAXB 依赖项:
<!-- JAXB API -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<!-- JAXB Runtime -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.5</version>
<scope>runtime</scope>
</dependency>
以下是将 POJO 序列化为 XML 的代码:
private static final Pattern REMOVE_HEADER = Pattern.compile("\\<\\?xml(.+?)\\?\\>");
public static <T> String toXML(final T data) {
try {
final var jaxbMarshaller = JAXBContext.newInstance(data.getClass()).createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
final var sw = new StringWriter();
jaxbMarshaller.marshal(data, sw);
return XMLUtils.REMOVE_HEADER.matcher(sw.toString()).replaceAll("").strip();
} catch (final JAXBException e) {
XMLUtils.LOGGER.error("Error while converting POJO to XML. ERROR: {}.", e.getMessage(), e);
}
return "";
}
这是我使用 java -jar 启动应用程序时的日志:
2021-12-26 21:19:14,526 [ForkJoinPool.commonPool-worker-11] ERROR com.enterprise.system.shared.util.XMLUtils - Error while converting POJO to XML. ERROR: Implementation of JAXB-API has not been found on module path or classpath..
javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
- with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.bind.v2.ContextFactory]
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:232)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:375)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:691)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:632)
at com.enterprise.system.shared.util.XMLUtils.toXML(XMLUtils.java:26)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:952)
at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:926)
at java.base/java.util.stream.AbstractTask.compute(AbstractTask.java:327)
at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Caused by: java.lang.ClassNotFoundException: com.sun.xml.bind.v2.ContextFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:92)
at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:125)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:230)
... 17 more
这是生成的带有 JAXB 依赖项的 spring-boot fat jar 的图像:
这是我的依赖树:
--- maven-dependency-plugin:3.2.0:tree (default-cli) @ java-eleven-jaxb-hell ---
com.enterprise.system:java-eleven-jaxb-hell:jar:0.0.1-SNAPSHOT
+- org.slf4j:slf4j-log4j12:jar:1.7.32:compile (optional)
| +- org.slf4j:slf4j-api:jar:1.7.32:compile (optional)
| \- log4j:log4j:jar:1.2.17:compile (optional)
+- org.springframework.boot:spring-boot-autoconfigure:jar:2.6.2:compile
| \- org.springframework.boot:spring-boot:jar:2.6.2:compile
| +- org.springframework:spring-core:jar:5.3.14:compile
| | \- org.springframework:spring-jcl:jar:5.3.14:compile
| \- org.springframework:spring-context:jar:5.3.14:compile
| +- org.springframework:spring-aop:jar:5.3.14:compile
| +- org.springframework:spring-beans:jar:5.3.14:compile
| \- org.springframework:spring-expression:jar:5.3.14:compile
+- org.projectlombok:lombok:jar:1.18.22:provided
+- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:compile
| \- jakarta.activation:jakarta.activation-api:jar:1.2.2:compile
\- com.sun.xml.bind:jaxb-impl:jar:2.3.5:runtime
\- com.sun.activation:jakarta.activation:jar:1.2.2:runtime
最后,这是我的模型类:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@XmlRootElement(name = "finans")
@XmlAccessorType(XmlAccessType.FIELD)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FinanTokenDTO {
@XmlAttribute
private String pln;
@XmlAttribute
private String ope;
@XmlAttribute
private String mod;
@XmlAttribute
private String mis;
@XmlAttribute
private String val;
@XmlAttribute
private String car;
@XmlAttribute
private String dti;
@XmlAttribute
private String dtf;
@XmlAttribute
private String ota;
@XmlElement
private FinanDTO finan;
}
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@XmlRootElement(name = "finan")
@XmlAccessorType(XmlAccessType.FIELD)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FinanDTO {
@XmlAttribute
private String prd;
@XmlAttribute
private String pkg;
@XmlAttribute
private String val;
@XmlAttribute
private String fty;
}
我正在使用 java 11,我知道在 java 11 中 JAXB 已从 SE JDK 中删除,因为它被视为 EE 功能。
由于 docker 映像的大小和使用 docker 容器的安全相关问题,我无法在生产环境中使用 mvn spring-boot:run 执行它。
既然 Spring Boot 生成了一个 fat jar,那么应用程序是否应该以 java -jar 运行应用到 spring boot fat jar 生成的文件,就像它在 pom.xml 文件夹中使用 mvn spring-boot:run 一样?
编辑:
经过大量挖掘和测试,我发现当我们尝试使用parallel stream 而不是 POJO 列表中的stream 来编组几个有效的 POJO 时,就会出现问题。我将代码上传到java-eleven-jaxb-hell 以便更好地理解,不幸的是,由于性能问题,我无法将parallelStream 更改为stream。请记住,要发生问题,您必须针对 spring-boot 在目标文件夹中生成的 fat jar 运行 java -jar。
【问题讨论】:
-
更好的问题是:“如何使用 jaxb 和 spring[-boot] 将 xml 转换为字符串?” ;)
-
@xerx593 刚刚编辑了标题。
-
please have a look at this,同时还更好地描述您的输入/模型/xml:xsd 或类..或从头开始..模型有多大/(xml)文件有多大? (当 xmls 太大/无法映射时,jaxb 也不好))
-
@xerx593 我刚刚插入了我的 POJO。虽然我认为没有实现问题,因为当我运行
mvn spring-boot:run时代码正确执行。问题是当我使用 java -jar 对 spring-boot 生成的 fat jar 运行应用程序时。 -
@xerx593 我运行了一个测试并将我的 POJO 列表切换为针对流而不是并行流进行编组,并且它有效。有什么想法吗?
标签: spring spring-boot jaxb java-11