20240823 Java Stream流怎麼被關閉了(內含Keycloak UserProvider坑)

前提:我們都知道了,Stream一但被用完,就沒了

有天為了開發Keycloak SPI,寫了這樣的代碼

//java 
Stream<GroupModel> groups = user.getGroupsStream();

groups.forEach(group -> {
List<String> roleNames = group.getRealmRoleMappingsStream().map(RoleModel::getName).collect(Collectors.toList());
// 又做了某些事情...
}

開心打包了個jar包執行

Keycloak服務日誌跟我說forEach那行:

java.lang.IllegalStateException: stream has already been operated upon or closed

;_;...


問題出在哪裡呢?後來追追原始碼,user.getGroupsStream()叫出的stream已經是被filter過的了(想不到吧囧)

Keycloak原始碼

// java
    default Stream<GroupModel> getGroupsStream(String search, Integer first, Integer max) {
        if (search != null) search = search.toLowerCase();
        final String finalSearch = search;
        Stream<GroupModel> groupModelStream = getGroupsStream()
                .filter(group -> finalSearch == null || group.getName().toLowerCase().contains(finalSearch));

        if (first != null && first > 0) {
            groupModelStream = groupModelStream.skip(first);
        }

        if (max != null && max >= 0) {
            groupModelStream = groupModelStream.limit(max);
        }

        return groupModelStream;
    }

另外內部那個group.getRealmRoleMappingsStream()叫出的stream也已經是被filter過的了

Keycloak原始碼

//java
@Override
public Stream<RoleModel> getRealmRoleMappingsStream() {
    return getRoleMappingsStream().filter(RoleUtils::isRealmRole);
}

Uhmmm...那只好先把它們收起來轉換成其他格式再做處理吧,不然用不了的...

最內層的改寫例子:

//java
List<RoleModel> filteredRoleModel = group.getRealmRoleMappingsStream().collect(Collectors.toList()); 
// filteredRoleModel.stream().開一個新流後可以做其他事情
Last Updated:
Contributors: Crystal