1. 概述
本文介绍 Aegis 数据绑定机制,这是一个能在 Java 对象与 XML Schema 描述的 XML 文档间建立映射的子系统。Aegis 在提供精细控制能力的同时,最大限度减少了开发工作量。
Aegis 是 Apache CXF 的组成部分,但不仅限于在该框架中使用。这个数据绑定机制可独立应用于任何场景,因此本文重点介绍其作为独立子系统的用法。
2. Maven 依赖
启用 Aegis 数据绑定仅需添加以下依赖:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-databinding-aegis</artifactId>
<version>3.1.8</version>
</dependency>
最新版本可在 Maven 仓库 查询。
3. 类型定义
本节通过三个示例类型说明 Aegis 的用法。
3.1. Course 类
这是最简单的示例类:
public class Course {
private int id;
private String name;
private String instructor;
private Date enrolmentDate;
// 标准 getter/setter
}
3.2. CourseRepo 接口
作为顶层类型,我们将其定义为接口(而非类)以展示 Aegis 处理接口的能力——这在 JAXB 中需要自定义适配器才能实现:
public interface CourseRepo {
String getGreeting();
void setGreeting(String greeting);
Map<Integer, Course> getCourses();
void setCourses(Map<Integer, Course> courses);
void addCourse(Course course);
}
注意 getCourses
方法返回 Map
类型。这体现了 Aegis 相对 JAXB 的优势:后者需要自定义适配器才能处理 Map,而前者原生支持。
3.3. CourseRepoImpl 实现类
public class CourseRepoImpl implements CourseRepo {
private String greeting;
private Map<Integer, Course> courses = new HashMap<>();
// 标准 getter/setter
@Override
public void addCourse(Course course) {
courses.put(course.getId(), course);
}
}
4. 自定义数据绑定
自定义配置需在类路径下放置 XML 映射文件。文件目录结构必须与对应 Java 类的包结构一致,例如:
- 类全限定名:
com.example.Course
- 映射文件路径:
com/example/Course.aegis.xml
映射文件命名规则:类名.aegis.xml
4.1. CourseRepo 映射配置
CourseRepo
接口位于 com.baeldung.cxf.aegis
包,因此映射文件为:
com/baeldung/cxf/aegis/CourseRepo.aegis.xml
配置内容如下:
<mappings xmlns:ns="http://courserepo.baeldung.com">
<mapping name="ns:Baeldung">
<property name="greeting" style="attribute"/>
</mapping>
</mappings>
此配置实现:
✅ 修改接口对应 XML 元素的名称和命名空间
✅ 将 greeting
属性转为 XML 属性(而非子元素)
4.2. Course 映射配置
Course
类的映射文件位于相同目录:com/baeldung/cxf/aegis/Course.aegis.xml
配置内容:
<mappings>
<mapping>
<property name="instructor" ignore="true"/>
</mapping>
</mappings>
此配置实现:
❌ 序列化时忽略 instructor
属性
⚠️ 从 XML 反序列化时该属性将丢失
更多自定义选项请参考 Aegis 官方文档。
5. 测试验证
本节通过测试用例演示 Aegis 数据绑定的完整流程。
测试类定义核心字段:
public class BaeldungTest {
private AegisContext context;
private String fileName = "baeldung.xml";
// 其他方法
}
5.1. 初始化 AegisContext
private void initializeContext() {
context = new AegisContext();
// 设置根类型
Set<Type> rootClasses = new HashSet<>();
rootClasses.add(CourseRepo.class);
context.setRootClasses(rootClasses);
// 指定接口实现类
Map<Class<?>, String> implMap = new HashMap<>();
implMap.put(CourseRepoImpl.class, "CourseRepo");
context.setBeanImplementationMap(implMap);
// 启用 xsi:type 属性
context.setWriteXsiTypes(true);
context.initialize();
}
关键配置说明:
- 根类型:Aegis 会为每个根类型生成 XML 映射元素
- 实现映射:为接口指定代理实现类
- 类型标记:在 XML 中保留
xsi:type
属性(除非被映射文件覆盖)
5.2. 测试数据准备
private CourseRepoImpl initCourseRepo() {
Course restCourse = new Course();
restCourse.setId(1);
restCourse.setName("REST with Spring");
restCourse.setInstructor("Eugen");
restCourse.setEnrolmentDate(new Date(1234567890000L));
Course securityCourse = new Course();
securityCourse.setId(2);
securityCourse.setName("Learn Spring Security");
securityCourse.setInstructor("Eugen");
securityCourse.setEnrolmentDate(new Date(1456789000000L));
CourseRepoImpl courseRepo = new CourseRepoImpl();
courseRepo.setGreeting("Welcome to Baeldung!");
courseRepo.addCourse(restCourse);
courseRepo.addCourse(securityCourse);
return courseRepo;
}
5.3. 对象与 XML 相互转换
序列化方法:
private void marshalCourseRepo(CourseRepo courseRepo) throws Exception {
AegisWriter<XMLStreamWriter> writer = context.createXMLStreamWriter();
AegisType aegisType = context.getTypeMapping().getType(CourseRepo.class);
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance()
.createXMLStreamWriter(new FileOutputStream(fileName));
writer.write(courseRepo,
new QName("http://aegis.cxf.baeldung.com", "baeldung"),
false, xmlWriter, aegisType);
xmlWriter.close();
}
反序列化方法:
private CourseRepo unmarshalCourseRepo() throws Exception {
AegisReader<XMLStreamReader> reader = context.createXMLStreamReader();
XMLStreamReader xmlReader = XMLInputFactory.newInstance()
.createXMLStreamReader(new FileInputStream(fileName));
CourseRepo courseRepo = (CourseRepo) reader.read(
xmlReader, context.getTypeMapping().getType(CourseRepo.class));
xmlReader.close();
return courseRepo;
}
5.4. 完整测试流程
@Test
public void whenMarshalingAndUnmarshalingCourseRepo_thenCorrect()
throws Exception {
initializeContext();
CourseRepo inputRepo = initCourseRepo();
marshalCourseRepo(inputRepo);
CourseRepo outputRepo = unmarshalCourseRepo();
Course restCourse = outputRepo.getCourses().get(1);
Course securityCourse = outputRepo.getCourses().get(2);
// 验证断言
assertEquals("Welcome to Baeldung!", outputRepo.getGreeting());
assertEquals("REST with Spring", restCourse.getName());
assertEquals(new Date(1234567890000L), restCourse.getEnrolmentDate());
assertNull(restCourse.getInstructor()); // 被映射文件忽略
assertEquals("Learn Spring Security", securityCourse.getName());
assertEquals(new Date(1456789000000L), securityCourse.getEnrolmentDate());
assertNull(securityCourse.getInstructor()); // 被映射文件忽略
}
验证要点:
✅ greeting
属性正确恢复
✅ Date
类型自动转换为 xsd:dateTime
❌ instructor
属性按预期被忽略
5.5. XML 输出对比
未自定义映射的 XML:
<ns1:baeldung xmlns:ns1="http://aegis.cxf.baeldung.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns1:CourseRepo">
<ns1:courses>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>1</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2009-02-14T06:31:30+07:00</ns1:enrolmentDate>
<ns1:id>1</ns1:id>
<ns1:instructor>Eugen</ns1:instructor> <!-- 未被忽略 -->
<ns1:name>REST with Spring</ns1:name>
</ns2:value>
</ns2:entry>
<!-- 其他课程数据 -->
</ns1:courses>
<ns1:greeting>Welcome to Baeldung!</ns1:greeting> <!-- 子元素形式 -->
</ns1:baeldung>
应用自定义映射后的 XML:
<ns1:baeldung xmlns:ns1="http://aegis.cxf.baeldung.com"
xmlns:ns="http://courserepo.baeldung.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns:Baeldung" greeting="Welcome to Baeldung!"> <!-- 属性形式 -->
<ns:courses>
<ns2:entry xmlns:ns2="urn:org.apache.cxf.aegis.types">
<ns2:key>1</ns2:key>
<ns2:value xsi:type="ns1:Course">
<ns1:enrolmentDate>2009-02-14T06:31:30+07:00</ns1:enrolmentDate>
<ns1:id>1</ns1:id>
<!-- instructor 属性被忽略 -->
<ns1:name>REST with Spring</ns1:name>
</ns2:value>
</ns2:entry>
<!-- 其他课程数据 -->
</ns:courses>
</ns1:baeldung>
关键变化对比:
| 特性 | 默认行为 | 自定义映射后 |
|---------------------|------------------------|---------------------------|
| 根元素类型 | ns1:CourseRepo
| ns:Baeldung
|
| greeting 属性 | XML 子元素 | XML 属性 |
| instructor 属性 | 包含在输出中 | 被忽略 |
💡 提示:测试运行后可在项目根目录找到生成的
baeldung.xml
文件。
6. 总结
本文演示了 Apache CXF Aegis 数据绑定作为独立子系统的使用方法,包括:
- Java 对象与 XML 的双向转换
- 通过映射文件自定义序列化行为
- 处理复杂类型(Map、接口、Date)的简单方案
完整示例代码请参考 GitHub 项目。