# bean-transfer **Repository Path**: jjjxxx/bean-transfer ## Basic Information - **Project Name**: bean-transfer - **Description**: 基于配置文件实现属性复制,基于SPEL,支持复杂表达式。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-11-19 - **Last Updated**: 2026-02-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Bean Transfer 完整使用指南 ## 目录 - [一、项目介绍](#一项目介绍) - [二、核心特性](#二核心特性) - [三、项目架构](#三项目架构) - [四、快速开始](#四快速开始) - [五、配置说明](#五配置说明) - [六、使用指南](#六使用指南) - [七、实战案例](#七实战案例) - [八、最佳实践](#八最佳实践) - [九、高级功能](#九高级功能) - [十、常见问题](#十常见问题) --- ## 一、项目介绍 ### 1.1 项目概述 Bean Transfer 是一个基于 XML 配置的对象到对象映射工具,专为复杂报文转换场景设计。该框架通过声明式的 XML 配置实现对象映射,无需修改实体类即可完成复杂的转换逻辑。 **适用场景:** - 报文转换(如 HTTP 请求/响应转换) - 异构系统之间的数据对接 - 需要频繁变化的字段映射关系 - 跨层级、嵌套对象的复杂映射 **不适用场景:** - 简单的 Bean 属性复制(推荐使用 BeanUtils、MapStruct 等工具) - 性能敏感的场景(反射机制有一定性能开销) ### 1.2 项目信息 - **项目名称**:bean-transfer - **组织**:com.hongri - **当前版本**:2.0.2-SNAPSHOT - **Java 版本**:JDK 8+ - **Spring Boot 版本**:2.7.18 - **开源协议**:需查看 LICENSE 文件 --- ## 二、核心特性 ### 2.1 主要功能 ✅ **XML 配置驱动**:通过 XML 文件定义映射规则,零侵入实体类 ✅ **复杂的路径解析**:支持 SpEL 表达式、多层嵌套路径访问 ✅ **上下文变量**:支持 `current` 和 `_` 上下文对象,实现跨层级访问 ✅ **灵活的值处理**:支持固定值、变量引用、SpEL 表达式 ✅ **自定义转换器**:提供扩展点,支持自定义转换逻辑 ✅ **Spring 集成**:提供 Spring Boot Starter,开箱即用 ✅ **类型安全**:支持类型转换和验证 ✅ **集合映射**:支持数组、List、Set 等集合类型的映射 ### 2.2 技术优势 | 特性 | 说明 | |------|------| | **零侵入** | 实体类无需添加任何注解或继承 | | **配置集中** | 所有映射规则集中在 XML 文件中,便于维护 | | **灵活扩展** | 支持自定义转换器、自定义函数 | | **性能优化** | 使用 ReflectASM 优化反射性能 | | **Spring 原生** | 深度集成 Spring 生态 | | **类型安全** | 提供类型转换器机制 | --- ## 三、项目架构 ### 3.1 模块结构 ``` bean-transfer (父工程) ├── bean-transfer-core # 核心模块 │ ├── converter/ # 类型转换器 │ ├── core/ # 核心实现类 │ ├── definition/ # 映射定义 │ ├── property/ # 属性处理 │ ├── schema/ # XML Schema │ └── util/ # 工具类 ├── bean-transfer-spring # Spring 集成模块 │ └── spring/ # Spring 适配器 ├── bean-transfer-starter # Spring Boot Starter │ └── spring/config/ # 自动配置 └── bean-transfer-config-generator # 配置生成器 └── generator/ # 配置生成工具 ``` ### 3.2 核心类说明 | 类名 | 说明 | |------|------| | `ObjectTransfer` | 对象转换接口 | | `ConfigurationObjectTransfer` | 基于配置的对象转换实现 | | `MappingConfiguration` | 映射配置实体 | | `BeanCopierConfiguration` | 全局配置 | | `XmlMappingConfigurationParser` | XML 配置解析器 | | `ConfigurationObjectTransferBuilder` | 转换器构建器 | ### 3.3 依赖关系图 ``` 应用层 ↓ bean-transfer-starter ↓ bean-transfer-spring ↓ bean-transfer-core ↓ dom4j, jackson, reflectasm, spring-context ``` --- ## 四、快速开始 ### 4.1 Maven 依赖 #### Spring Boot 项目(推荐) ```xml com.hongri bean-transfer-starter 2.0.2-SNAPSHOT ``` #### 普通 Spring 项目 ```xml com.hongri bean-transfer-spring 2.0.2-SNAPSHOT ``` #### 独立使用 ```xml com.hongri bean-transfer-core 2.0.2-SNAPSHOT ``` ### 4.2 配置文件位置 默认情况下,框架会从 `classpath:mappingConfiguration/` 目录下加载 XML 配置文件。 可以通过以下方式自定义配置文件位置: ```java @Configuration public class CustomLoaderConfig { @Bean public MappingConfigurationLoader mappingConfigurationLoader( MappingConfigurationParser parser) { // 自定义配置文件路径 List basePackages = Arrays.asList( "mappingConfiguration", "customMappingConfig" ); return new XmlMappingConfigurationLoader(parser, basePackages); } @Bean public MappingConfigurationFactory mappingConfigurationFactory( MappingConfigurationLoader loader) throws Exception { DefaultMappingConfigurationFactory factory = new DefaultMappingConfigurationFactory(loader); factory.loadConfiguration(); // 加载所有配置 return factory; } } ``` ### 4.3 Hello World 示例 #### 步骤 1:定义实体类 ```java // 源对象 public class UserDTO { private String userName; private Integer userAge; private String userAddress; // getters and setters } // 目标对象 public class UserVO { private String name; private Integer age; private String address; // getters and setters } ``` #### 步骤 2:创建映射配置 将以下内容保存到 `src/main/resources/mappingConfiguration/user-mapping.xml`: ```xml ``` #### 步骤 3:使用转换器 ```java @Autowired private ConfigurationObjectTransferBuilder transferBuilder; public UserVO convertUser(UserDTO dto) { ObjectTransfer transfer = transferBuilder.buildObjectTransfer("user-mapping"); return (UserVO) transfer.transfer(dto); } ``` --- ## 五、配置说明 ### 5.1 配置文件结构 #### 完整配置示例 ```xml ``` ### 5.2 配置标签详解 #### mappingConfiguration(根标签) | 属性 | 必填 | 说明 | |------|------|------| | `class` | 是 | 目标实体类的全限定名或简写(如 `map`) | | `namespace` | 是 | 唯一标识符,用于加载和管理配置 | #### field-mapping(字段映射) | 属性 | 必填 | 说明 | 示例 | |------|------|------|------| | `target` | 是 | 目标字段路径,支持 SpEL | `userInfo.name` | | `source` | 否 | 源字段路径,支持 SpEL | `data.user.name` | | `value` | 否 | 固定值,支持 `${}` 变量 | `${requestId}` | | `escape-class` | 否 | 值转义处理类全限定名 | `com.example.EscapeHandler` | | `target-class` | 否 | 目标字段类型,通常不需要填写 | `java.lang.String` | | `converter-class` | 否 | 自定义转换器类 | `com.example.CustomConverter` | #### object-mapping(对象映射) | 属性 | 必填 | 说明 | |------|------|------| | `target` | 是 | 目标对象字段路径 | | `source` | 是 | 源对象字段路径 | | `target-class` | 否 | 目标对象类型 | | `converter-class` | 否 | 自定义转换器类 | #### array-mapping(数组映射) | 属性 | 必填 | 说明 | |------|------|------| | `target` | 是 | 目标集合字段路径 | | `source` | 是 | 源集合字段路径 | | `item-class` | 是 | 集合元素类型 | | `target-class` | 否 | 目标对象类型 | | `converter-class` | 否 | 自定义转换器类 | ### 5.3 关键概念说明 #### 上下文对象 在映射过程中,框架提供了两个特殊的上下文对象: **`current`**:当前正在处理的外层对象 ```xml ``` **`_`**:当前处理对象的上级对象 ```xml ``` #### 路径解析示例 ```xml ``` #### 动态值处理 **SpEL 表达式:** ```xml ``` **固定值:** ```xml ``` **变量引用:** ```xml ``` **复杂集合处理:** ```xml ``` #### 类名简写 为简化配置,支持以下类名简写: | 简写 | 实际类名 | |------|----------| | `map` | `java.util.LinkedHashMap` | | `hmap` | `java.util.HashMap` | | `list` | `java.util.ArrayList` | | `set` | `java.util.HashSet` | ```xml ``` --- ## 六、使用指南 ### 6.1 Spring Boot 集成 #### 自动配置 引入 `bean-transfer-starter` 后,框架会自动配置以下 Bean: - `MappingConfigurationParser`:XML 配置解析器 - `MappingConfigurationLoader`:配置加载器 - `MappingConfigurationFactory`:配置工厂 - `BeanCopierConfiguration`:全局配置 - `ConfigurationObjectTransferBuilder`:转换器构建器 #### 配置文件位置 默认情况下,XML 配置文件应放置在 `classpath:mappingConfiguration/` 目录下。 #### 自定义配置 ```java @Configuration public class CustomCopierConfig { @Bean public CustomFunctionProvider customFunctionProvider() { Map functions = new HashMap<>(); functions.put("formatDate", new DateFormatter()); return () -> functions; } @Bean public TypeConverter customTypeConverter() { return new MyCustomConverter(); } } ``` ### 6.2 基本使用 #### 构建 ObjectTransfer ```java @Autowired private ConfigurationObjectTransferBuilder transferBuilder; // 方式 1:使用 namespace 构建 ObjectTransfer transfer = transferBuilder.buildObjectTransfer("namespace-name"); // 方式 2:带上下文构建 Map context = new HashMap<>(); context.put("requestId", "123456"); ObjectTransfer transfer = transferBuilder.buildObjectTransfer("namespace-name", context); // 方式 3:使用自定义 EvaluationContext EvaluationContext evaluationContext = ...; ObjectTransfer transfer = transferBuilder.buildObjectTransfer("namespace-name", context, evaluationContext); ``` #### 执行转换 ```java // 简单转换 SourceObject source = new SourceObject(); TargetObject target = (TargetObject) transfer.transfer(source); // 带 Map 上下文的转换 Map contextMap = new HashMap<>(); contextMap.put("userId", "user001"); contextMap.put("requestId", UUID.randomUUID().toString()); SourceObject source = new SourceObject(); TargetObject target = (TargetObject) transfer.transfer(source); ``` ### 6.3 自定义转换器 #### 实现 TypeConverter 接口 ```java public class MyCustomConverter implements TypeConverter { @Override public boolean supports(Class sourceType, Class targetType) { return targetType == CustomType.class; } @Override public Object convert(Object source, Class targetType) { if (source == null) { return null; } // 自定义转换逻辑 return convertToCustomType(source); } } ``` #### 注册自定义转换器 ```java @Configuration public class ConverterConfig { @Bean public TypeConverterFactory customTypeConverterFactory() { DefaultTypeConverterFactory factory = new DefaultTypeConverterFactory(); factory.registerConverter(new MyCustomConverter()); return factory; } @Bean public BeanCopierConfiguration beanCopierConfiguration( TypeConverterFactory factory) { return new BeanCopierConfiguration.Builder() .typeConverterFactory(factory) .build(); } } ``` ### 6.4 自定义函数 #### 定义自定义函数 ```java public class DateFormatter { public String format(Object date, String pattern) { if (date == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(pattern); return sdf.format(date); } } ``` #### 注册自定义函数 ```java @Bean public CustomFunctionProvider customFunctionProvider() { Map functions = new HashMap<>(); functions.put("formatDate", new DateFormatter()); functions.put("encrypt", new EncryptFunction()); return () -> functions; } ``` #### 在 XML 中使用自定义函数 ```xml ``` ### 6.5 自定义转义处理器 #### 实现 EscapeHandler ```java public class XssEscapeHandler implements EscapeHandler { @Override public Object escape(Object value) { if (value instanceof String) { return escapeHtml((String) value); } return value; } private String escapeHtml(String html) { // HTML 转义逻辑 return html.replace("<", "<") .replace(">", ">"); } } ``` #### 在 XML 中使用 ```xml ``` --- ## 七、实战案例 ### 7.1 案例 1:HTTP 请求参数转换 #### 场景 将前端 JSON 请求转换为后端 API 请求对象。 #### 源对象 ```java public class FrontendHotelRequest { private String hotelId; private String startDate; private String endDate; private Integer guestCount; private List roomTypes; // getters and setters } ``` #### 目标对象 ```java public class BackendHotelRequest { private String hotelCode; private Date checkInTime; private Date checkOutTime; private Integer numberOfGuests; private List rooms; // getters and setters } ``` #### 映射配置 ```xml ``` #### 使用代码 ```java @Service public class HotelService { @Autowired private ConfigurationObjectTransferBuilder transferBuilder; public BackendHotelRequest convertRequest(FrontendHotelRequest frontendRequest) { ObjectTransfer transfer = transferBuilder.buildObjectTransfer("hotel-request-mapping"); return (BackendHotelRequest) transfer.transfer(frontendRequest); } } ``` ### 7.2 案例 2:复杂报文响应转换 #### 场景 将第三方 API 响应转换为内部统一的响应格式。 #### 第三方响应 ```java public class ThirdPartyResponse { private Integer returnCode; private String returnMsg; private ResponseData response; // getters and setters public static class ResponseData { private List hotels; private Integer totalCount; private PageInfo pagination; // getters and setters } public static class HotelInfo { private String id; private String name; private Double lat; private Double lng; private Location location; private Integer stock; private BigDecimal price; // getters and setters } public static class Location { private String city; private String province; private String country; // getters and setters } } ``` #### 内部响应 ```java public class HotelDetailResponse { private Integer code; private String message; private HotelData data; // getters and setters public static class HotelData { private List hotels; private Integer total; private PageInfoDTO page; // getters and setters } public static class HotelInfoDTO { private String hotelId; private String hotelName; private Address address; private Boolean isAvailable; private String price; // getters and setters } public static class Address { private String city; private Coordinates coordinates; // getters and setters } public static class Coordinates { private Double latitude; private Double longitude; // getters and setters } } ``` #### 映射配置 ```xml ``` #### 使用代码 ```java @Service public class HotelIntegrationService { @Autowired private ConfigurationObjectTransferBuilder transferBuilder; @Autowired private ThirdPartyApiClient apiClient; public HotelDetailResponse getHotelDetails(String hotelId) { // 调用第三方 API ThirdPartyResponse thirdPartyResponse = apiClient.queryHotel(hotelId); // 转换响应 ObjectTransfer transfer = transferBuilder.buildObjectTransfer("hotel-response-mapping"); return (HotelDetailResponse) transfer.transfer(thirdPartyResponse); } } ``` ### 7.3 案例 3:订单报文转换 #### 场景 将业务系统订单响应转换为前端展示的订单详情。 #### 配置文件 ```xml ``` ### 7.4 案例 4:请求报文生成(Map 转换) #### 场景 将前端请求对象转换为后端 API 需要的 Map 格式。 #### 配置文件 ```xml ``` #### 使用代码 ```java @Service public class HotelQueryService { @Autowired private ConfigurationObjectTransferBuilder transferBuilder; public Map buildQueryRequest(HotelQueryDTO query) { // 准备上下文变量 Map context = new HashMap<>(); context.put("signature", generateSignature(query)); context.put("currentTime", System.currentTimeMillis()); // 执行转换 ObjectTransfer transfer = transferBuilder.buildObjectTransfer("hotel-query-request", context); return (Map) transfer.transfer(query); } private String generateSignature(HotelQueryDTO query) { // 签名逻辑 return "generated-signature"; } } ``` ### 7.5 案例 5:复杂嵌套和数组展平 #### 场景 将复杂的第三方酒店响应转换为内部格式,展示深层嵌套对象映射、数组索引访问、复杂 SpEL 表达式等高级用法。 #### 配置文件(基于实际项目 qcc.xml) ```xml ``` #### 关键技术点说明 **1. 多层嵌套对象映射** ```xml ``` 支持直接使用点号分隔的路径来访问多层嵌套对象。 **2. 数组索引访问** ```xml ``` 可以直接访问数组或 List 的指定索引元素。 **3. 跨层级访问(使用 `_`)** ```xml ``` `_` 表示当前对象的父级对象,`_._` 表示父级的父级对象。 **4. 当前对象引用(使用 `current`)** ```xml ``` `current` 表示当前正在处理的外层对象。 **5. 复杂 SpEL 表达式 - flatMap** ```xml ``` 使用 SpEL 的 `flatMap` 和投影操作符 `![...]` 来展平复杂的嵌套数组结构。 **6. 条件表达式** ```xml ``` 使用 SpEL 表达式进行条件判断,返回布尔值。 --- ## 八、最佳实践 ### 8.1 配置文件组织 #### 推荐的目录结构 ``` src/main/resources/ └── mappingConfiguration/ ├── hotel/ # 酒店相关配置 │ ├── hotel-request-req.xml # 酒店查询请求 │ ├── hotel-detail-res.xml # 酒店详情响应 │ └── hotel-search-res.xml # 酒店搜索响应 ├── order/ # 订单相关配置 │ ├── order-create-req.xml │ ├── order-detail-res.xml │ └── order-list-res.xml └── common/ # 通用配置 └── common-mapping.xml ``` #### 命名规范 - **请求配置**:`{service}-{method}-req.xml` - **响应配置**:`{service}-{method}-res.xml` - **Namespace**:`{service}@{method}@{type}`(type 为 req/res) ```xml ... ``` ### 8.2 性能优化建议 #### 1. 避免深层嵌套 ```xml ``` #### 2. 简化 SpEL 表达式 ```xml ``` #### 3. 使用 Map 作为中间对象 当返回对象非常复杂时,可以将响应转换为 Map 以提高性能: ```xml ``` 程序会自动将 Map 复制到对应的实体类上。 #### 4. 缓存 ObjectTransfer 实例 ```java @Service public class OrderService { @Autowired private ConfigurationObjectTransferBuilder transferBuilder; // 缓存 ObjectTransfer 实例 private ObjectTransfer orderDetailTransfer; @PostConstruct public void init() { orderDetailTransfer = transferBuilder.buildObjectTransfer("order-detail-mapping"); } public OrderDetailVO getOrderDetail(String orderId) { // 直接使用缓存的实例 return (OrderDetailVO) orderDetailTransfer.transfer(...); } } ``` ### 8.3 错误处理 #### 1. 在转换器中捕获异常 ```java public class SafeTypeConverter implements TypeConverter { @Override public Object convert(Object source, Class targetType) { try { // 转换逻辑 return doConvert(source, targetType); } catch (Exception e) { log.error("类型转换失败: source={}, target={}", source, targetType, e); // 返回默认值或 null return getDefaultValue(targetType); } } } ``` #### 2. 验证配置文件 ```java @Component public class MappingConfigValidator { @Autowired private MappingConfigurationFactory factory; @PostConstruct public void validateConfigs() { // 加载所有配置进行验证 try { factory.loadMappingConfiguration("namespace1"); factory.loadMappingConfiguration("namespace2"); // ... } catch (Exception e) { throw new RuntimeException("映射配置验证失败", e); } } } ``` #### 3. 添加日志记录 ```java public class LoggingTypeConverter implements TypeConverter { private static final Logger log = LoggerFactory.getLogger(LoggingTypeConverter.class); @Override public Object convert(Object source, Class targetType) { log.debug("开始转换: source={}, targetType={}", source, targetType); Object result = doConvert(source, targetType); log.debug("转换完成: result={}", result); return result; } } ``` ### 8.4 测试策略 #### 1. 单元测试示例 ```java @SpringBootTest class MappingConfigurationTest { @Autowired private ConfigurationObjectTransferBuilder transferBuilder; @Test void testHotelResponseMapping() { // 准备测试数据 ThirdPartyResponse source = createTestResponse(); // 执行转换 ObjectTransfer transfer = transferBuilder.buildObjectTransfer("hotel-response-mapping"); HotelDetailResponse result = (HotelDetailResponse) transfer.transfer(source); // 验证结果 assertNotNull(result); assertEquals(200, result.getCode()); assertEquals("success", result.getMessage()); assertNotNull(result.getData()); assertEquals(10, result.getData().getTotal()); assertEquals(2, result.getData().getHotels().size()); // 验证嵌套对象 HotelInfoDTO hotel = result.getData().getHotels().get(0); assertEquals("H001", hotel.getHotelId()); assertEquals("北京希尔顿酒店", hotel.getHotelName()); assertTrue(hotel.getIsAvailable()); assertNotNull(hotel.getAddress()); assertEquals("北京市", hotel.getAddress().getCity()); } private ThirdPartyResponse createTestResponse() { // 创建测试数据 ThirdPartyResponse response = new ThirdPartyResponse(); // ... return response; } } ``` #### 2. 集成测试 ```java @SpringBootTest @AutoConfigureMockMvc class MappingIntegrationTest { @Autowired private MockMvc mockMvc; @Autowired private ConfigurationObjectTransferBuilder transferBuilder; @Test void testFullIntegration() throws Exception { // 模拟第三方响应 ThirdPartyResponse mockResponse = createMockResponse(); // 验证映射配置是否存在 MappingConfiguration config = transferBuilder .getMappingConfigurationFactory() .getMappingConfiguration("hotel-response-mapping"); assertNotNull(config); // 验证转换结果 ObjectTransfer transfer = transferBuilder.buildObjectTransfer("hotel-response-mapping"); HotelDetailResponse result = (HotelDetailResponse) transfer.transfer(mockResponse); // 断言关键字段 assertEquals(200, result.getCode()); // ... } } ``` ### 8.5 版本管理 #### 配置文件版本控制 ```xml ``` #### 多版本共存 当接口升级时,保持旧版本配置,新增新版本配置: ``` mappingConfiguration/ ├── hotel/ │ ├── getDetail-res-v1.0.xml # 旧版本 │ └── getDetail-res-v2.0.xml # 新版本 ``` ### 8.6 代码质量 #### 1. 配置文件注释 ```xml ``` #### 2. 使用常量定义 Namespace ```java public class MappingConstants { public static final String HOTEL_DETAIL_RES = "hotel-service@getDetail@res"; public static final String ORDER_CREATE_REQ = "order-service@create@req"; public static final String USER_INFO_RES = "user-service@getInfo@res"; } // 使用 ObjectTransfer transfer = transferBuilder.buildObjectTransfer(MappingConstants.HOTEL_DETAIL_RES); ``` --- ## 九、高级功能 ### 9.1 使用配置生成器 框架提供了配置生成器,可以根据目标类自动生成 XML 配置模板。 #### ClassMappingConfigGenerator ```java public class ConfigGeneratorExample { public static void main(String[] args) throws Exception { // 生成配置 String config = ClassMappingConfigGenerator.generateConfig( HotelDetailResponse.class, "hotel-detail-auto" ); // 保存到文件 Path path = Paths.get("src/main/resources/mappingConfiguration/hotel-detail-auto.xml"); Files.write(path, config.getBytes(StandardCharsets.UTF_8)); System.out.println("配置文件已生成: " + path); } } ``` 生成的配置示例: ```xml ``` ### 9.2 动态 Namespace ```java @Service public class DynamicMappingService { @Autowired private ConfigurationObjectTransferBuilder transferBuilder; public Object transferWithDynamicNamespace(String version, Object source) { String namespace = "hotel-service@getDetail@res@" + version; ObjectTransfer transfer = transferBuilder.buildObjectTransfer(namespace); return transfer.transfer(source); } } ``` ### 9.3 自定义属性访问器 ```java public class CustomPropertyAccessor implements PropertyAccessor { @Override public boolean canRead(EvaluationContext context, Object target, String name) { // 自定义读取逻辑 return target instanceof CustomObject; } @Override public TypedValue read(EvaluationContext context, Object target, String name) { // 自定义读取实现 CustomObject obj = (CustomObject) target; return new TypedValue(obj.getProperty(name)); } @Override public boolean canWrite(EvaluationContext context, Object target, String name) { return false; } @Override public void write(EvaluationContext context, Object target, String name, Object newValue) { // 自定义写入实现 } @Override public Class[] getSpecificTargetClasses() { return new Class[]{CustomObject.class}; } } ``` --- ## 十、常见问题 ### 10.1 配置文件加载失败 **问题:** ``` Caused by: com.hongri.copier.exception.MappingConfigurationException: Cannot find mapping configuration: namespace-name ``` **解决方案:** 1. 确认 XML 文件在 `classpath:mappingConfiguration/` 目录下 2. 确认 `namespace` 属性值与代码中使用的一致 3. 检查 XML 文件格式是否正确 ### 10.2 类型转换错误 **问题:** ``` Caused by: com.hongri.copier.exception.BeanCopierException: Type conversion failed: source=xxx, target=yyy ``` **解决方案:** 1. 实现自定义 `TypeConverter` 处理特殊类型转换 2. 在 XML 中指定 `converter-class` 属性 3. 检查源值和目标类型是否兼容 ### 10.3 SpEL 表达式解析失败 **问题:** ``` org.springframework.expression.spel.SpelParseException: Expression cannot start with '#' at position 0 ``` **解决方案:** 1. SpEL 表达式应该放在 `source` 属性中,而非 `value` 2. 确保表达式语法正确 3. 使用 `#` 前缀标识 SpEL 表达式 ### 10.4 嵌套对象为 null **问题:** 转换过程中,嵌套对象的字段没有被赋值。 **解决方案:** 1. 确认源对象中嵌套对象不为 null 2. 使用 `?:` 操作符提供默认值:`source="data?.name"` 3. 在 XML 配置中显式创建嵌套对象 ### 10.5 性能问题 **问题:** 对象转换速度慢,影响系统性能。 **解决方案:** 1. 缓存 `ObjectTransfer` 实例,避免重复创建 2. 减少深层嵌套,使用路径直接访问 3. 简化 SpEL 表达式 4. 对于复杂对象,考虑转换为 Map 中间格式 5. 调整 ReflectASM 的缓存策略 ### 10.6 集合映射问题 **问题:** 集合映射后元素丢失或顺序错误。 **解决方案:** 1. 确认 `item-class` 属性正确 2. 检查源集合是否为 null 3. 使用 `?:` 提供默认空集合:`source="items?:#{}"` 4. 确认目标集合类型支持(List、Set、数组) ### 10.7 Spring Boot 集成问题 **问题:** `ConfigurationObjectTransferBuilder` 无法自动注入。 **解决方案:** 1. 确认引入了 `bean-transfer-starter` 依赖 2. 检查 `@SpringBootApplication` 的扫描范围 3. 确认配置文件在 classpath 中 4. 手动创建 Bean: ```java @Configuration public class ManualConfig { @Bean public ConfigurationObjectTransferBuilder transferBuilder( BeanCopierConfiguration config, MappingConfigurationFactory factory) { ConfigurationObjectTransferBuilder builder = new ConfigurationObjectTransferBuilder(); builder.setBeanCopierConfiguration(config); builder.setMappingConfigurationFactory(factory); return builder; } } ``` --- ## 附录 ### A. 配置文件完整示例 ```xml ``` ### B. 类型转换器示例 ```java // 日期转换器 public class DateConverter implements TypeConverter { private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public boolean supports(Class sourceType, Class targetType) { return targetType == Date.class || sourceType == Date.class; } @Override public Object convert(Object source, Class targetType) { if (source == null) return null; try { if (source instanceof String) { return sdf.parse((String) source); } else if (source instanceof Date) { return sdf.format((Date) source); } } catch (ParseException e) { throw new RuntimeException("日期转换失败", e); } return source; } } // 枚举转换器 public class EnumConverter implements TypeConverter { @Override public boolean supports(Class sourceType, Class targetType) { return targetType.isEnum(); } @Override public Object convert(Object source, Class targetType) { if (source == null) return null; if (source instanceof String) { return Enum.valueOf((Class) targetType, (String) source); } else if (source instanceof Integer) { return targetType.getEnumConstants()[(Integer) source]; } return source; } } ``` --- ## 更新日志 ### 历史版本 - v1.0.0: 初始版本 --- ## 联系方式 - **项目地址**:[项目 Git 仓库] - **问题反馈**:[Issue Tracker] - **邮件联系**:[维护者邮箱] --- *本文档最后更新时间:2024 年 1 月*