JoinInMemory 的使用
- 业务功能接口
public interface OrderDetailService {
List<? extends OrderDetailVO> getByUserId(Long userId);
}
- 业务功能实现类
@Service
public class OrderDetailServiceV6 implements OrderDetailService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private JoinService joinService;
@Override
public List<? extends OrderDetailVO> getByUserId(Long userId) {
List<Order> orders = this.orderRepository.getByUserId(userId);
List<OrderDetailVOV6> orderDetailVOS = orders.stream()
.map(order -> new OrderDetailVOV6(OrderVO.apply(order)))
.collect(toList());
this.joinService.joinInMemory(OrderDetailVOV6.class, orderDetailVOS);
return orderDetailVOS;
}
}
- 关键注解:
JoinAddressVOOnId、JoinInMemoryConfig、JoinInMemory
@Data
@JoinInMemoryConfig(executorType = JoinInMemoryExecutorType.PARALLEL)
public class OrderDetailVOV6 extends OrderDetailVO {
private final OrderVO order;
@JoinUserVOOnId(keyFromSourceData = "#{order.userId}")
private UserVO user;
@JoinAddressVOOnId(keyFromSourceData = "#{order.addressId}")
private AddressVO address;
@JoinProductVOOnId(keyFromSourceData = "#{order.productId}")
private ProductVO product;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JoinInMemory(keyFromSourceData = "",
keyFromJoinData = "#{id}",
loader = "#{@userRepository.getByIds(#root)}",
joinDataConverter = "#{T(com.xdh.joininmemory.demo.UserVO).apply(#root)}"
)
public @interface JoinUserVOOnId {
@AliasFor(
annotation = JoinInMemory.class
)
String keyFromSourceData();
}
JoinInMemory 的原理
JoinInMemory 注解
- 数据装配的核心处理流程都是:1、通过主表的主键ID查询出数据,2、拿到这些数据中作为其他表的主键的字段,3、根据步骤2取出的字段去其他表进行查询,4、将查询结果转化为需要的类型后设置到返回结果中
- 在 JoinMemory 解决数据装配问题一 中进行了抽象,在本篇幅中提供注解能力完成数据装配
-
keyFromSourceData 属性:指定从源数据中提取出下一步需要用到的 key,其中源数据是指:从数据库查询出来的数据、key 就是指:其他表的主键
-
keyFromJoinData 属性:如何更好描述? -
loader 属性:指定根据 key 去查询信息的方法
-
joinDataConverter 属性:指定将数据库查询结果对象转换为返回信息需要的类型的转换方法
-
runLevel:当使用该注解需要依赖其他优先级更高的注解时,可以使用该属性指定优先级
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JoinInMemory {
/**
* 从 sourceData 中提取 key
* @return
*/
String keyFromSourceData();
/**
* 从 joinData 中提取 key
* @return
*/
String keyFromJoinData();
/**
* 批量数据抓取
* @return
*/
String loader();
/**
* 结果转换器
* @return
*/
String joinDataConverter() default "";
/**
* 运行级别,同一级别的 join 可 并行执行
* @return
*/
int runLevel() default 10;
}
JoinInMemoryConfig 注解
用于配置 JoinInMemory 注解是进行并行抓取还是串行抓取,并且可以指定并行抓取的线程池
- JoinInMemeoryExecutorType 属性
- 当执行【批量数据抓取】方法时,可以提供两种方式:一个是串行执行抓取方法,一个是并行执行抓取方法
- 串行抓取:针对实时性要求不高的业务场景
- 并行抓取:针对实时性要求高的场景,或者是当接口响应速度变慢时,可以将串行抓取变为并行抓取,从而提高响应速度
- executorName 属性:当使用并行抓取策略的时候,可以指定线程池名字,未指定则使用默认创建的 defaultExecutor 线程池
@Override
public OrderDetail getById(Long id) {
Order order = this.orderQueryRepository.findById(id)
.orElse(null);
if (order == null){
return null;
}
OrderDetail orderDetail = new OrderDetail(order);
this.joinService.joinInMemory(orderDetail);
return orderDetail;
}
JoinService 服务接口
JoinMemory 服务对外接口
- joinInMemory(T t) 方法:用于对单个对象进行 JoinMemory 的场景,比如:
this.joinService.joinInMemory(orderDetail) - joinInMemory(List<> t) 方法:用于对列表数据进行 JoinMemory 的场景,比如:
this.joinService.joinInMemory(orderDetailList)
public interface JoinService {
/**
* 执行内存 join
* @param t
*/
default <T> void joinInMemory(T t){
if (t == null){
return;
}
joinInMemory((Class<T>) t.getClass(), Collections.singletonList(t));
}
default <T> void joinInMemory(List<T> t){
if (CollectionUtils.isEmpty(t)){
return;
}
if (t.size() == 1){
joinInMemory(t.get(0));
}else {
joinInMemory((Class<T>) t.get(0).getClass(), t);
}
}
/**
* 执行内存 Join
* @param tCls 实际类型
* @param t 需要抓取的集合
*/
<T> void joinInMemory(Class<T> tCls, List<T> t);
/**
* 注册一个类型,主要用于初始化
* @param tCls
* @param <T>
*/
<T> void register(Class<T> tCls);
}
DefaultJoinService 实现类
JoinService 的实现类
-
核心关注点有下面几个
- DefaultJoinService 构造函数传入了 JoinItemsExecutorFactory
- JoinItemsExecutorFactory 一个工厂类,其具体作用后续讲解
- joinInMemory 方法:通过 createJoinExecutorGroup 方法获取到一个 JoinItemsExecutor,并执行 execute 方法
- createJoinExecutorGroup 方法:通过构造函数传入的 JoinItemsExecutorFactory,调用其 createFor 方法,得到 JoinItemsExecutor
-
那么根据上面的几个关注点,可以从下面几个思路去理解实现原理
DefaultJoinService 的构造函数在哪里使用了?- 见 JoinInMemoryAutoConfiguration
JoinItemsExecutorFactory 的 createFor 方法的逻辑是什么?- 见 DefaultJoinItemsExecutorFactory
JoinItemsExecutor 的 execute 方法的逻辑是什么?- 见 AbstractJoinItemsExecutor、ParallelJoinItemsExecutor、SerialJoinItemsExecutor
public class DefaultJoinService implements JoinService {
private final JoinItemsExecutorFactory joinItemsExecutorFactory;
/**
* 缓存,避免频繁的初始化
*/
private final Map<Class, JoinItemsExecutor> cache = Maps.newConcurrentMap();
public DefaultJoinService(JoinItemsExecutorFactory joinItemsExecutorFactory) {
this.joinItemsExecutorFactory = joinItemsExecutorFactory;
}
@Override
public <T> void joinInMemory(Class<T> tCls, List<T> t) {
this.cache.computeIfAbsent(tCls, this::createJoinExecutorGroup)
.execute(t);
}
@Override
public <T> void register(Class<T> tCls) {
this.cache.computeIfAbsent(tCls, this::createJoinExecutorGroup);
}
private JoinItemsExecutor createJoinExecutorGroup(Class aClass) {
return this.joinItemsExecutorFactory.createFor(aClass);
}
}
JoinInMemoryAutoConfiguration
注入相关 Bean
@ConditionalOnMissingBean是一个条件注解,它的作用是在满足条件的情况下,使注解的组件(Bean)生效- 具体来说,当一个组件使用了
@ConditionalOnMissingBean注解时,它的生效与否将受到当前 Spring 上下文中是否存在该类型的 Bean 的影响。如果当前上下文中不存在该类型的 Bean,则被注解的组件将被创建并注册为一个 Bean;如果已经存在了同类型的 Bean,则被注解的组件将不会被创建。 - 这个注解常用于配置类或者自定义的配置文件中,用于控制某个特定的 Bean 在不存在时进行配置
- 具体来说,当一个组件使用了
- 注入 JoinItemsExecutorFactory:通过创建 DefaultJoinItemsExecutorFactory 得到
- 注入 JoinService:通过创建 DefaultJoinService 对象得到
- 注入 defaultExecutor:这个就是 JoinInMemoryConfig 注解中提供的默认线程池
注入 JoinInMemoryBasedJoinItemExecutorFactory?
@Configuration
@Slf4j
public class JoinInMemoryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public JoinItemsExecutorFactory joinItemsExecutorFactory(Collection<? extends JoinItemExecutorFactory> joinItemExecutorFactories,
Map<String, ExecutorService> executorServiceMap){
return new DefaultJoinItemsExecutorFactory(joinItemExecutorFactories, executorServiceMap);
}
@Bean
@ConditionalOnMissingBean
public JoinService joinService(JoinItemsExecutorFactory joinItemsExecutorFactory){
return new DefaultJoinService(joinItemsExecutorFactory);
}
@Bean
public JoinInMemoryBasedJoinItemExecutorFactory joinInMemoryBasedJoinItemExecutorFactory(ApplicationContext applicationContext){
return new JoinInMemoryBasedJoinItemExecutorFactory(new BeanFactoryResolver(applicationContext));
}
@Bean
public ExecutorService defaultExecutor(){
BasicThreadFactory basicThreadFactory = new BasicThreadFactory.Builder()
.namingPattern("JoinInMemory-Thread-%d")
.daemon(true)
.build();
int maxSize = Runtime.getRuntime().availableProcessors() * 3;
return new ThreadPoolExecutor(0, maxSize,
60L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
basicThreadFactory,
new ThreadPoolExecutor.CallerRunsPolicy());
}
}
JoinItemsExecutorFactory 和 DefaultJoinItemsExecutorFactory
JoinItemsExecutorFactory
- 工厂类,从 class 中解析信息,并创建 JoinItemsExecutor
DefaultJoinItemsExecutorFactory
- 使用 JoinItemExecutorFactory 为每个 Join 数据 创建 JoinItemExecutor
- 将一个 class 的 JoinItemExecutor 封装成 JoinItemsExecutor
- 核心关注点有下面几个
- createFor 方法:通过
JoinItemExecutorFactory拿到一系列的JoinItemExecutor,再读取JoinInMemoryConfig注解配置的JoinInMemeoryExecutorType,然后根据JoinItemExecutor和JoinInMemeoryExecutorType创建JoinItemsExecutor对象 - buildJoinItemsExecutor 方法:根据
JoinInMemeoryExecutorType创建JoinItemsExecutor对象 - createForType 方法:见
JoinItemExecutorFactory
- createFor 方法:通过
public interface JoinItemsExecutorFactory {
/**
* 为 类 创建 Join 执行器
* @param cls
* @param <D>
* @return
*/
<D> JoinItemsExecutor<D> createFor(Class<D> cls);
}
@Slf4j
public class DefaultJoinItemsExecutorFactory implements JoinItemsExecutorFactory {
private final List<JoinItemExecutorFactory> joinItemExecutorFactories;
private final Map<String, ExecutorService> executorServiceMap;
public DefaultJoinItemsExecutorFactory(Collection<? extends JoinItemExecutorFactory> joinItemExecutorFactories,
Map<String, ExecutorService> executorServiceMap) {
this.joinItemExecutorFactories = Lists.newArrayList(joinItemExecutorFactories);
// 按执行顺序进行排序
AnnotationAwareOrderComparator.sort(this.joinItemExecutorFactories);
this.executorServiceMap = executorServiceMap;
}
@Override
public <D> JoinItemsExecutor<D> createFor(Class<D> cls) {
// 依次遍历 JoinItemExecutorFactory, 收集 JoinItemExecutor 信息
List<JoinItemExecutor<D>> joinItemExecutors = this.joinItemExecutorFactories.stream()
.flatMap(factory -> factory.createForType(cls).stream())
.collect(Collectors.toList());
// 从 class 上读取配置信息
JoinInMemoryConfig joinInMemoryConfig = cls.getAnnotation(JoinInMemoryConfig.class);
// 封装为 JoinItemsExecutor
return buildJoinItemsExecutor(cls, joinInMemoryConfig, joinItemExecutors);
}
private <D> JoinItemsExecutor<D> buildJoinItemsExecutor(Class<D> cls, JoinInMemoryConfig joinInMemoryConfig, List<JoinItemExecutor<D>> joinItemExecutors){
// 使用 串行执行器
if(joinInMemoryConfig == null || joinInMemoryConfig.executorType() == JoinInMemoryExecutorType.SERIAL){
log.info("JoinInMemory for {} use serial executor", cls);
return new SerialJoinItemsExecutor<>(cls, joinItemExecutors);
}
// 使用 并行执行器
if (joinInMemoryConfig.executorType() == JoinInMemoryExecutorType.PARALLEL){
log.info("JoinInMemory for {} use parallel executor, pool is {}", cls, joinInMemoryConfig.executorName());
// 获取线程池
ExecutorService executor = executorServiceMap.get(joinInMemoryConfig.executorName());
Preconditions.checkArgument(executor != null);
return new ParallelJoinItemsExecutor<>(cls, joinItemExecutors, executor);
}
throw new IllegalArgumentException();
}
}
JoinItemsExecutor、AbstractJoinItemsExecutor、SerialJoinItemsExecutor、ParallelJoinItemsExecutor
JoinItemsExecutor 执行器接口,抽象实现类以及具体实现类
- JoinItemsExecutor 执行器接口
public interface JoinItemsExecutor<DATA> {
void execute(List<DATA> datas);
}
- AbstractJoinItemsExecutor:JoinItemsExecutor 的抽象实现,主要是在构造函数中对所有的 JoinItemExecutor 按照 runLevel 进行优先级排序
abstract class AbstractJoinItemsExecutor<DATA> implements JoinItemsExecutor<DATA> {
@Getter(AccessLevel.PROTECTED)
private final Class<DATA> dataCls;
@Getter(AccessLevel.PROTECTED)
private final List<JoinItemExecutor<DATA>> joinItemExecutors;
public AbstractJoinItemsExecutor(Class<DATA> dataCls,
List<JoinItemExecutor<DATA>> joinItemExecutors) {
Preconditions.checkArgument(dataCls != null);
Preconditions.checkArgument(joinItemExecutors != null);
this.dataCls = dataCls;
this.joinItemExecutors = joinItemExecutors;
Collections.sort(this.joinItemExecutors, Comparator.comparingInt(JoinItemExecutor::runOnLevel));
}
}
- SerialJoinItemsExecutor:串行执行器,JoinItemsExecutor 接口的具体实现,多个 join 操作顺序执行
/**
* 串行执行器,多个 join 操作顺序执行
*/
@Slf4j
public class SerialJoinItemsExecutor<DATA> extends AbstractJoinItemsExecutor<DATA> {
public SerialJoinItemsExecutor(Class<DATA> dataCls,
List<JoinItemExecutor<DATA>> joinItemExecutors) {
super(dataCls, joinItemExecutors);
}
@Override
public void execute(List<DATA> datas) {
getJoinItemExecutors().forEach(dataJoinExecutor -> {
log.debug("run join on level {} use {}",
dataJoinExecutor.runOnLevel(), dataJoinExecutor);
if (log.isDebugEnabled()){
StopWatch stopWatch = StopWatch.createStarted();
dataJoinExecutor.execute(datas);
stopWatch.stop();
log.debug("run execute cost {} ms, executor is {}, data is {}.",
stopWatch.getTime(TimeUnit.MILLISECONDS),
dataJoinExecutor,
datas);
}else {
dataJoinExecutor.execute(datas);
}
});
}
}
- ParallelJoinItemsExecutor:并行执行器,利用配置的线程池,采用多线程方式去执行 join,同一 level 的 join 在线程中并行执行
/**
* 并行执行器,同一 level 的 join 在线程中并行执行
*/
@Slf4j
public class ParallelJoinItemsExecutor<DATA> extends AbstractJoinItemsExecutor<DATA> {
private final ExecutorService executor;
private final List<JoinExecutorWithLevel> joinExecutorWithLevel;
public ParallelJoinItemsExecutor(Class<DATA> dataCls,
List<JoinItemExecutor<DATA>> joinItemExecutors,
ExecutorService executor) {
super(dataCls, joinItemExecutors);
this.executor = executor;
this.joinExecutorWithLevel = buildJoinExecutorWithLevel();
}
private List<JoinExecutorWithLevel> buildJoinExecutorWithLevel() {
List<JoinExecutorWithLevel> collect = getJoinItemExecutors().stream()
.collect(Collectors.groupingBy(joinExecutor -> joinExecutor.runOnLevel()))
.entrySet().stream()
.map(entry -> new JoinExecutorWithLevel(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
// 根据 level 进行排序,解决依赖问题
Collections.sort(collect, Comparator.comparingInt(o -> o.level));
return collect;
}
@Override
public void execute(List<DATA> datas) {
this.joinExecutorWithLevel.forEach(joinExecutorWithLevel1 -> {
log.debug("run join on level {} use {}", joinExecutorWithLevel1.getLevel(),
joinExecutorWithLevel1.getJoinItemExecutors());
List<Task> tasks = buildTasks(joinExecutorWithLevel1, datas);
try {
if (log.isDebugEnabled()) {
StopWatch stopWatch = StopWatch.createStarted();
this.executor.invokeAll(tasks);
stopWatch.stop();
log.debug("run execute cost {} ms, task is {}.",
stopWatch.getTime(TimeUnit.MILLISECONDS),
tasks);
}else {
this.executor.invokeAll(tasks);
}
} catch (InterruptedException e) {
log.error("invoke task {} interrupted", tasks, e);
}
});
}
private List<Task> buildTasks(JoinExecutorWithLevel joinExecutorWithLevel, List<DATA> datas) {
return joinExecutorWithLevel.getJoinItemExecutors().stream()
.map(joinExecutor -> new Task(joinExecutor, datas))
.collect(Collectors.toList());
}
@Value
class Task implements Callable<Void> {
private final JoinItemExecutor<DATA> joinItemExecutor;
private final List<DATA> datas;
@Override
public Void call() throws Exception {
this.joinItemExecutor.execute(this.datas);
return null;
}
}
@Value
class JoinExecutorWithLevel{
private final Integer level;
private final List<JoinItemExecutor<DATA>> joinItemExecutors;
}
}
JoinItemExecutorFactory、AbstractAnnotationBasedJoinItemExecutorFactory、JoinInMemoryBasedJoinItemExecutorFactory
- JoinItemExecutorFactory:为一个类生成一组 JoinMemory 执行器
/**
* 为一个类生成一组 JoinMemory 执行器
*/
public interface JoinItemExecutorFactory {
<DATA> List<JoinItemExecutor<DATA>> createForType(Class<DATA> cls);
}
- AbstractAnnotationBasedJoinItemExecutorFactory:JoinItemExecutorFactory 的抽象实现类
- 核心关注点
- createForField 方法:见 JoinItemExecutor、AbstractJoinItemExecutor、JoinItemExecutorAdapter
/**
* JoinItemExecutorFactory 的实现类,并定义一些抽象方法,用于生成 Function、BiConsumer
* 通过传入的 Class 类,获取该类的所有字段,并获取字段上的使用到 JoinInMemory 注解
* 通过 JoinItemExecutorAdapter 获取到所有使用到了 JoinInMemory 注解的字段的 JoinInMemory 处理器
*/
abstract class AbstractAnnotationBasedJoinItemExecutorFactory<A extends Annotation>
implements JoinItemExecutorFactory {
private final Class<A> annotationCls;
protected AbstractAnnotationBasedJoinItemExecutorFactory(Class<A> annotationCls) {
this.annotationCls = annotationCls;
}
@Override
public <DATA> List<JoinItemExecutor<DATA>> createForType(Class<DATA> cls) {
// 从 字段 上获取 注解,并将其转换为 JoinItemExecutor
List<Field> fieldsListWithAnnotation = FieldUtils.getAllFieldsList(cls);
// 为每一个字段创建一个执行器
return fieldsListWithAnnotation.stream()
.map(field -> createForField(cls, field,
AnnotatedElementUtils.findMergedAnnotation(field, annotationCls))
).filter(Objects::nonNull)
.collect(toList());
}
private <DATA> JoinItemExecutor<DATA> createForField(Class<DATA> cls, Field field,A ann) {
if (ann == null){
return null;
}
JoinItemExecutorAdapter adapter = JoinItemExecutorAdapter.builder()
.name(createName(cls, field, ann))
.runLevel(createRunLevel(cls, field, ann))
.keyFromSourceData(createKeyGeneratorFromData(cls, field, ann))
.joinDataLoader(createDataLoader(cls, field, ann))
.keyFromJoinData(createKeyGeneratorFromJoinData(cls, field, ann))
.joinDataConverter(createDataConverter(cls, field, ann))
.foundCallback(createFoundFunction(cls, field, ann))
.lostCallback(createLostFunction(cls, field, ann))
.build();
return adapter;
}
protected <DATA> BiConsumer<Object, Object> createLostFunction(Class<DATA> cls, Field field, A ann){
return null;
}
protected abstract <DATA> BiConsumer<Object, List<Object>> createFoundFunction(Class<DATA> cls, Field field, A ann);
protected abstract <DATA> Function<Object, Object> createDataConverter(Class<DATA> cls, Field field, A ann);
protected abstract <DATA> Function<Object, Object> createKeyGeneratorFromJoinData(Class<DATA> cls, Field field, A ann);
protected abstract <DATA> Function<List<Object>, List<Object>> createDataLoader(Class<DATA> cls, Field field, A ann);
protected abstract <DATA> Function<Object, Object> createKeyGeneratorFromData(Class<DATA> cls, Field field, A ann);
protected abstract <DATA> int createRunLevel(Class<DATA> cls, Field field, A ann);
protected <DATA> String createName(Class<DATA> cls, Field field, A ann){
return new StringBuilder()
.append("class[").append(cls.getSimpleName()).append("]")
.append("#field[").append(field.getName()).append("]")
.append("-").append(ann.getClass().getSimpleName())
.toString();
}
}
- AbstractAnnotationBasedJoinItemExecutorFactory
- 核心关注点
- Function:Function<T, R> 接口表示一个接受参数类型为 T 的对象,并返回结果类型为 R 的对象的函数。它定义了 apply() 方法,用于执行具体的函数逻辑。常见的函数式接口方法有:andThen()、compose() 和 identity()
- BiConsumer:BiConsumer<T, U> 接口表示一个接受两个参数类型分别为 T 和 U 的对象,并不返回结果的操作。它定义了 accept() 方法,用于执行具体的操作逻辑
- Spring Expression Language:SpEL你感兴趣的实现原理浅析
/**
* 继承 AbstractAnnotationBasedJoinItemExecutorFactory 类,实现其抽象方法
* 抽象方法的实现原理:根据 JoinInMemory 的属性 + Spring表达式语言(Spring Expression Language),生成相应的 Function 和 BiConsumer
*/
@Slf4j
public class JoinInMemoryBasedJoinItemExecutorFactory extends AbstractAnnotationBasedJoinItemExecutorFactory<JoinInMemory>{
private final ExpressionParser parser = new SpelExpressionParser();
private final TemplateParserContext templateParserContext = new TemplateParserContext();
private final BeanResolver beanResolver;
public JoinInMemoryBasedJoinItemExecutorFactory(BeanResolver beanResolver) {
super(JoinInMemory.class);
this.beanResolver = beanResolver;
}
@Override
protected <DATA> BiConsumer<Object, List<Object>> createFoundFunction(Class<DATA> cls, Field field, JoinInMemory ann) {
log.info("write field is {} for class {}", field.getName(), cls);
boolean isCollection = Collection.class.isAssignableFrom(field.getType());
return new DataSetter(field.getName(), isCollection);
}
@Override
protected <DATA> Function<Object, Object> createDataConverter(Class<DATA> cls, Field field, JoinInMemory ann) {
if (StringUtils.isEmpty(ann.joinDataConverter())){
log.info("No Data Convert for class {}, field {}", cls, field.getName());
return Function.identity();
}else {
log.info("Data Convert is {} for class {}, field {}", ann.joinDataConverter(), cls, field.getName());
return new DataGetter(ann.joinDataConverter());
}
}
@Override
protected <DATA> Function<Object, Object> createKeyGeneratorFromJoinData(Class<DATA> cls, Field field, JoinInMemory ann) {
log.info("Key from join data is {} for class {}, field {}",
ann.keyFromJoinData(), cls, field.getName());
return new DataGetter(ann.keyFromJoinData());
}
@Override
protected <DATA> Function<List<Object>, List<Object>> createDataLoader(Class<DATA> cls, Field field, JoinInMemory ann) {
log.info("data loader is {} for class {}, field {}",
ann.loader(), cls, field.getName());
return new DataGetter(ann.loader());
}
@Override
protected <DATA> Function<Object, Object> createKeyGeneratorFromData(Class<DATA> cls, Field field, JoinInMemory ann) {
log.info("Key from source data is {} for class {}, field {}",
ann.keyFromJoinData(), cls, field.getName());
return new DataGetter(ann.keyFromSourceData());
}
@Override
protected <DATA> int createRunLevel(Class<DATA> cls, Field field, JoinInMemory ann) {
log.info("run level is {} for class {}, field {}",
ann.runLevel(), cls, field.getName());
return ann.runLevel();
}
private class DataSetter<T, U> implements BiConsumer<Object, List<Object>>{
private final String fieldName;
private final boolean isCollection;
private final Expression expression;
private DataSetter(String fieldName, boolean isCollection) {
this.fieldName = fieldName;
this.expression = parser.parseExpression(fieldName);
this.isCollection = isCollection;
}
@Override
public void accept(Object data, List<Object> result) {
if (isCollection) {
this.expression.setValue(data, result);
}else {
int size = result.size();
if (size == 1){
this.expression.setValue(data, result.get(0));
}else {
log.warn("write join result to {} error, field is {}, data is {}",
data,
fieldName,
result);
}
}
}
}
private class DataGetter<T, R> implements Function<T, R>{
private final String expStr;
private final Expression expression;
private final EvaluationContext evaluationContext;
private DataGetter(String expStr) {
this.expStr = expStr;
this.expression = parser.parseExpression(expStr, templateParserContext);
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.setBeanResolver(beanResolver);
this.evaluationContext = evaluationContext;
}
@Override
public Object apply(Object data) {
if (data == null){
return null;
}
return expression.getValue(evaluationContext, data);
}
}
}
JoinItemExecutor、AbstractJoinItemExecutor、JoinItemExecutorAdapter
JoinMemory 执行器,JoinMemory 执行器的抽象实现类,模板方法的适配器
/**
* JoinMemory 执行器:执行数据 join 操作
*/
public interface JoinItemExecutor<DATA> {
void execute(List<DATA> datas);
default int runOnLevel(){
return 0;
}
}
/**
* JoinMemory 执行器的抽象实现类:定义抽象方法,并在 execute 中编排后执行
*
* @param <SOURCE_DATA> 原始数据
* @param <JOIN_KEY> join 使用的 key
* @param <JOIN_DATA> join 获取的 数据
* @param <JOIN_RESULT> 转换后的结果数据
*/
@Slf4j
abstract class AbstractJoinItemExecutor<SOURCE_DATA, JOIN_KEY, JOIN_DATA, JOIN_RESULT> implements JoinItemExecutor<SOURCE_DATA> {
/**
* 从原始数据中生成 JoinKey
* @param data
* @return
*/
protected abstract JOIN_KEY createJoinKeyFromSourceData(SOURCE_DATA data);
/**
* 根据 JoinKey 批量获取 JoinData
* @param joinKeys
* @return
*/
protected abstract List<JOIN_DATA> getJoinDataByJoinKeys(List<JOIN_KEY> joinKeys);
/**
* 从 JoinData 中获取 JoinKey
* @param joinData
* @return
*/
protected abstract JOIN_KEY createJoinKeyFromJoinData(JOIN_DATA joinData);
/**
* 将 JoinData 转换为 JoinResult
* @param joinData
* @return
*/
protected abstract JOIN_RESULT convertToResult(JOIN_DATA joinData);
/**
* 将 JoinResult 写回至 SourceData
* @param data
* @param JoinResults
*/
protected abstract void onFound(SOURCE_DATA data, List<JOIN_RESULT> JoinResults);
/**
* 未找到对应的 JoinData
* @param data
* @param joinKey
*/
protected abstract void onNotFound(SOURCE_DATA data, JOIN_KEY joinKey);
@Override
public void execute(List<SOURCE_DATA> sourceDatas) {
// 从源数据中提取 JoinKey
List<JOIN_KEY> joinKeys = sourceDatas.stream()
.filter(Objects::nonNull)
.map(this::createJoinKeyFromSourceData)
.filter(Objects::nonNull)
.distinct()
.collect(toList());
log.debug("get join key {} from source data {}", joinKeys, sourceDatas);
// 根据 JoinKey 获取 JoinData
List<JOIN_DATA> allJoinDatas = getJoinDataByJoinKeys(joinKeys);
log.debug("get join data {} by join key {}", allJoinDatas, joinKeys);
// 将 JoinData 以 Map 形式进行组织
Map<JOIN_KEY, List<JOIN_DATA>> joinDataMap = allJoinDatas.stream()
.filter(Objects::nonNull)
.collect(groupingBy(this::createJoinKeyFromJoinData));
log.debug("group by join key, result is {}", joinDataMap);
// 处理每一条 SourceData
for (SOURCE_DATA data : sourceDatas){
// 从 SourceData 中 获取 JoinKey
JOIN_KEY joinKey = createJoinKeyFromSourceData(data);
if (joinKey == null){
log.warn("join key from join data {} is null", data);
continue;
}
// 根据 JoinKey 获取 JoinData
List<JOIN_DATA> joinDatasByKey = joinDataMap.get(joinKey);
if (CollectionUtils.isNotEmpty(joinDatasByKey)){
// 获取到 JoinData, 转换为 JoinResult,进行数据写回
List<JOIN_RESULT> joinResults = joinDatasByKey.stream()
.filter(Objects::nonNull)
.map(joinData -> convertToResult(joinData))
.collect(toList());
log.debug("success to convert join data {} to join result {}", joinDatasByKey, joinResults);
onFound(data, joinResults);
log.debug("success to write join result {} to source data {}", joinResults, data);
}else {
log.warn("join data lost by join key {} for source data {}", joinKey, data);
// 为获取到 JoinData,进行 notFound 回调
onNotFound(data, joinKey);
}
}
}
}
/**
* 模板方法的适配器
*
* JoinMemory 执行器的实现类:实现 AbstractJoinItemExecutor 中的抽象方法
* 通过 JoinItemExecutorAdapter 构造器的入参,传入要实现的抽象方法的实现逻辑(Function、BiConsumer)
* 在 AbstractAnnotationBasedJoinItemExecutorFactory 中调用了构造器
*/
@Slf4j
@Builder
@Getter
public class JoinItemExecutorAdapter<SOURCE_DATA, JOIN_KEY, JOIN_DATA, RESULT>
extends AbstractJoinItemExecutor<SOURCE_DATA, JOIN_KEY, JOIN_DATA, RESULT> {
private final String name;
private final int runLevel;
private final Function<SOURCE_DATA, JOIN_KEY> keyFromSourceData;
private final Function<List<JOIN_KEY>, List<JOIN_DATA>> joinDataLoader;
private final Function<JOIN_DATA, JOIN_KEY> keyFromJoinData;
private final Function<JOIN_DATA, RESULT> joinDataConverter;
private final BiConsumer<SOURCE_DATA, List<RESULT>> foundCallback;
private final BiConsumer<SOURCE_DATA, JOIN_KEY> lostCallback;
public JoinItemExecutorAdapter(String name,
Integer runLevel,
Function<SOURCE_DATA, JOIN_KEY> keyFromSourceData,
Function<List<JOIN_KEY>, List<JOIN_DATA>> joinDataLoader,
Function<JOIN_DATA, JOIN_KEY> keyFromJoinData,
Function<JOIN_DATA, RESULT> joinDataConverter,
BiConsumer<SOURCE_DATA, List<RESULT>> foundCallback,
BiConsumer<SOURCE_DATA, JOIN_KEY> lostCallback) {
Preconditions.checkArgument(keyFromSourceData != null);
Preconditions.checkArgument(joinDataLoader != null);
Preconditions.checkArgument(keyFromJoinData != null);
Preconditions.checkArgument(joinDataConverter != null);
Preconditions.checkArgument(foundCallback != null);
this.name = name;
this.keyFromSourceData = keyFromSourceData;
this.joinDataLoader = joinDataLoader;
this.keyFromJoinData = keyFromJoinData;
this.joinDataConverter = joinDataConverter;
this.foundCallback = foundCallback;
if (lostCallback != null) {
this.lostCallback = getDefaultLostFunction().andThen(lostCallback);
}else {
this.lostCallback = getDefaultLostFunction();
}
if (runLevel == null){
this.runLevel = 0;
}else {
this.runLevel = runLevel.intValue();
}
}
@Override
protected JOIN_KEY createJoinKeyFromSourceData(SOURCE_DATA data) {
return this.keyFromSourceData.apply(data);
}
@Override
protected List<JOIN_DATA> getJoinDataByJoinKeys(List<JOIN_KEY> joinKeys) {
return this.joinDataLoader.apply(joinKeys);
}
@Override
protected JOIN_KEY createJoinKeyFromJoinData(JOIN_DATA joinData) {
return this.keyFromJoinData.apply(joinData);
}
@Override
protected RESULT convertToResult(JOIN_DATA joinData) {
return this.joinDataConverter.apply(joinData);
}
@Override
protected void onFound(SOURCE_DATA data, List<RESULT> JoinResults) {
this.foundCallback.accept(data, JoinResults);
}
@Override
protected void onNotFound(SOURCE_DATA data, JOIN_KEY joinKey) {
this.lostCallback.accept(data, joinKey);
}
private BiConsumer<SOURCE_DATA, JOIN_KEY> getDefaultLostFunction() {
return (data, joinKey) -> {
log.warn("failed to find join data by {} for {}", joinKey, data);
};
}
@Override
public int runOnLevel() {
return this.runLevel;
}
@Override
public String toString() {
return "JoinExecutorAdapter-for-" + name;
}
}