-
Notifications
You must be signed in to change notification settings - Fork 33
装配操作
在 crane4j
中, “根据 A 的 key 值拿到 B,再把 B 的属性映射到 A” 这句话被视为一次装配操作,一次装配操作只会基于一个 key 字段触发,一个对象完成一次填充可能需要经过多次装配操作。
一个 Class
对应的操作配置都通过 BeanOperationParser
解析,由于目前解析器只有 AnnotationAwareBeanOperationParser
一种,因此只能基于注解完成装配操作的声明。
关于属性映射部分可以参考字段映射一节。
一般情况下,可以使用 @Assemble
在某个 key 字段上声明一次装配操作:
public class Student {
@Assemble(namespace = "student", props = @Mapping(src = "studentName", ref = "name"))
private Integer id;
private String name;
}
上述配置表示这样一个操作:
- 使用
id
字段的值; - 去
namespace
为student
的数据源容器中获取数据源对象; - 获得与
id
字段值对应的数据后,将数据源对象中的studentName
字段值,映射到name
字段上;
在父类中声明的操作,子类也会一并继承。
有时候为了保持代码的整洁,或者不方便直接修改父类,我们也可以不直接在属性上添加注解,而是在类上添加注解:
@Operations(
assembles = @Assemble(key = "id", namespace = "student", props = @Mapping(src = "studentName", ref = "name"))
)
public class Student {
private Integer id;
private String name;
}
与直接在属性上声明不同,需要通过 key
属性绑定对应的 key
字段。
在父类上声明的配置同样可以被子类继承。
有时候,我们会需要在一个 key
上声明多次装配操作,这也是允许的:
public class UserVO {
@Assemble(namespace = "user", props = @Mapping(src = "role", ref = "role"), groups = "admin")
@Assemble(namespace = "user", props = @Mapping(src = "name", ref = "name"), groups = {"base", "admin"})
private Integer id;
private String name;
private String role;
}
当使用默认的执行器时,不必担心多次查库,默认的执行器
DisorderedBeanOperationExecutor
会将容器相同的操作汇总在一起再执行,保证尽可能少的减少数据源容器的调用次数。
出于代码的整洁,有时候如果要映射的字段特别多,或者相同的注解配置需要反复使用,推荐使用组合注解:
// 将目标注解作为元注解
@Assemble(key = "id", namespace = "student", props = @Mapping(src = "studentName", ref = "name"))
@Documented
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AssembleStudent { }
然后直接使用合成注解 @AssembleStudent
代替原本的一大坨:
public class Student {
// 直接使用合成注解
@AssembleStudent
private Integer id;
private String name;
}
该功能基于 Spring
的 MergeAnnotation
实现,遵循 Spring
组合注解的语义。
默认情况下,统一类中的操作的排序为:
- 在属性上直接使用
@Assemble
声明的操作,按属性顺序排序; - 通过
@Operations
声明的操作,按它们声明的顺序排序; - 在同一类中,属性上声明的操作优先于类上声明的操作;
- 在不同中,子类中声明的操作优先于父类中声明的操作;
除此之外,也可以通过 sort
属性可以指定不同操作之间的顺序:
public class Student {
@Assemble(namespace = "id", sort = 0, props = @Mapping(ref = "id3"))
private Integer id1;
@Assemble(namespace = "id", sort = 1, props = @Mapping(ref = "id4"))
private Integer id2;
@Assemble(namespace = "id", sort = 1, props = @Mapping(ref = "id2"))
private Integer id3;
private Integer id4;
}
上述示例中, 三个操作的执行顺序分别为 id1 -> id3 -> id2
, sort
的值越小,则排序时越靠前,这个顺序将会优先于默认的排序规则。
需要注意的是,排序值不一定代表最终的执行顺序,只代表遍历 BeanOperations
中的装配操作时的顺序,最终的执行顺序需要通过 BeanOperationExecutor
来保证。
默认支持按顺序执行的执行器为 OrderedBeanOperationExecutor
,不过相较其他执行器性能会差一些,用户可以根据自己的需求选择。
所有的装配操作中的读跟写最终都通过装配操作处理器 AssembleOperationHandler
完成处理,用户同样也可以在注解的 handler
属性中选择处理器:
public class Student {
@Assemble(
key = "id", namespace = "student",
props = @Mapping(src = "studentName", ref = "name"),
handler = ReflectAssembleOperationHandler.class // 指定操作处理器
)
private Integer id;
private String name;
}
在配置解析过程中,会从 Spring
上下文根据指定的类型获取对应的操作处理器。
目前 AssembleOperationHandler
只提供了基于反射的单 key
值处理器 ReflectAssembleOperationHandler
和多 key
值处理器 MultiKeyAssembleOperationHandler
两种实现。
用户可以自定义操作处理器,以便更好的支持其他类型的数据,或者提高处理器的执行效率。