为什么我的变量被调用时会发生变化?

时间:2020-10-15 09:59:01

标签: java spring-boot

我的项目涉及SpringBoot。因此,在Service中,我创建了一种方法来检索所有用户可用的默认索引:

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

@Autowired
private IndexRepository indexRepository;



/**
 * Read from application.properties
 *
 * We only store the index ids in the application.properties
 */
@Value("${default.allowed.ids}")
private String defaultAllowedIds;

// I've also tried and removed the static with the same behaviour
private static HashSet<Index> listOfDefaultAllowedIndex = null;

public User findByUsername(String username) {
    return userRepository.findByUsername(username);
}

public User findByResetToken(String resetToken) {
    return userRepository.findByResetToken(resetToken);
}

/**
 * Saves the user in the database and encrypts the password with bcrypt.
 *
 * @param user
 */
public void save(User user) {

    // Code to save a new user

}

public void update(User user) {

    // COde to update the user
}

/**
 * List the default index allowed by default to all users
 *
 * @return the listOfDefaultAllowedIndex
 */
private HashSet<IndexSetup> getListOfDefaultAllowedIndex() {
    
    System.err.println("TEST : DEFAULT LIST CONTAINS " + (listOfDefaultAllowedIndex != null ? 
            listOfDefaultAllowedIndex.size() + " index" : "NULL"));
            // This is null at the beginning then it grows
    
    System.err.println("TEST : THE DEFAULT STRING IS " + defaultAllowedIndexIds); 
    // This is always an empty string

    if (listOfDefaultAllowedIndex == null) {


        // 1) Gets the ids from the property
        HashSet<String> defaultIds = new HashSet(Stream.of(
                defaultAllowedIndexIds.split(
                        SEMI_COLON_SEPARATOR)).collect(Collectors.toSet()));
        
        System.err.println("TEST : DEFAULT IDS : " + defaultIds); // THis is empty
                    

        // 2) Converts them into list of Index object
        listOfDefaultAllowedIndex = new HashSet(StreamSupport
                .stream(indexRepository.findAll().spliterator(),
                        false)
                .filter(idx -> defaultIds.contains(Integer.toString(
                idx.getId())))
                .collect(Collectors.toSet()));
        
        System.err.println("TEST : CONVERTED INDEX : ");
        listOfDefaultAllowedIndex.stream().forEach(
                    idx -> System.err.println(idx.getName() + " (" + idx.getId() + ")")); // THis is empty

    }
    
     System.err.println("TEST : READING FROM CACHE (FROM THREAD " + Thread.currentThread().getName() + ") FOR THE DEFAULT IDS : ");
        listOfDefaultAllowedIndex.stream().forEach(
                    idx -> System.err.println(idx.getName() + " (" + idx.getId() + ")"));
                    // THis should be empty but is growing

    return listOfDefaultAllowedIndex;
}

因此,每次getListOfDefaultAllowedIndex都会增长listOfDefaultAllowedIndex(实际上,如果它是一个List但它是一个Set,它会增长),尽管application.properties中的属性不会更改,而listOfDefaultAllowedIndex只会更改在该方法中(如果为null,则在重置服务器时)。我已经从IDE中使用Find UsageFind in project函数进行搜索,除了该方法之外,找不到任何结果。

我注意到线程名称不同,但是我无法弄清楚竞争条件如何将值更改为不同于空的值。

请注意:我注意到,如果我删除类变量listOfDefaultAllowedIndexif listOfDefaultAllowedIndex == null条件,并在方法中声明listOfDefaultAllowedIndex,那么它将按预期工作, listOfDefaultAllowedIndex保持为空(在这种情况下,该属性为空),但是我无法解释原因。

因此,什么可以使变量更改?

编辑

正如@RalfKleberhoff所指出的,我确实是通过另一种方法这样做泄漏了原始的HashSet

HashSet<Index> allowedIndexes = this.getListOfDefaultAllowedIndex();
//...
allowedIndexes.addAll(...);

这个other old question实际上回答了我的问题。

要解决这个问题,我必须通过将Set包装到Set中来返回Collections.unmodifiableSet(listOfDefaultAllowedIndex)的不变版本,如下面的答案所建议。哇,我完全忘记了!

任何帮助表示赞赏

1 个答案:

答案 0 :(得分:2)

最可能的原因:

您正在将内部HashSet泄漏给吸气剂的调用者。

因此,如果调用者修改了从您的获取方法返回的HashSet作为返回值,则此更改将反映在您的内部字段值中,因为它是相同的HashSet对象。

要检查:从您的吸气剂中返回Collections.unmodifiableSet(listOfDefaultAllowedIndex)(并将返回类型调整为Set而不是HashSet)。然后,任何尝试更改Set的调用方都将获得异常,而无需进行任何修改。然后,您可以决定如何继续:例如让此呼叫者进行复制,请务必返回该字段的HashSet的副本