【问题标题】:How to create a customisation annotation for splitting request param and collect return result?如何创建自定义注解拆分请求参数并收集返回结果?
【发布时间】:2019-03-19 11:23:27
【问题描述】:

我有一个方法 params 是一个大于 50000 个项目的列表; 受限于业务逻辑,列表必须小于30000,这样我才有办法在逻辑之前把这个数组拆分成二维数组

public static final <T> Collection<List<T>> partitionBasedOnSize(List<T> inputList, int size) {
        AtomicInteger counter = new AtomicInteger(0);
        return inputList.stream().collect(Collectors.groupingBy(s -> counter.getAndIncrement() / size)).values();
}

这是我目前的解决方案:

public List<Account> getChildrenList(List<Long> ids) {
        List<Account> childrenList = new ArrayList<>();
        Collection<List<Long>> childrenId2dList = PartitionArray.partitionBasedOnSize(childrenIdsList, 30000);
        for (List<Long> list : childrenId2dList) {
            //this is my business logic: start
            childrenList.addAll(accountRepository.getAccounts(list));
            //this is my business logic: end
        }
        return childrenAccountsList;
}

我想在方法之上创建一个注释而不是许多重复的代码(每次都检查和尽管...)

对不起,我的英语不好,我画了一个图表: 方法调用>尽管数组>业务逻辑>收集所有结果>返回

【问题讨论】:

    标签: java java-8 java-annotations


    【解决方案1】:

    在我看来,在这种情况下使用注释有点过度设计(您必须编写注释处理器)。您可以轻松地使用 泛型lambdas 和/或 方法引用 来实现您的目标。例如:

    以这种方式更新PartitionArray

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    public class PartitionArray {
    
        private static <T> Collection<List<T>> partitionBasedOnSize(List<T> inputList, int partitionSize) {
            Collection<List<T>> collection = new ArrayList<>();
            int remainingSize = inputList.size();
            int index = 0;
            while (remainingSize > partitionSize) {
                collection.add(inputList.subList(index, index + partitionSize));
                remainingSize -= partitionSize;
                index += partitionSize;
            }
            collection.add(inputList.subList(index, index + remainingSize));
            return collection;
        }
    
        public static <D, T> List<D> partitionAndDoBusinessFunction(List<T> ids, Function<List<T>, List<D>> businessFunction, int partitionSize) {
            List<D> dataList = new ArrayList<>();
            Collection<List<T>> idListCollection = partitionBasedOnSize(ids, partitionSize);
            for (List<T> idList : idListCollection) {
                dataList.addAll(businessFunction.apply(idList));
            }
            return dataList;
        }
    }
    

    然后只需从您的AccountService 使用它(使用方法参考):

    import java.util.List;
    
    public class AccountService {
    
        private AccountRepository accountRepository;
    
        public List<Account> getAccounts(List<Long> ids) {
            return PartitionArray.partitionAndDoBusinessFunction(ids, accountRepository::getAccounts, 30000);
        }
    }
    

    或者使用 lambda:

    import java.util.List;
    
    public class AccountService {
    
        private AccountRepository accountRepository;
    
        public List<Account> getAccounts(List<Long> ids) {
            return PartitionArray.partitionAndDoBusinessFunction(ids, idList -> {
                List<Account> accounts = accountRepository.getAccounts(idList);
                // do more business on accounts
                return accounts;
            }, 30000);
        }
    }
    

    【讨论】:

    • 这很好! (讨厌 AspectJ 解决方案,过于工程化,有很多依赖项、配置等)。我只会将分区大小作为参数添加到新方法中,而不是将其硬编码为30000
    • @FedericoPeraltaSchaffner 我完全同意你的看法 - Lambda 和方法参考是针对这种情况的。我还按照您的建议添加了partitionSize 作为参数。
    • 但是这个基于AtomicIntegerpartitionBasedOnSize 实现是可怕的。无需遍历所有列表元素(并为每个元素更新 AtomicInteger),只需找出元素的数量。名单已经知道了。我们可以通过size() 查询它,并通过subList 直接请求它的一小部分,免费(不会发生复制)。只需使用return IntStream.range(0, (inputList.size()+size-1)/size) .mapToObj(i -&gt; inputList.subList(i*=size, Math.min(inputList.size(), i+size))) .collect(Collectors.toList()); 即可获得List&lt;List&lt;T&gt;&gt;(即使维持秩序)
    • @Holger 感谢您的评论。我同意你的看法。我承认我没有挑战地复制/粘贴了 OP 的实现......现在用我自己的实现进行了更新。
    【解决方案2】:

    这是一个使用注释的解决方案,使用AspectJ

    首先,定义所需的注解:

    package partition;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Partitioned {
    
        /**
         * The size of the partition, for instance 30000
         */
        int size();
    }
    

    因此您的服务将如下所示:

    package partition;
    
    import java.util.List;
    
    public class AccountService {
    
        private AccountRepository accountRepository;
    
        public AccountService(AccountRepository accountRepository) {
            this.accountRepository = accountRepository;
        }
    
        @Partitioned(size = 30000)
        public List<Account> getAccounts(List<Long> ids) {
            return accountRepository.getAccounts(ids);
        }
    }
    

    到目前为止,这很容易。然后是注释的处理,AspectJ 进入游戏。定义一个链接到注释的aspect

    package partition;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    @Aspect
    public class PartitionedAspect {
    
        @Pointcut("@annotation(partitioned)")
        public void callAt(Partitioned partitioned) {
        }
    
        @Around("callAt(partitioned)")
        public <T, D> Object around(ProceedingJoinPoint pjp, Partitioned partitioned) throws Throwable {
            List<T> inputIds = (List) pjp.getArgs()[0];
            if (inputIds.size() > partitioned.size()) {
                List<D> dataList = new ArrayList<>();
                Collection<List<T>> partitionedIds = PartitionArray.partitionBasedOnSize(inputIds, partitioned.size());
                for (List<T> idList : partitionedIds) {
                    List<D> data = (List) pjp.proceed(new Object[]{idList});
                    dataList.addAll(data);
                }
                return dataList;
            }
            return pjp.proceed();
        }
    }
    

    当然,您必须导入 AspectJ,并且还必须在编译时执行一些额外的工作。假设您使用的是 maven,请将这些行添加到您的 pom.xml(插件和依赖项):

    <build>
        <plugins>
            ...
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.7</version>
                <configuration>
                    <complianceLevel>1.8</complianceLevel>
                    <source>1.8</source>
                    <target>1.8</target>
                    <showWeaveInfo>true</showWeaveInfo>
                    <verbose>true</verbose>
                    <Xlint>ignore</Xlint>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    
    <dependencies>
        ...
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
    ...
    </dependencies>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-23
      • 2016-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多