背景 在使用 mapstruct 框架进行数据转换时,可以有 2 种方法进行对象类型转换,第一种是通过将对象注入到 Spring 容器中进行调用,这种方法则强依赖 Spring 容器;第二种声明一个静态的转换器实例字段,这种方法则不依赖 Spring,调用比较方便,如下示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Mapper(componentModel = "cdi") public interface CarMapper { CarDto carToCarDto (Car car) ; } @Mapper public interface GolfPlayerMapper { GolfPlayerMapper INSTANCE = Mappers.getMapper( GolfPlayerMapper.class ); GolfPlayerDto toDto (GolfPlayer player) ; GolfPlayer toPlayer (GolfPlayerDto player) ; }
案例解析 根据以上场景,我们在做数据转换时,需要在 camel 的 dsl 中,通过获取类静态字段作为对象,再进行方法调用。
首先看一下常规的数据方式实现
示例数据如下
转换的数据类型定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Data public class EmployeeBean { private String name; } @Data public class PersonBean { private String name; } public interface EmployeeConverter { EmployeeConverter converter = new EmployeeConverterImpl (); EmployeeBean convert (PersonBean personBean) ; } public class EmployeeConverterImpl implements EmployeeConverter { public EmployeeBean convert (PersonBean personBean) { EmployeeBean employeeBean = new EmployeeBean (); employeeBean.setName(personBean.getName()); return employeeBean; } }
转换的 DSL 定义
可以由 2 种方式实现
第一种,直接调用具体的实现类 EmployeeConverterImpl,缺点是暴露了接口的实现类
第二种,将 EmployeeConverterImpl 类注入到容器中,使用注入的实例引用调用 employeeConverter,缺点是依赖 Spring 容器
1 2 3 4 5 6 7 8 9 10 11 12 13 <camelContext xmlns ="http://camel.apache.org/schema/spring" > <route > <from uri ="direct:start" /> <bean method ="convert" beanType ="org.apache.camel.spring.converter.model.EmployeeConverterImpl" > </bean > </route > <bean id ="employeeConverter" class ="org.apache.camel.spring.converter.model.EmployeeConverterImpl" /> <route > <from uri ="direct:start" /> <bean method ="convert" ref ="employeeConverter" > </bean > </route > </camelContext >
除了以上方法,最直接的方式是如何在不依赖 Spring 的情况下,直接获取 EmployeeConverter 的静态字段 converter?
方案 首先要解决的问题是如何获取类的静态字段,通过 simple 脚本表达式的 type 前缀,可以获取字段的结果,如下示例
employeeConverter 设置的
1 2 3 4 5 6 7 8 <camelContext xmlns ="http://camel.apache.org/schema/spring" > <route > <from uri ="direct:start" /> <setHeader name ="employeeConverter" > <simple > ${type:org.apache.camel.spring.converter.model.EmployeeConverter.converter}</simple > </setHeader > </route > </camelContext >
type 前缀的表达式实现逻辑如下,发现实际返回的是字段的 toString 结果(lookupConstantFieldValue 方法实现),不满足诉求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public static Expression typeExpression (final String name) { return new ExpressionAdapter () { private ClassResolver classResolver; private Expression exp; @Override public void init (CamelContext context) { classResolver = context.getClassResolver(); exp = ExpressionBuilder.simpleExpression(name); exp.init(context); } public Object evaluate (Exchange exchange) { String text = exp.evaluate(exchange, String.class); Class<?> type = classResolver.resolveClass(text); if (type != null ) { return type; } int pos = text.lastIndexOf('.' ); if (pos > 0 ) { String before = text.substring(0 , pos); String after = text.substring(pos + 1 ); type = classResolver.resolveClass(before); if (type != null ) { return ObjectHelper.lookupConstantFieldValue(type, after); } } throw CamelExecutionException.wrapCamelExecutionException(exchange, new ClassNotFoundException ("Cannot find type " + text)); } @Override public String toString () { return "type:" + name; } }; } public static String lookupConstantFieldValue (Class<?> clazz, String name) { if (clazz == null ) { return null ; } if (name.startsWith("." )) { name = name.substring(1 ); } for (Field field : clazz.getFields()) { if (field.getName().equals(name)) { try { Object v = field.get(null ); return v.toString(); } catch (IllegalAccessException e) { return null ; } } } return null ; }
那么我们可以通过自定义表达式的方式来获取字段对象
1 2 3 4 5 6 7 8 9 10 11 <camelContext xmlns ="http://camel.apache.org/schema/spring" > <route > <from uri ="direct:start" /> <setHeader name ="employeeConverter" > <language language ="lx" > object:org.apache.camel.spring.converter.model.EmployeeConverter.converter</language > </setHeader > <transform > <simple > ${header.employeeConverter.convert}</simple > </transform > </route > </camelContext >
自定义表达式实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 public class LxLanguage extends LanguageSupport implements Language { @Override public Predicate createPredicate (String expression) { return ExpressionToPredicateAdapter.toPredicate(createExpression(expression)); } @Override public Expression createExpression (String expression) { String remainder = ifStartsWithReturnRemainder("object:" , expression); if (remainder != null ) return objectExpression(remainder); return null ; } private String ifStartsWithReturnRemainder (String prefix, String text) { if (text.startsWith(prefix)) { String remainder = text.substring(prefix.length()); if (remainder.length() > 0 ) { return remainder; } } return null ; } public static Expression objectExpression (final String name) { return new ExpressionAdapter () { private ClassResolver classResolver; private Expression exp; @Override public void init (CamelContext context) { classResolver = context.getClassResolver(); exp = ExpressionBuilder.simpleExpression(name); exp.init(context); } public Object evaluate (Exchange exchange) { String text = exp.evaluate(exchange, String.class); Class<?> type = classResolver.resolveClass(text); if (type != null ) { return type; } int pos = text.lastIndexOf('.' ); if (pos > 0 ) { String before = text.substring(0 , pos); String after = text.substring(pos + 1 ); type = classResolver.resolveClass(before); if (type != null ) { return lookupFieldValue(type, after); } } throw CamelExecutionException.wrapCamelExecutionException(exchange, new ClassNotFoundException ("Cannot find type " + text)); } @Override public String toString () { return "object:" + name; } }; } public static Object lookupFieldValue (Class<?> clazz, String name) { if (clazz == null ) { return null ; } if (name.startsWith("." )) { name = name.substring(1 ); } for (Field field : clazz.getFields()) { if (field.getName().equals(name)) { try { Object v = field.get(null ); return v; } catch (IllegalAccessException e) { return null ; } } } return null ; } }