Переменная экземпляра Spring Service в многопоточной среде - PullRequest
0 голосов
/ 15 октября 2018

Недавно я столкнулся с проблемой состояния гонки из-за объявления переменной экземпляра внутри класса обслуживания по умолчанию в области действия (синглтон).Цель переменной экземпляра состояла в том, чтобы сделать мой код более читабельным и избежать постоянной передачи одной и той же переменной разным частным методам в классе Service.Пример звучит так:

@Service
public class SomeServiceImpl implements SomeService {
    private final StatusRepository statusRepository;
    private Predicate<Status> statusPredicate;

    @Autowired
    public SomeServiceImpl(StatusRepository statusRepository) {
        this.statusRepository = statusRepository;
    }

    @Override
    public List<Status> getAllowedStatuses(String userId) {
        statuses = statusRepository.getAll();
        initPredicate();
        appendPredicateA();
        appendPredicateB();
        List<Status> results = statuses.stream()
            .filter(statusPredicate)
            .collect(Collectors.toList());
        return results;
    }

    private void initPredicate() {
        statusPredicate = p -> p.getDefault().equals("default");
    }

    private void appendPredicateA() {
        statusPredicate.and(p -> p.getA().equals("A"));
    }

    private void appendPredicateB() {
        statusPredicate.and(p -> p.getB().equals("B"));
    }
}

Это очень простой пример того, чего я хочу достичь.Это явно не потокобезопасно, потому что теперь класс обслуживания находится в состоянии.Я мог бы просто решить эту проблему, превратив переменную statusPredicate в локальную переменную, и методы void возвращают предикат после того, как он былдобавлены новые условия, но они будут загромождены следующим образом:

@Override
public List<Status> getAllowedStatuses(String userId) {
    statuses = statusRepository.getAll();
    Predicate<Status> statusPredicate = p -> p.getDefault().equals("default");
    statusPredicate = appendPredicateA(statusPredicate);
    statusPredicate = appendPredicateB(statusPredicate);
    List<Status> results = statuses.stream()
        .filter(statusPredicate)
        .collect(Collectors.toList());
    return results;
}

Было бы постоянно вызывать изменение переменной и возвращать переменную.

Я знаю несколько решений, которые могут решитьнапример, добавление @ RequestScope к классу Service, чтобы каждый запрос от HTTP получал новый экземпляр объекта Service, или использование ThreadLocal в переменной Predicate.Тем не менее, я не совсем уверен, каков наилучший подход и можно ли с самого начала объявить переменную экземпляра в классе Service.Если с самого начала плохо делать класс Service с состоянием, как мне структурировать свой код, чтобы он был чище и при этом оставался без состояния?

Пожалуйста, сообщите!Заранее спасибо: D

1 Ответ

0 голосов
/ 12 марта 2019

Spring Service может быть с состоянием, и поэтому область действия для.

Область Spring Service по умолчанию - Singleton, потому что это самый широкий вариант использования.Но вы не должны использовать изменяемые локальные переменные.

Просто попробуйте проект без состояния и используйте области действия для решения проблемы, только если создание экземпляра класса быстрое, иначе TreadLocal будет работать лучше.

...